/* * url.cpp - IUniformResourceLocator implementation for InternetShortcut class. */ /* Headers **********/ #include "project.hpp" #pragma hdrstop #include "assoc.h" #include "resource.h" #define DECL_CRTFREE #include /* Module Constants *******************/ #pragma data_seg(DATA_SEG_READ_ONLY) PRIVATE_DATA const char s_cszURLSeparator[] = ":"; PRIVATE_DATA const char s_cchURLSuffixSlash = '/'; PRIVATE_DATA const char s_cszURLPrefixesKey[] = "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes"; PRIVATE_DATA const char s_cszDefaultURLPrefixKey[] = "Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix"; #pragma data_seg() /***************************** Private Functions *****************************/ PRIVATE_CODE PCSTR SkipLeadingSlashes(PCSTR pcszURL) { PCSTR pcszURLStart; ASSERT(IS_VALID_STRING_PTR(pcszURL, CSTR)); // Skip two leading slashes. if (pcszURL[0] == s_cchURLSuffixSlash && pcszURL[1] == s_cchURLSuffixSlash) { pcszURLStart = CharNext(pcszURL); pcszURLStart = CharNext(pcszURLStart); } else pcszURLStart = pcszURL; ASSERT(IS_VALID_STRING_PTR(pcszURL, CSTR) && EVAL(IsStringContained(pcszURL, pcszURLStart))); return(pcszURLStart); } PRIVATE_CODE HRESULT ApplyURLPrefix(PCSTR pcszURL, PSTR *ppszTranslatedURL) { HRESULT hr = S_FALSE; HKEY hkeyPrefixes; ASSERT(IS_VALID_STRING_PTR(pcszURL, CSTR)); ASSERT(IS_VALID_WRITE_PTR(ppszTranslatedURL, PSTR)); *ppszTranslatedURL = NULL; if (RegOpenKey(g_hkeyURLSettings, s_cszURLPrefixesKey, &hkeyPrefixes) == ERROR_SUCCESS) { PCSTR pcszURLStart; DWORD dwiValue; char rgchValueName[MAX_PATH_LEN]; DWORD dwcbValueNameLen; DWORD dwType; char rgchPrefix[MAX_PATH_LEN]; DWORD dwcbPrefixLen; pcszURLStart = SkipLeadingSlashes(pcszURL); dwcbValueNameLen = sizeof(rgchValueName); dwcbPrefixLen = sizeof(rgchPrefix); for (dwiValue = 0; RegEnumValue(hkeyPrefixes, dwiValue, rgchValueName, &dwcbValueNameLen, NULL, &dwType, (PBYTE)rgchPrefix, &dwcbPrefixLen) == ERROR_SUCCESS; dwiValue++) { if (! lstrnicmp(pcszURLStart, rgchValueName, dwcbValueNameLen)) { DWORD cbUrl = lstrlen(pcszURLStart); // If the url==prefix, then we're calling the executable. if (cbUrl >= dwcbPrefixLen) { // dwcbPrefixLen includes null terminator. *ppszTranslatedURL = new(char[dwcbPrefixLen + cbUrl]); if (*ppszTranslatedURL) { lstrcpy(*ppszTranslatedURL, rgchPrefix); lstrcat(*ppszTranslatedURL, pcszURLStart); // (+ 1) for null terminator. ASSERT(lstrlen(*ppszTranslatedURL) + 1 == (int)dwcbPrefixLen + lstrlen(pcszURLStart)); hr = S_OK; } else hr = E_OUTOFMEMORY; } break; } dwcbValueNameLen = sizeof(rgchValueName); dwcbPrefixLen = sizeof(rgchPrefix); } RegCloseKey(hkeyPrefixes); switch (hr) { case S_OK: TRACE_OUT(("ApplyURLPrefix(): Prefix %s prepended to prefix %s of %s to yield URL %s.", rgchValueName, rgchPrefix, pcszURL, *ppszTranslatedURL)); break; case S_FALSE: TRACE_OUT(("ApplyURLPrefix(): No matching prefix found for URL %s.", pcszURL)); break; default: ASSERT(hr == E_OUTOFMEMORY); break; } } else TRACE_OUT(("ApplyURLPrefix(): No URL prefixes registered.")); ASSERT((hr == S_OK && IS_VALID_STRING_PTR(*ppszTranslatedURL, STR)) || ((hr == S_FALSE || hr == E_OUTOFMEMORY) && ! *ppszTranslatedURL)); return(hr); } PRIVATE_CODE HRESULT ApplyDefaultURLPrefix(PCSTR pcszURL, PSTR *ppszTranslatedURL) { HRESULT hr; char rgchDefaultURLPrefix[MAX_PATH_LEN]; DWORD dwcbDefaultURLPrefixLen; ASSERT(IS_VALID_STRING_PTR(pcszURL, CSTR)); ASSERT(IS_VALID_WRITE_PTR(ppszTranslatedURL, PSTR)); *ppszTranslatedURL = NULL; dwcbDefaultURLPrefixLen = sizeof(rgchDefaultURLPrefix); if (GetDefaultRegKeyValue(g_hkeyURLSettings, s_cszDefaultURLPrefixKey, rgchDefaultURLPrefix, &dwcbDefaultURLPrefixLen) == ERROR_SUCCESS) { // (+ 1) for null terminator. *ppszTranslatedURL = new(char[dwcbDefaultURLPrefixLen + lstrlen(pcszURL) + 1]); if (*ppszTranslatedURL) { PCSTR pcszURLStart; pcszURLStart = SkipLeadingSlashes(pcszURL); lstrcpy(*ppszTranslatedURL, rgchDefaultURLPrefix); lstrcat(*ppszTranslatedURL, pcszURLStart); // (+ 1) for null terminator. ASSERT(lstrlen(*ppszTranslatedURL) + 1 == (int)dwcbDefaultURLPrefixLen + lstrlen(pcszURLStart)); hr = S_OK; } else hr = E_OUTOFMEMORY; } else hr = S_FALSE; switch (hr) { case S_OK: TRACE_OUT(("ApplyDefaultURLPrefix(): Default prefix %s prepended to %s to yield URL %s.", rgchDefaultURLPrefix, pcszURL, *ppszTranslatedURL)); break; case S_FALSE: TRACE_OUT(("ApplyDefaultURLPrefix(): No default URL prefix registered.")); break; default: ASSERT(hr == E_OUTOFMEMORY); break; } ASSERT((hr == S_OK && IS_VALID_STRING_PTR(*ppszTranslatedURL, STR)) || ((hr == S_FALSE || hr == E_OUTOFMEMORY) && ! *ppszTranslatedURL)); return(hr); } PRIVATE_CODE HRESULT MyTranslateURL(PCSTR pcszURL, DWORD dwFlags, PSTR *ppszTranslatedURL) { HRESULT hr; PARSEDURL pu; ASSERT(IS_VALID_STRING_PTR(pcszURL, CSTR)); ASSERT(FLAGS_ARE_VALID(dwFlags, ALL_TRANSLATEURL_FLAGS)); ASSERT(IS_VALID_WRITE_PTR(ppszTranslatedURL, PSTR)); // Check URL syntax. pu.cbSize = sizeof(pu); if (ParseURL(pcszURL, &pu) == S_OK) { *ppszTranslatedURL = NULL; hr = S_FALSE; } else { if (IS_FLAG_SET(dwFlags, TRANSLATEURL_FL_GUESS_PROTOCOL)) hr = ApplyURLPrefix(pcszURL, ppszTranslatedURL); else hr = S_FALSE; if (hr == S_FALSE && IS_FLAG_SET(dwFlags, TRANSLATEURL_FL_USE_DEFAULT_PROTOCOL)) hr = ApplyDefaultURLPrefix(pcszURL, ppszTranslatedURL); } ASSERT((hr == S_OK && IS_VALID_STRING_PTR(*ppszTranslatedURL, STR)) || ((hr == S_FALSE || hr == E_OUTOFMEMORY) && ! *ppszTranslatedURL)); return(hr); } #ifdef DEBUG PRIVATE_CODE IsValidPCURLINVOKECOMMANDINFO(PCURLINVOKECOMMANDINFO pcurlici) { return(IS_VALID_READ_PTR(pcurlici, CURLINVOKECOMMANDINFO) && EVAL(pcurlici->dwcbSize >= sizeof(*pcurlici)) && FLAGS_ARE_VALID(pcurlici->dwFlags, ALL_IURL_INVOKECOMMAND_FLAGS) && (IS_FLAG_CLEAR(pcurlici->dwFlags, IURL_INVOKECOMMAND_FL_ALLOW_UI) || IS_VALID_HANDLE(pcurlici->hwndParent, WND)) && (IS_FLAG_SET(pcurlici->dwFlags, IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB) || IS_VALID_STRING_PTR(pcurlici->pcszVerb, CSTR))); } #endif /********************************** Methods **********************************/ HRESULT STDMETHODCALLTYPE InternetShortcut::RegisterProtocolHandler( HWND hwndParent, PSTR pszAppBuf, UINT ucAppBufLen) { HRESULT hr; DWORD dwFlags = 0; DebugEntry(InternetShortcut::RegisterProtocolHandler); ASSERT(! hwndParent || IS_VALID_HANDLE(hwndParent, WND)); ASSERT(IS_VALID_WRITE_BUFFER_PTR(pszAppBuf, STR, ucAppBufLen)); ASSERT(IS_VALID_STRING_PTR(m_pszURL, STR)); SET_FLAG(dwFlags, URLASSOCDLG_FL_REGISTER_ASSOC); if (! m_pszFile) SET_FLAG(dwFlags, URLASSOCDLG_FL_USE_DEFAULT_NAME); hr = URLAssociationDialog(hwndParent, dwFlags, m_pszFile, m_pszURL, pszAppBuf, ucAppBufLen); switch (hr) { case S_FALSE: TRACE_OUT(("InternetShortcut::RegisterProtocolHandler(): One time execution of %s via %s requested.", m_pszURL, pszAppBuf)); break; case S_OK: TRACE_OUT(("InternetShortcut::RegisterProtocolHandler(): Protocol handler registered for %s.", m_pszURL)); break; default: ASSERT(FAILED(hr)); break; } ASSERT(! ucAppBufLen || (IS_VALID_STRING_PTR(pszAppBuf, STR) && (UINT)lstrlen(pszAppBuf) < ucAppBufLen)); DebugExitHRESULT(InternetShortcut::RegisterProtocolHandler, hr); return(hr); } HRESULT STDMETHODCALLTYPE InternetShortcut::SetURL(PCSTR pcszURL, DWORD dwInFlags) { HRESULT hr; BOOL bChanged; PSTR pszNewURL = NULL; DebugEntry(InternetShortcut::SetURL); ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut)); ASSERT(! pcszURL || IS_VALID_STRING_PTR(pcszURL, CSTR)); ASSERT(FLAGS_ARE_VALID(dwInFlags, ALL_IURL_SETURL_FLAGS)); bChanged = ! ((! pcszURL && ! m_pszURL) || (pcszURL && m_pszURL && ! lstrcmp(pcszURL, m_pszURL))); if (bChanged && pcszURL) { DWORD dwTranslateURLFlags; PSTR pszTranslatedURL; dwTranslateURLFlags = 0; if (IS_FLAG_SET(dwInFlags, IURL_SETURL_FL_GUESS_PROTOCOL)) SET_FLAG(dwTranslateURLFlags, TRANSLATEURL_FL_GUESS_PROTOCOL); if (IS_FLAG_SET(dwInFlags, IURL_SETURL_FL_USE_DEFAULT_PROTOCOL)) SET_FLAG(dwTranslateURLFlags, TRANSLATEURL_FL_USE_DEFAULT_PROTOCOL); hr = TranslateURL(pcszURL, dwTranslateURLFlags, &pszTranslatedURL); if (SUCCEEDED(hr)) { PCSTR pcszURLToUse; // Still different? if (hr == S_OK) { bChanged = (lstrcmp(pszTranslatedURL, m_pszURL) != 0); pcszURLToUse = pszTranslatedURL; } else { ASSERT(hr == S_FALSE); pcszURLToUse = pcszURL; } if (bChanged) { PARSEDURL pu; // Validate URL syntax. pu.cbSize = sizeof(pu); hr = ParseURL(pcszURLToUse, &pu); if (hr == S_OK) { pszNewURL = new(char[lstrlen(pcszURLToUse) + 1]); if (pszNewURL) lstrcpy(pszNewURL, pcszURLToUse); else hr = E_OUTOFMEMORY; } } else hr = S_OK; } if (pszTranslatedURL) { LocalFree(pszTranslatedURL); pszTranslatedURL = NULL; } } else hr = S_OK; if (hr == S_OK) { if (bChanged) { if (m_pszURL) delete m_pszURL; m_pszURL = pszNewURL; Dirty(TRUE); TRACE_OUT(("InternetShortcut::SetURL(): Set URL to %s.", CHECK_STRING(m_pszURL))); } else TRACE_OUT(("InternetShortcut::SetURL(): URL already %s.", CHECK_STRING(m_pszURL))); } ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut)); ASSERT(hr == S_OK || hr == E_OUTOFMEMORY || hr == URL_E_INVALID_SYNTAX); DebugExitHRESULT(InternetShortcut::SetURL, hr); return(hr); } HRESULT STDMETHODCALLTYPE InternetShortcut::GetURL(PSTR *ppszURL) { HRESULT hr; DebugEntry(InternetShortcut::GetURL); ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut)); ASSERT(IS_VALID_WRITE_PTR(ppszURL, PSTR)); *ppszURL = NULL; if (m_pszURL) { // (+ 1) for null terminator. *ppszURL = (PSTR)SHAlloc(lstrlen(m_pszURL) + 1); if (*ppszURL) { lstrcpy(*ppszURL, m_pszURL); hr = S_OK; TRACE_OUT(("InternetShortcut::GetURL(): Got URL %s.", *ppszURL)); } else hr = E_OUTOFMEMORY; } else // No URL. hr = S_FALSE; ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut)); ASSERT((hr == S_OK && IS_VALID_STRING_PTR(*ppszURL, STR)) || ((hr == S_FALSE || hr == E_OUTOFMEMORY) && ! *ppszURL)); DebugExitHRESULT(InternetShortcut::GetURL, hr); return(hr); } HRESULT STDMETHODCALLTYPE InternetShortcut::InvokeCommand( PURLINVOKECOMMANDINFO purlici) { HRESULT hr = E_INVALIDARG; char szOneShotApp[MAX_PATH_LEN]; BOOL bExecFailedWhine = FALSE; DebugEntry(InternetShortcut::InvokeCommand); ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut)); ASSERT(IS_VALID_STRUCT_PTR(purlici, CURLINVOKECOMMANDINFO)); if (purlici && EVAL(sizeof(*purlici) == purlici->dwcbSize)) { if (m_pszURL) { PSTR pszProtocol; hr = CopyURLProtocol(m_pszURL, &pszProtocol); if (hr == S_OK) { hr = IsProtocolRegistered(pszProtocol); if (hr == URL_E_UNREGISTERED_PROTOCOL && IS_FLAG_SET(purlici->dwFlags, IURL_INVOKECOMMAND_FL_ALLOW_UI)) { TRACE_OUT(("InternetShortcut::InvokeCommand(): Unregistered URL protocol %s. Invoking URL protocol handler association dialog.", pszProtocol)); hr = RegisterProtocolHandler(purlici->hwndParent, szOneShotApp, sizeof(szOneShotApp)); } switch (hr) { case S_OK: { SHELLEXECUTEINFO sei; char szDefaultVerb[MAX_PATH_LEN]; // Execute URL via registered protocol handler. ZeroMemory(&sei, sizeof(sei)); sei.fMask = (SEE_MASK_CLASSNAME | SEE_MASK_NO_HOOKS); if (IS_FLAG_CLEAR(purlici->dwFlags, IURL_INVOKECOMMAND_FL_ALLOW_UI)) SET_FLAG(sei.fMask, SEE_MASK_FLAG_NO_UI); if (IS_FLAG_CLEAR(purlici->dwFlags, IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB)) sei.lpVerb = purlici->pcszVerb; else { if (GetClassDefaultVerb(pszProtocol, szDefaultVerb, sizeof(szDefaultVerb))) sei.lpVerb = szDefaultVerb; else ASSERT(! sei.lpVerb); } sei.cbSize = sizeof(sei); sei.hwnd = purlici->hwndParent; sei.lpFile = m_pszURL; sei.lpDirectory = m_pszWorkingDirectory; sei.nShow = m_nShowCmd; sei.lpClass = pszProtocol; TRACE_OUT(("InternetShortcut::InvokeCommand(): Invoking %s verb on URL %s.", sei.lpVerb ? sei.lpVerb : "open", sei.lpFile)); hr = ShellExecuteEx(&sei) ? S_OK : IS_E_EXEC_FAILED; if (hr == S_OK) TRACE_OUT(("InternetShortcut::InvokeCommand(): ShellExecuteEx() via registered protcol handler succeeded for %s.", m_pszURL)); else WARNING_OUT(("InternetShortcut::InvokeCommand(): ShellExecuteEx() via registered protcol handler failed for %s.", m_pszURL)); break; } case S_FALSE: hr = MyExecute(szOneShotApp, m_pszURL, 0); switch (hr) { case E_FAIL: bExecFailedWhine = TRUE; hr = IS_E_EXEC_FAILED; break; default: break; } break; default: ASSERT(FAILED(hr)); break; } delete pszProtocol; pszProtocol = NULL; } } else // No URL. Not an error. hr = S_FALSE; if (FAILED(hr) && IS_FLAG_SET(purlici->dwFlags, IURL_INVOKECOMMAND_FL_ALLOW_UI)) { int nResult; switch (hr) { case IS_E_EXEC_FAILED: if (bExecFailedWhine) { ASSERT(IS_VALID_STRING_PTR(szOneShotApp, STR)); if (MyMsgBox(purlici->hwndParent, MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE), MAKEINTRESOURCE(IDS_EXEC_FAILED), (MB_OK | MB_ICONEXCLAMATION), &nResult, szOneShotApp)) { ASSERT(nResult == IDOK); } } break; case URL_E_INVALID_SYNTAX: ASSERT(IS_VALID_STRING_PTR(m_pszURL, STR)); if (MyMsgBox(purlici->hwndParent, MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE), MAKEINTRESOURCE(IDS_EXEC_INVALID_SYNTAX), (MB_OK | MB_ICONEXCLAMATION), &nResult, m_pszURL)) { ASSERT(nResult == IDOK); } break; case URL_E_UNREGISTERED_PROTOCOL: { PSTR pszProtocol; ASSERT(IS_VALID_STRING_PTR(m_pszURL, STR)); if (CopyURLProtocol(m_pszURL, &pszProtocol) == S_OK) { if (MyMsgBox(purlici->hwndParent, MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE), MAKEINTRESOURCE(IDS_EXEC_UNREGISTERED_PROTOCOL), (MB_OK | MB_ICONEXCLAMATION), &nResult, pszProtocol)) { ASSERT(nResult == IDOK); } delete pszProtocol; pszProtocol = NULL; } break; } case E_OUTOFMEMORY: if (MyMsgBox(purlici->hwndParent, MAKEINTRESOURCE(IDS_SHORTCUT_ERROR_TITLE), MAKEINTRESOURCE(IDS_EXEC_OUT_OF_MEMORY), (MB_OK | MB_ICONEXCLAMATION), &nResult)) { ASSERT(nResult == IDOK); } break; default: ASSERT(hr == E_ABORT); break; } } } ASSERT(IS_VALID_STRUCT_PTR(this, CInternetShortcut)); ASSERT(hr == S_OK || hr == E_ABORT || hr == E_OUTOFMEMORY || hr == E_INVALIDARG || hr == URL_E_INVALID_SYNTAX || hr == URL_E_UNREGISTERED_PROTOCOL || hr == IS_E_EXEC_FAILED); DebugExitHRESULT(InternetShortcut::InvokeCommand, hr); return(hr); } /***************************** Exported Functions ****************************/ INTSHCUTAPI HRESULT WINAPI TranslateURLA(PCSTR pcszURL, DWORD dwInFlags, PSTR *ppszTranslatedURL) { HRESULT hr; DebugEntry(TranslateURLA); *ppszTranslatedURL = NULL; #ifdef EXPV /* Verify parameters. */ if (IS_VALID_STRING_PTR(pcszURL, CSTR) && IS_VALID_WRITE_PTR(ppszTranslatedURL, PSTR)) { if (FLAGS_ARE_VALID(dwInFlags, ALL_TRANSLATEURL_FLAGS)) #endif { PSTR pszTempTranslatedURL; hr = MyTranslateURL(pcszURL, dwInFlags, &pszTempTranslatedURL); if (hr == S_OK) { // (+ 1) for null terminator. *ppszTranslatedURL = (PSTR)LocalAlloc( LMEM_FIXED, lstrlen(pszTempTranslatedURL) + 1); if (*ppszTranslatedURL) { lstrcpy(*ppszTranslatedURL, pszTempTranslatedURL); ASSERT(lstrlen(*ppszTranslatedURL) == lstrlen(pszTempTranslatedURL)); } else hr = E_OUTOFMEMORY; delete pszTempTranslatedURL; pszTempTranslatedURL = NULL; } } #ifdef EXPV else hr = E_FLAGS; } else hr = E_POINTER; #endif switch (hr) { case S_FALSE: TRACE_OUT(("TranslateURLA(): URL %s does not require translation.", pcszURL)); break; case S_OK: TRACE_OUT(("TranslateURLA(): URL %s translated to URL %s.", pcszURL, *ppszTranslatedURL)); break; default: ASSERT(hr == E_OUTOFMEMORY || hr == E_POINTER); break; } ASSERT((hr == S_OK && IS_VALID_STRING_PTR(*ppszTranslatedURL, STR)) || ((hr == S_FALSE || hr == E_OUTOFMEMORY || hr == E_POINTER || hr == E_FLAGS) && ! *ppszTranslatedURL)); DebugExitHRESULT(TranslateURLA, hr); return(hr); } #pragma warning(disable:4100) /* "unreferenced formal parameter" warning */ INTSHCUTAPI HRESULT WINAPI TranslateURLW(PCWSTR pcszURL, DWORD dwInFlags, PWSTR UNALIGNED *ppszTranslatedURL) { HRESULT hr; DebugEntry(TranslateURLW); SetLastError(ERROR_NOT_SUPPORTED); hr = E_NOTIMPL; DebugExitHRESULT(TranslateURLW, hr); return(hr); } #pragma warning(default:4100) /* "unreferenced formal parameter" warning */