|
|
/*
* url.cpp - IUniformResourceLocator implementation for InternetShortcut class. */
/* Headers
**********/
#include "project.hpp"
#pragma hdrstop
#include "assoc.h"
#include "resource.h"
#define DECL_CRTFREE
#include <crtfree.h>
/* 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 */
|