WindowsXP-SP1/shell/shdocvw/url.cpp

470 lines
15 KiB
C++

/*
* url.cpp - IUniformResourceLocator implementation for InternetShortcut class.
*/
/* Headers
**********/
#include "priv.h"
#pragma hdrstop
#define INC_OLE2
#include "intshcut.h"
/* Module Constants
*******************/
const TCHAR c_szURLPrefixesKey[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes");
const TCHAR c_szDefaultURLPrefixKey[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix");
// DPA array that holds the IURLSearchHook Pointers
static HDPA g_hdpaHooks = NULL;
// CURRENT_USER
static const TCHAR c_szURLSearchHook[] = TSZIEPATH TEXT("\\URLSearchHooks");
/***************************** Private Functions *****************************/
int DPA_DestroyURLSearchHooksCallback(LPVOID p, LPVOID d)
{
IURLSearchHook * psuh = (IURLSearchHook *)p;
ASSERT(psuh);
ATOMICRELEASET(psuh, IURLSearchHook);
return 1;
}
extern "C" {
void DestroyHdpaHooks()
{
if (g_hdpaHooks)
{
ENTERCRITICAL;
//---------------------------- Critical Section -------------------------
HDPA hdpa = g_hdpaHooks;
g_hdpaHooks = NULL;
//-----------------------------------------------------------------------
LEAVECRITICAL;
if (hdpa)
{
DPA_DestroyCallback(hdpa, DPA_DestroyURLSearchHooksCallback, 0);
hdpa = NULL;
}
}
}
}
HRESULT InvokeURLSearchHook(IURLSearchHook * pusHook, LPCTSTR pcszQuery, LPTSTR pszResult, ISearchContext * pSC)
{
HRESULT hr = E_FAIL;
ASSERT(pusHook);
WCHAR szSearchURL[MAX_URL_STRING];
SHTCharToUnicode(pcszQuery, szSearchURL, ARRAYSIZE(szSearchURL));
// if we can get an IURLSearchHook2, we'll pass in the
// search context, otherwise we'll just do without
IURLSearchHook2 * pUSH2 = NULL;
hr = pusHook->QueryInterface(IID_IURLSearchHook2, (void **)&pUSH2);
if (SUCCEEDED(hr))
{
RIP(pUSH2 != NULL);
hr = pUSH2->TranslateWithSearchContext(szSearchURL, ARRAYSIZE(szSearchURL), pSC);
pUSH2->Release();
}
else
{
hr = pusHook->Translate(szSearchURL, ARRAYSIZE(szSearchURL));
}
// In case the URLSearchHook worked, convert result to TCHAR
// This includes two cases: S_OK and S_FALSE
if (SUCCEEDED(hr))
{
//WARNING: (dli) Assuming pszResult size = MAX_URL_STRING
SHUnicodeToTChar(szSearchURL, pszResult, MAX_URL_STRING);
}
return hr;
}
/*
* Returns:
* S_OK Search handled completely, pszResult has the full URL to browse to.
* 0x00000000 Stop running any further IURLSearchHooks and pass this URL back to
* the browser for browsing.
*
* S_FALSE Query has been preprocessed, pszResult has the result of the preprocess,
* 0x00000001 further search still needed. Go on executing the rest of the IURLSearchHooks
* The preprocessing steps can be: 1. replaced certain characters
* 2. added more hints
*
* E_ABORT Search handled completely, stop running any further IURLSearchHooks,
* 0x80004004 but NO BROWSING NEEDED as a result, pszResult is a copy of pcszQuery.
* FEATURE: This is not fully implemented, yet, making IURLQualify return this
* involves too much change.
*
* E_FAIL This Hook was unsuccessful. Search not handled at all, pcszQueryURL has the
* 0x80004005 query string. Please go on running other IURLSearchHooks.
* return
*/
HRESULT TryURLSearchHooks(LPCTSTR pcszQuery, LPTSTR pszResult, ISearchContext * pSC)
{
HRESULT hr = E_FAIL;
TCHAR szNewQuery[MAX_URL_STRING];
StrCpyN(szNewQuery, pcszQuery, ARRAYSIZE(szNewQuery));
int ihdpa;
for (ihdpa = 0; ihdpa < (g_hdpaHooks ? DPA_GetPtrCount(g_hdpaHooks) : 0); ihdpa++)
{
IURLSearchHook * pusHook;
pusHook = (IURLSearchHook *) DPA_GetPtr(g_hdpaHooks, ihdpa);
if (!pusHook)
return E_FAIL;
hr = InvokeURLSearchHook(pusHook, szNewQuery, pszResult, pSC);
if ((hr == S_OK) || (hr == E_ABORT))
break;
else if (hr == S_FALSE)
StrCpyN(szNewQuery, pszResult, ARRAYSIZE(szNewQuery));
}
return hr;
}
void InitURLSearchHooks()
{
HDPA hdpa = DPA_Create(4);
// We need to look in LOCAL_MACHINE if this registry entry doesn't exist in CURRENT_USER.
// The installer needs to install the values into LOCAL_MACHINE so they are accessable
// to all users. Then anyone wanting to modify the value, will need to determine if they
// want to add it to a specific user's CURRENT_USER or modify the LOCAL_MACHINE value to
// apply the change to all users. (bryanst - #6722)
HUSKEY hkeyHooks;
if ((hdpa) && (SHRegOpenUSKey(c_szURLSearchHook, KEY_READ, NULL, &hkeyHooks, FALSE) == ERROR_SUCCESS))
{
TCHAR szCLSID[GUIDSTR_MAX];
DWORD dwccCLSIDLen;
LONG lEnumReturn;
DWORD dwiValue = 0;
do {
dwccCLSIDLen = ARRAYSIZE(szCLSID);
lEnumReturn = SHRegEnumUSValue(hkeyHooks, dwiValue, szCLSID, &dwccCLSIDLen,
NULL, NULL, NULL, SHREGENUM_DEFAULT);
if (lEnumReturn == ERROR_SUCCESS)
{
CLSID clsidHook;
if (SUCCEEDED(SHCLSIDFromString(szCLSID, &clsidHook)))
{
IURLSearchHook * pusHook;
HRESULT hr = CoCreateInstance(clsidHook, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
IID_IURLSearchHook, (LPVOID *)&pusHook);
if (SUCCEEDED(hr))
DPA_AppendPtr(hdpa, pusHook);
}
}
dwiValue++;
} while (lEnumReturn == ERROR_SUCCESS);
SHRegCloseUSKey(hkeyHooks);
}
ENTERCRITICAL;
//---------------------------- Critical Section --------------------------
if (!g_hdpaHooks)
{
g_hdpaHooks = hdpa;
hdpa = NULL;
}
//------------------------------------------------------------------------
LEAVECRITICAL;
if (hdpa)
{
DPA_DestroyCallback(hdpa, DPA_DestroyURLSearchHooksCallback, 0);
hdpa = NULL;
}
}
HRESULT ApplyURLSearch(LPCTSTR pcszQuery, LPTSTR pszTranslatedUrl, ISearchContext * pSC)
{
if (!g_hdpaHooks)
InitURLSearchHooks();
return TryURLSearchHooks(pcszQuery, pszTranslatedUrl, pSC);
}
/*----------------------------------------------------------
Purpose: This function qualifies a string as a URL. Strings
such as "www.foo.com" would have the scheme guessed
if the correct flags are given. Local paths are
converted to "file:" URLs.
pszTranslatedURL may point to the same buffer as
pcszURL.
If the given string is already a URL (not necessarily
canonicalized, though), this function will not touch it,
unless UQF_CANONICALIZE is set, in which case the string
will be canonicalized.
Returns: S_OK or S_FALSE means we filled in pszTranslatedURL.
S_OK means we altered the URL to qualify it too.
various failure codes too
Cond: --
*/
SHDOCAPI
IURLQualifyWithContext(
IN LPCWSTR pcszURL,
IN DWORD dwFlags, // UQF_*
OUT LPWSTR pszTranslatedURL,
LPBOOL pbWasSearchURL,
LPBOOL pbWasCorrected,
ISearchContext * pSC)
{
HRESULT hres = S_FALSE;
DWORD cchSize;
SHSTR strOut;
BOOL bWasCorrected = FALSE;
ASSERT(IS_VALID_STRING_PTR(pcszURL, -1));
ASSERT(IS_VALID_WRITE_BUFFER(pszTranslatedURL, TCHAR, MAX_URL_STRING));
if (pbWasSearchURL)
*pbWasSearchURL = FALSE;
// Special cases: URLs of the form <drive>:<filename>
// URLs of the form \<filename>
// we'll assume that if the second character is a : or |, this is an url of
// that form, and we will guess "file://" for the prefix.
// we'll assume any url that begins with a single \ is a file: url
// NOTE: We do this here because these are cases where the protocol is
// left off, and is likely to be incorrectly guessed, such as a
// relative path \data\ftp\docs, would wrongly be turned
// into "ftp://\data\ftp\docs".
// Note: PathIsURL returns TRUE for non-canonicalized URLs too
if (PathIsURL(pcszURL))
{
LPCWSTR pcszTemp = pcszURL;
cchSize = MAX_URL_STRING;
if (IsFlagSet(dwFlags, UQF_AUTOCORRECT))
{
hres = UrlFixup(pcszURL, pszTranslatedURL, cchSize);
if (hres == S_OK)
{
bWasCorrected = TRUE;
pcszTemp = pszTranslatedURL;
}
}
if (dwFlags & UQF_CANONICALIZE)
hres = UrlCanonicalize(pcszTemp, pszTranslatedURL, &cchSize, 0);
else if (pszTranslatedURL != pcszTemp)
StrCpyN(pszTranslatedURL, pcszTemp, MAX_URL_STRING);
hres = S_OK;
}
else
{
// Look for file paths
if (IsFlagClear(dwFlags, UQF_IGNORE_FILEPATHS) && (
#ifdef UNIX
pcszURL[0] == TEXT('/') ||
#endif
pcszURL[1] == TEXT(':') || pcszURL[1] == TEXT('|') || pcszURL[0] == TEXT('\\')))
{
hres = strOut.SetSize(MAX_PATH);
if(SUCCEEDED(hres))
{
// SHSTRs have a size granularity, so the size
// will be equal to or greater than what was set.
// this means we need to get it our self.
DWORD cchOut = strOut.GetSize();
TCHAR szCurrentDir[MAX_PATH];
//
// APPCOMPAT - IE30 compatibility - zekel 8-Jan-97
// we need to GetCurrentDirectory() in order to
// put a default drive letter on the path
// if necessary.
//
if(GetCurrentDirectory(ARRAYSIZE(szCurrentDir), szCurrentDir))
PathCombine(strOut.GetInplaceStr(), szCurrentDir, pcszURL);
else
hres = strOut.SetStr(pcszURL);
if(SUCCEEDED(hres))
{
hres = UrlCreateFromPath(strOut, strOut.GetInplaceStr(), &cchOut, 0);
if (E_POINTER == hres && SUCCEEDED(hres = strOut.SetSize(cchOut)))
{
cchOut = strOut.GetSize();
hres = UrlCreateFromPath(strOut, strOut.GetInplaceStr(), &cchOut, 0);
}
}
}
}
else if (SUCCEEDED(hres = strOut.SetSize(MAX_URL_STRING)))
{
// all the Apply*() below rely on MAX_URL_STRING
// No; begin processing general-case URLs. Try to guess the
// protocol or resort to the default protocol.
DWORD cchOut = strOut.GetSize();
if (IsFlagSet(dwFlags, UQF_GUESS_PROTOCOL))
hres = UrlApplyScheme(pcszURL, strOut.GetInplaceStr(), &cchOut, URL_APPLY_GUESSSCHEME);
//
// Try to auto-correct the protocol
//
if (hres == S_FALSE &&
IsFlagSet(dwFlags, UQF_AUTOCORRECT))
{
hres = UrlFixup(pcszURL, strOut.GetInplaceStr(), strOut.GetSize());
bWasCorrected = (hres == S_OK);
}
if (hres == S_FALSE &&
IsFlagSet(dwFlags, UQF_USE_DEFAULT_PROTOCOL))
{
// run the search with or without the search context
hres = ApplyURLSearch(pcszURL, strOut.GetInplaceStr(), pSC);
if (SUCCEEDED(hres) && pbWasSearchURL) {
*pbWasSearchURL = TRUE;
}
// If that fails, then tack on the default protocol
if (FAILED(hres) || hres == S_FALSE)
{
cchOut = strOut.GetSize();
hres = UrlApplyScheme(pcszURL, strOut.GetInplaceStr(), &cchOut, URL_APPLY_DEFAULT);
}
}
// Did the above fail?
if (S_FALSE == hres)
{
// Yes; return the real reason why the URL is bad
hres = URL_E_INVALID_SYNTAX;
}
else if (dwFlags & UQF_CANONICALIZE)
{
// No; canonicalize
cchSize = strOut.GetSize();
hres = UrlCanonicalize(strOut, strOut.GetInplaceStr(), &cchSize, 0);
}
}
if (SUCCEEDED(hres))
{
StrCpyN(pszTranslatedURL, strOut, MAX_URL_STRING);
}
}
if (pbWasCorrected)
*pbWasCorrected = bWasCorrected;
return hres;
}
SHDOCAPI
IURLQualify(
IN LPCWSTR pcszURL,
IN DWORD dwFlags, // UQF_*
OUT LPWSTR pszTranslatedURL,
LPBOOL pbWasSearchURL,
LPBOOL pbWasCorrected)
{
return IURLQualifyWithContext(pcszURL, dwFlags, pszTranslatedURL, pbWasSearchURL, pbWasCorrected, NULL);
}
/***************************** Exported Functions ****************************/
STDAPI
URLQualifyA(
LPCSTR pszURL,
DWORD dwFlags, // UQF_*
LPSTR *ppszOut)
{
HRESULT hres;
ASSERT(IS_VALID_STRING_PTRA(pszURL, -1));
ASSERT(IS_VALID_WRITE_PTR(ppszOut, LPSTR));
*ppszOut = NULL;
WCHAR szTempTranslatedURL[MAX_URL_STRING];
WCHAR szURL[MAX_URL_STRING];
SHAnsiToUnicode(pszURL, szURL, ARRAYSIZE(szURL));
hres = IURLQualify(szURL, dwFlags, szTempTranslatedURL, NULL, NULL);
if (SUCCEEDED(hres))
{
CHAR szOut[MAX_URL_STRING];
SHUnicodeToAnsi(szTempTranslatedURL, szOut, ARRAYSIZE(szOut));
*ppszOut = StrDupA(szOut);
if (!*ppszOut)
hres = E_OUTOFMEMORY;
}
return hres;
}
STDAPI
URLQualifyW(
LPCWSTR pszURL,
DWORD dwFlags, // UQF_*
LPWSTR *ppszOut)
{
HRESULT hres;
ASSERT(IS_VALID_STRING_PTRW(pszURL, -1));
ASSERT(IS_VALID_WRITE_PTR(ppszOut, LPWSTR));
WCHAR szTempTranslatedURL[MAX_URL_STRING];
hres = IURLQualify(pszURL, dwFlags, szTempTranslatedURL, NULL, NULL);
if (SUCCEEDED(hres))
{
*ppszOut = StrDup(szTempTranslatedURL);
if (!*ppszOut)
hres = E_OUTOFMEMORY;
}
return hres;
}