#include "private.h" #include "offl_cpp.h" #include "subsmgrp.h" HRESULT _GetURLData(IDataObject *, int, TCHAR *, TCHAR *); HRESULT _ConvertHDROPData(IDataObject *, BOOL); HRESULT ScheduleDefault(LPCTSTR, LPCTSTR); #define CITBDTYPE_HDROP 1 #define CITBDTYPE_URL 2 #define CITBDTYPE_TEXT 3 // // Constructor // COfflineDropTarget::COfflineDropTarget(HWND hwndParent) { m_cRefs = 1; m_hwndParent = hwndParent; m_pDataObj = NULL; m_grfKeyStateLast = 0; m_fHasHDROP = FALSE; m_fHasSHELLURL = FALSE; m_fHasTEXT = FALSE; m_dwEffectLastReturned = 0; DllAddRef(); } // // Destructor // COfflineDropTarget::~COfflineDropTarget() { DllRelease(); } // // QueryInterface // STDMETHODIMP COfflineDropTarget::QueryInterface(REFIID riid, LPVOID *ppv) { HRESULT hr = E_NOINTERFACE; *ppv = NULL; // Any interface on this object is the object pointer if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IDropTarget)) { *ppv = (LPDROPTARGET)this; AddRef(); hr = NOERROR; } return hr; } // // AddRef // STDMETHODIMP_(ULONG) COfflineDropTarget::AddRef() { return ++m_cRefs; } // // Release // STDMETHODIMP_(ULONG) COfflineDropTarget::Release() { if (0L != --m_cRefs) { return m_cRefs; } delete this; return 0L; } // // DragEnter // STDMETHODIMP COfflineDropTarget::DragEnter(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) { // Release any old data object we might have // TraceMsg(TF_SUBSFOLDER, TEXT("odt - DragEnter")); if (m_pDataObj) { m_pDataObj->Release(); } m_grfKeyStateLast = grfKeyState; m_pDataObj = pDataObj; // // See if we will be able to get CF_HDROP from this guy // if (pDataObj) { pDataObj->AddRef(); TCHAR url[INTERNET_MAX_URL_LENGTH], name[MAX_NAME_QUICKLINK]; FORMATETC fe = {(CLIPFORMAT) RegisterClipboardFormat(CFSTR_SHELLURL), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; m_fHasSHELLURL = m_fHasHDROP = m_fHasTEXT = FALSE; if (NOERROR == pDataObj->QueryGetData(&fe)) { TraceMsg(TF_SUBSFOLDER, "odt - DragEnter : SHELLURL!"); m_fHasSHELLURL = (NOERROR == _GetURLData(pDataObj,CITBDTYPE_URL,url,name)); } if (fe.cfFormat = CF_HDROP, NOERROR == pDataObj->QueryGetData(&fe)) { TraceMsg(TF_SUBSFOLDER, "odt - DragEnter : HDROP!"); m_fHasHDROP = (NOERROR == _ConvertHDROPData(pDataObj, FALSE)); } if (fe.cfFormat = CF_TEXT, NOERROR == pDataObj->QueryGetData(&fe)) { TraceMsg(TF_SUBSFOLDER, "odt - DragEnter : TEXT!"); m_fHasTEXT = (NOERROR == _GetURLData(pDataObj,CITBDTYPE_TEXT,url,name)); } } // Save the drop effect if (pdwEffect) { *pdwEffect = m_dwEffectLastReturned = GetDropEffect(pdwEffect); } return S_OK; } // // GetDropEffect // DWORD COfflineDropTarget::GetDropEffect(LPDWORD pdwEffect) { ASSERT(pdwEffect); if (m_fHasSHELLURL || m_fHasTEXT) { return *pdwEffect & (DROPEFFECT_COPY | DROPEFFECT_LINK); } else if (m_fHasHDROP) { return *pdwEffect & (DROPEFFECT_COPY ); } else { return DROPEFFECT_NONE; } } // // DragOver // STDMETHODIMP COfflineDropTarget::DragOver(DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) { // TraceMsg(TF_SUBSFOLDER, TEXT("odt - DragOver")); if (m_grfKeyStateLast == grfKeyState) { // Return the effect we saved at dragenter time if (*pdwEffect) { *pdwEffect = m_dwEffectLastReturned; } } else { if (*pdwEffect) { *pdwEffect = m_dwEffectLastReturned = GetDropEffect(pdwEffect); } } m_grfKeyStateLast = grfKeyState; return S_OK; } // // DragLeave // STDMETHODIMP COfflineDropTarget::DragLeave() { // TraceMsg(TF_SUBSFOLDER, TEXT("odt - DragLeave")); if (m_pDataObj) { m_pDataObj->Release(); m_pDataObj = NULL; } return S_OK; } // // Drop // STDMETHODIMP COfflineDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) { // UINT idCmd; // Choice from drop popup menu HRESULT hr = S_OK; // // Take the new data object, since OLE can give us a different one than // it did in DragEnter // // TraceMsg(TF_SUBSFOLDER, TEXT("odt - Drop")); if (m_pDataObj) { m_pDataObj->Release(); } m_pDataObj = pDataObj; if (pDataObj) { pDataObj->AddRef(); } // If the dataobject doesn't have an HDROP, its not much good to us *pdwEffect &= DROPEFFECT_COPY|DROPEFFECT_LINK; if (!(*pdwEffect)) { DragLeave(); return S_OK; } hr = E_NOINTERFACE; if (m_fHasHDROP) hr = _ConvertHDROPData(pDataObj, TRUE); else { TCHAR url[INTERNET_MAX_URL_LENGTH], name[MAX_NAME_QUICKLINK]; if (m_fHasSHELLURL) hr = _GetURLData(pDataObj, CITBDTYPE_URL, url, name); if (FAILED(hr) && m_fHasTEXT) hr = _GetURLData(pDataObj, CITBDTYPE_TEXT, url, name); if (SUCCEEDED(hr)) { TraceMsg(TF_SUBSFOLDER, "URL: %s, Name: %s", url, name); hr = ScheduleDefault(url, name); } } if (FAILED(hr)) { TraceMsg(TF_SUBSFOLDER, "Couldn't DROP"); } DragLeave(); return hr; } HRESULT _CLSIDFromExtension( LPCTSTR pszExt, CLSID *pclsid) { TCHAR szProgID[80]; long cb = SIZEOF(szProgID); if (RegQueryValue(HKEY_CLASSES_ROOT, pszExt, szProgID, &cb) == ERROR_SUCCESS) { TCHAR szCLSID[80]; StrCatN(szProgID, TEXT("\\CLSID"), ARRAYSIZE(szProgID)); cb = SIZEOF(szCLSID); if (RegQueryValue(HKEY_CLASSES_ROOT, szProgID, szCLSID, &cb) == ERROR_SUCCESS) { // FEATURE (scotth): call shell32's SHCLSIDFromString once it // exports A/W versions. This would clean this // up. #ifdef UNICODE return CLSIDFromString(szCLSID, pclsid); #else WCHAR wszCLSID[80]; MultiByteToWideChar(CP_ACP, 0, szCLSID, -1, wszCLSID, ARRAYSIZE(wszCLSID)); return CLSIDFromString(wszCLSID, pclsid); #endif } } return E_FAIL; } // get the target of a shortcut. this uses IShellLink which // Internet Shortcuts (.URL) and Shell Shortcuts (.LNK) support so // it should work generally // HRESULT _GetURLTarget(LPCTSTR pszPath, LPTSTR pszTarget, UINT cch) { IShellLink *psl; HRESULT hr = E_FAIL; CLSID clsid; if (FAILED(_CLSIDFromExtension(PathFindExtension(pszPath), &clsid))) clsid = CLSID_ShellLink; // assume it's a shell link hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&psl); if (SUCCEEDED(hr)) { IPersistFile *ppf; hr = psl->QueryInterface(IID_IPersistFile, (void **)&ppf); if (SUCCEEDED(hr)) { #ifdef UNICODE hr = ppf->Load(pszPath, 0); #else WCHAR wszPath[MAX_PATH]; MultiByteToWideChar(CP_ACP, 0, pszPath, -1, wszPath, ARRAYSIZE(wszPath)); hr = ppf->Load (wszPath, 0); #endif ppf->Release(); } if (SUCCEEDED(hr)) { IUniformResourceLocator * purl; hr = psl->QueryInterface(IID_IUniformResourceLocator,(void**)&purl); if (SUCCEEDED(hr)) purl->Release(); } if (SUCCEEDED(hr)) hr = psl->GetPath(pszTarget, cch, NULL, SLGP_UNCPRIORITY); psl->Release(); } return hr; } HRESULT _ConvertHDROPData(IDataObject *pdtobj, BOOL bSubscribe) { HRESULT hRes = NOERROR; STGMEDIUM stgmedium; FORMATETC formatetc; TCHAR url[INTERNET_MAX_URL_LENGTH]; TCHAR name[MAX_NAME_QUICKLINK]; name[0] = 0; url[0] = 0; formatetc.cfFormat = CF_HDROP; formatetc.ptd = NULL; formatetc.dwAspect = DVASPECT_CONTENT; formatetc.lindex = -1; formatetc.tymed = TYMED_HGLOBAL; // Get the parse string hRes = pdtobj->GetData(&formatetc, &stgmedium); if (SUCCEEDED(hRes)) { LPTSTR pszURLData = (LPTSTR)GlobalLock(stgmedium.hGlobal); if (pszURLData) { TCHAR szPath[MAX_PATH]; SHFILEINFO sfi; int cFiles, i; HDROP hDrop = (HDROP)stgmedium.hGlobal; hRes = S_FALSE; cFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); for (i = 0; i < cFiles; i ++) { DragQueryFile(hDrop, i, szPath, ARRAYSIZE(szPath)); // defaults... StrCpyN(name, szPath, MAX_NAME_QUICKLINK); if (SHGetFileInfo(szPath, 0, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME)) StrCpyN(name, sfi.szDisplayName, MAX_NAME_QUICKLINK); if (SHGetFileInfo(szPath, 0, &sfi, sizeof(sfi),SHGFI_ATTRIBUTES) && (sfi.dwAttributes & SFGAO_LINK)) { if (SUCCEEDED(_GetURLTarget(szPath, url, INTERNET_MAX_URL_LENGTH))) { TraceMsg(TF_SUBSFOLDER, "URL: %s, Name: %s", url, name); // If we just want to see whether there is some urls // here, we can break now. if (!bSubscribe) { if ((IsHTTPPrefixed(url)) && (!SHRestricted2(REST_NoAddingSubscriptions, url, 0))) { hRes = S_OK; } break; } hRes = ScheduleDefault(url, name); } } } GlobalUnlock(stgmedium.hGlobal); if (bSubscribe) hRes = S_OK; } else hRes = S_FALSE; ReleaseStgMedium(&stgmedium); } return hRes; } // Takes a variety of inputs and returns a string for drop targets. // szUrl: the URL // szName: the name (for quicklinks and the confo dialog boxes) // returns: NOERROR if succeeded // HRESULT _GetURLData(IDataObject *pdtobj, int iDropType, TCHAR *pszUrl, TCHAR *pszName) { HRESULT hRes = NOERROR; STGMEDIUM stgmedium; FORMATETC formatetc; *pszName = 0; *pszUrl = 0; switch (iDropType) { case CITBDTYPE_URL: formatetc.cfFormat = (CLIPFORMAT) RegisterClipboardFormat(CFSTR_SHELLURL); break; case CITBDTYPE_TEXT: formatetc.cfFormat = CF_TEXT; break; default: return E_UNEXPECTED; } formatetc.ptd = NULL; formatetc.dwAspect = DVASPECT_CONTENT; formatetc.lindex = -1; formatetc.tymed = TYMED_HGLOBAL; // Get the parse string hRes = pdtobj->GetData(&formatetc, &stgmedium); if (SUCCEEDED(hRes)) { LPTSTR pszURLData = (LPTSTR)GlobalLock(stgmedium.hGlobal); if (pszURLData) { if (iDropType == CITBDTYPE_URL) { STGMEDIUM stgmediumFGD; // defaults StrCpyN(pszUrl, pszURLData, INTERNET_MAX_URL_LENGTH); StrCpyN(pszName, pszURLData, MAX_NAME_QUICKLINK); formatetc.cfFormat = (CLIPFORMAT) RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR); if (SUCCEEDED(pdtobj->GetData(&formatetc, &stgmediumFGD))) { FILEGROUPDESCRIPTOR *pfgd = (FILEGROUPDESCRIPTOR *)GlobalLock(stgmediumFGD.hGlobal); if (pfgd) { TCHAR szPath[MAX_PATH]; StrCpyN(szPath, pfgd->fgd[0].cFileName, ARRAYSIZE(szPath)); PathRemoveExtension(szPath); StrCpyN(pszName, szPath, MAX_NAME_QUICKLINK); GlobalUnlock(stgmediumFGD.hGlobal); } ReleaseStgMedium(&stgmediumFGD); } } else if (iDropType == CITBDTYPE_TEXT) { if (PathIsURL(pszURLData)) { StrCpyN(pszUrl, pszURLData, INTERNET_MAX_URL_LENGTH); StrCpyN(pszName, pszURLData, MAX_NAME_QUICKLINK); } else hRes = E_FAIL; } GlobalUnlock(stgmedium.hGlobal); } ReleaseStgMedium(&stgmedium); } if (SUCCEEDED(hRes)) { if (!IsHTTPPrefixed(pszUrl) || SHRestricted2(REST_NoAddingSubscriptions, pszUrl, 0)) { hRes = E_FAIL; } } return hRes; } HRESULT ScheduleDefault(LPCTSTR url, LPCTSTR name) { if (!IsHTTPPrefixed(url)) return E_INVALIDARG; ISubscriptionMgr * pSub= NULL; HRESULT hr = CoInitialize(NULL); RETURN_ON_FAILURE(hr); hr = CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER, IID_ISubscriptionMgr, (void **)&pSub); CoUninitialize(); RETURN_ON_FAILURE(hr); ASSERT(pSub); BSTR bstrURL = NULL, bstrName = NULL; hr = CreateBSTRFromTSTR(&bstrURL, url); if (S_OK == hr) hr = CreateBSTRFromTSTR(&bstrName, name); // We need a perfectly valid structure. SUBSCRIPTIONINFO subInfo; ZeroMemory((void *)&subInfo, sizeof (SUBSCRIPTIONINFO)); subInfo.cbSize = sizeof(SUBSCRIPTIONINFO); if (S_OK == hr) hr = pSub->CreateSubscription(NULL, bstrURL, bstrName, CREATESUBS_NOUI, SUBSTYPE_URL, &subInfo); SAFERELEASE(pSub); SAFEFREEBSTR(bstrURL); SAFEFREEBSTR(bstrName); if (FAILED(hr)) { TraceMsg(TF_ALWAYS, "Failed to add default object."); TraceMsg(TF_ALWAYS, " hr = 0x%x\n", hr); return (FAILED(hr))?hr:E_FAIL; } else if (hr == S_FALSE) { TraceMsg(TF_SUBSFOLDER, "%s(%s) is already there.", url, name); return hr; } return S_OK; }