windows-nt/Source/XPSP1/NT/shell/shdocvw/urlhook.cpp
2020-09-26 16:20:57 +08:00

567 lines
18 KiB
C++

//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation
//
// File: urlhook.cpp
//
// History:
// 9-24-96 by dli
//------------------------------------------------------------------------
#include "priv.h"
#include "sccls.h"
#include "resource.h"
#include <mluisupp.h>
// CURRENT_USER
static const TCHAR c_szSearchUrl[] = TSZIEPATH TEXT("\\SearchUrl");
#define TF_URLSEARCHHOOK 0
// structure for the character replacement in URL searches
typedef struct _SUrlCharReplace {
TCHAR from;
TCHAR to[10];
} SUrlCharReplace;
class CURLSearchHook : public IURLSearchHook2
{
public:
CURLSearchHook();
// *** IUnknown Methods
virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
virtual STDMETHODIMP_(ULONG) AddRef(void) ;
virtual STDMETHODIMP_(ULONG) Release(void);
// *** IURLSearchHook
virtual STDMETHODIMP Translate(LPWSTR lpwszSearchURL, DWORD cchBufferSize);
// *** IURLSearchHook2
virtual STDMETHODIMP TranslateWithSearchContext(LPWSTR lpwszSearchURL, DWORD cchBufferSize, ISearchContext * pSearchContext);
protected:
// IUnknown
UINT _cRef;
HRESULT _IsKeyWordSearch(LPCTSTR pcszURL);
HRESULT _IsURLSearchable(LPTSTR pszURL, HKEY * phkeySearch, LPCTSTR * pcszQuery);
HRESULT _ReplaceChars(HKEY hkeySearch, LPCTSTR pcszQuery, PTSTR pszReplaced, int cchReplaced);
HRESULT _Search(HKEY hkeySearch, LPCTSTR pcszQuery, PTSTR pszTranslatedURL, DWORD cchTranslatedUrl, PTSTR pszSearchUrl, ISearchContext * pSC);
void _ConvertToUtf8(LPWSTR pszQuery, int cch);
};
#ifdef DEBUG
#define _AddRef(psz) { ++_cRef; TraceMsg(TF_URLSEARCHHOOK, "CURLSearchHook(%x)::QI(%s) is AddRefing _cRef=%lX", this, psz, _cRef); }
#else
#define _AddRef(psz) ++_cRef
#endif
CURLSearchHook::CURLSearchHook()
: _cRef(1)
{
DllAddRef();
}
HRESULT CURLSearchHook::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
// ppvObj must not be NULL
ASSERT(ppvObj != NULL);
if (ppvObj == NULL)
return E_INVALIDARG;
*ppvObj = NULL;
if (IsEqualIID(riid, IID_IUnknown) ||
IsEqualIID(riid, IID_IURLSearchHook) ||
IsEqualIID(riid, IID_IURLSearchHook2))
{
*ppvObj = SAFECAST(this, IURLSearchHook2 *);
TraceMsg(TF_URLSEARCHHOOK, "QI IURLSEARCHHOOK succeeded");
}
else
return E_NOINTERFACE; // Otherwise, don't delegate to HTMLObj!!
_AddRef(TEXT("IURLSearchHook"));
return S_OK;
}
ULONG CURLSearchHook::AddRef()
{
_cRef++;
TraceMsg(TF_URLSEARCHHOOK, "CURLSearchHook(%x)::AddRef called, new _cRef=%lX", this, _cRef);
return _cRef;
}
ULONG CURLSearchHook::Release()
{
_cRef--;
TraceMsg(TF_URLSEARCHHOOK, "CURLSearchHook(%x)::Release called, new _cRef=%lX", this, _cRef);
if (_cRef > 0)
return _cRef;
delete this;
DllRelease();
return 0;
}
HRESULT CURLSearchHook::_IsKeyWordSearch(LPCTSTR pcszURL)
{
TCHAR szAcceptedRequestKey[256];
LPTSTR lpsz = szAcceptedRequestKey;
LPTSTR lpszKey = szAcceptedRequestKey;
// load the accepted request keywords and compare them with what the user typed in
MLLoadString(IDS_URL_SEARCH_KEY, szAcceptedRequestKey, ARRAYSIZE(szAcceptedRequestKey)-1);
int RequestKeyLen = 0;
while (*lpsz) {
if (*lpsz == TEXT(' ')){
if (! StrCmpNI(pcszURL, lpszKey, RequestKeyLen+1))
return S_OK;
else {
lpsz++;
lpszKey = lpsz;
RequestKeyLen = 0;
}
}
else {
lpsz++;
RequestKeyLen++;
}
}
return S_FALSE;
}
// This function determines if we will do an autosearch on the string user typed in
//
// Priorities:
// 1 --- Key word search: search with "go", "find" and so on
// 2 --- possible URL address: contains '.', ':', '/' and '\\', so don't search
// 3 --- Space triggered search.
// 4 --- Don't search.
HRESULT CURLSearchHook::_IsURLSearchable(LPTSTR pszURL, HKEY * phkeySearch, LPCTSTR * ppcszQuery)
{
BOOL fExtendedChar = FALSE;
TCHAR szRegSearchKey[MAX_PATH];
LPTSTR pszKey = StrChr(pszURL, TEXT(' '));
if (pszKey == NULL)
{
// No keyword, but if any of the characters are non-ascii, we will default
// to search because it's likely not a url
fExtendedChar = HasExtendedChar(pszURL);
if (!fExtendedChar)
return S_FALSE;
pszKey = pszURL;
}
StrCpyN(szRegSearchKey, c_szSearchUrl, ARRAYSIZE(szRegSearchKey));
if ((_IsKeyWordSearch(pszURL) == S_FALSE) && !fExtendedChar)
{
// Find the end of the default Registry Subkey and
// append the keyword so the regkey becomes:
// Software\Microsoft\Internet Explorer\SearchUrl\go
ASSERT((ARRAYSIZE(c_szSearchUrl) + 1) < ARRAYSIZE(szRegSearchKey));
PTSTR pszEnd = &szRegSearchKey[ARRAYSIZE(c_szSearchUrl) - 1];
*pszEnd++ = TEXT('\\');
const int cchBuf = ARRAYSIZE(szRegSearchKey) - (ARRAYSIZE(c_szSearchUrl) + 1);
const int cchToCopy = (int) (pszKey - pszURL + 1);
StrCpyN(pszEnd, pszURL, min(cchBuf, cchToCopy));
// See if this is a search keyword in the registry
if (OpenRegUSKey(szRegSearchKey, 0, KEY_READ, phkeySearch) == ERROR_SUCCESS)
{
PathRemoveBlanks(pszKey);
*ppcszQuery = pszKey;
return S_OK;
}
// No keyword so use entire "url" for the search
pszKey = pszURL;
if (StrCSpn(pszURL, TEXT(":/\\")) != lstrlen(pszURL))
{
return S_FALSE;
}
}
// Null out the key to signal that we should use the internal hard-coded search string
*phkeySearch = NULL;
PathRemoveBlanks(pszKey);
*ppcszQuery = pszKey;
return S_OK;
}
HRESULT CURLSearchHook::_ReplaceChars(HKEY hkeySearch, LPCTSTR pcszQuery, LPTSTR pszReplaced, int cchReplaced)
{
// The following are strings and its lengthes passed in RegEnumValue
TCHAR szOrig[2];
DWORD dwOrigLen;
TCHAR szMatch[10];
DWORD dwMatchLen;
HDSA hdsaReplace = NULL;
// If we are using our hard-coded search url, we get the char replacements from the string table
if (NULL == hkeySearch)
{
WCHAR szSub[MAX_PATH];
if (MLLoadString(IDS_SEARCH_SUBSTITUTIONS, szSub, ARRAYSIZE(szSub)) && *szSub != NULL)
{
// The first char is our deliminator followed by replacement pairs (", ,+,#,%23,&,%26,?,%3F,+,%2B,=,%3d")
WCHAR chDelim = szSub[0];
LPWSTR pszFrom = &szSub[1];
BOOL fDone = FALSE;
LPWSTR pszNext;
do
{
// Null terminater our source string
LPWSTR pszTo = StrChr(pszFrom, chDelim);
if (NULL == pszTo)
{
break;
}
*pszTo = L'\0';
// Null terminate the dest string
++pszTo;
LPWSTR pszToEnd = StrChr(pszTo, chDelim);
if (pszToEnd)
{
*pszToEnd = L'\0';
pszNext = pszToEnd + 1;
}
else
{
pszNext = NULL;
}
// If the "from" string is one char and the "to" substitution fits, store this pair
SUrlCharReplace scr;
if (pszFrom[1] == L'\0' && lstrlen(pszTo) < ARRAYSIZE(scr.to))
{
scr.from = pszFrom[0];
StrCpyN(scr.to, pszTo, ARRAYSIZE(scr.to));
if (!hdsaReplace)
hdsaReplace = DSA_Create(SIZEOF(SUrlCharReplace), 4);
if (hdsaReplace)
DSA_AppendItem(hdsaReplace, &scr);
}
pszFrom = pszNext;
}
while (pszNext != NULL);
}
}
// The search url is in the registry, so get the char substitutions from there
else
{
DWORD dwType;
LONG lRegEnumResult;
DWORD dwiValue = 0;
do
{
dwOrigLen = ARRAYSIZE(szOrig);
dwMatchLen = SIZEOF(szMatch);
lRegEnumResult = RegEnumValue(hkeySearch, dwiValue, szOrig,
&dwOrigLen, NULL, &dwType, (PBYTE)szMatch,
&dwMatchLen);
dwiValue++;
SUrlCharReplace scr;
if ((lRegEnumResult == ERROR_SUCCESS) && (dwType == REG_SZ) && (dwOrigLen == 1)
&& dwMatchLen < ARRAYSIZE(scr.to))
{
scr.from = szOrig[0];
StrCpyN(scr.to, szMatch, ARRAYSIZE(scr.to));
if (!hdsaReplace)
hdsaReplace = DSA_Create(SIZEOF(SUrlCharReplace), 4);
if (hdsaReplace)
DSA_AppendItem(hdsaReplace, &scr);
}
} while ((lRegEnumResult == ERROR_SUCCESS) || (lRegEnumResult == ERROR_MORE_DATA));
}
if (hdsaReplace)
{
// Replace all characters found in the registry by their matches in the search key word
LPTSTR lpHead = pszReplaced;
int cchHead = cchReplaced;
int ich;
int ihdsa;
BOOL bCharFound;
int querylen = lstrlen(pcszQuery);
for (ich = 0; ich < querylen && cchHead > 1; ich++)
{
bCharFound = FALSE;
// First look through the DSA array to find a match
for (ihdsa = 0; ihdsa < DSA_GetItemCount(hdsaReplace); ihdsa++)
{
SUrlCharReplace *pscr;
pscr = (SUrlCharReplace *)DSA_GetItemPtr(hdsaReplace, ihdsa);
if (pscr && pscr->from == pcszQuery[ich])
{
int szLen = lstrlen(pscr->to);
StrCpyN(lpHead, pscr->to, cchHead);
lpHead += szLen;
cchHead -= szLen;
bCharFound = TRUE;
break;
}
}
// Copy the character over if there is no replacements
if (!bCharFound)
{
*lpHead = pcszQuery[ich];
lpHead++;
cchHead--;
}
}
if (cchHead > 0)
*lpHead = 0;
DSA_Destroy(hdsaReplace);
hdsaReplace = NULL;
}
return S_OK;
}
void CURLSearchHook::_ConvertToUtf8(LPWSTR pszQuery, int cch)
{
// Only need to covert if extended characters found
if (HasExtendedChar(pszQuery))
{
ConvertToUtf8Escaped(pszQuery, cch);
}
}
// pszTranslatedUrl is the output of this function
HRESULT CURLSearchHook::_Search(HKEY hkeySearch, LPCTSTR pcszQuery, PTSTR pszTranslatedUrl, DWORD cchTranslatedUrl, PTSTR pszSearchUrl, ISearchContext * pSC)
{
HRESULT hr = E_FAIL;
// Get the search provider from the registry
DWORD dwType;
WCHAR szProvider[MAX_PATH];
szProvider[0] = 0;
DWORD cbProvider = sizeof(szProvider);
if (SHRegGetUSValue(c_szSearchUrl, L"Provider", &dwType, &szProvider, &cbProvider, FALSE, NULL, 0) != ERROR_SUCCESS ||
dwType != REG_SZ)
{
szProvider[0] = 0;
}
TCHAR szSearchPath[MAX_URL_STRING];
DWORD dwSearchPathLen = SIZEOF(szSearchPath);
BOOL fSuccess;
if (pszSearchUrl != NULL)
{
StrCpyNW(szSearchPath, pszSearchUrl, ARRAYSIZE(szSearchPath));
fSuccess = TRUE;
}
else
{
// Find the search URL in the registry or our string table
if (hkeySearch)
{
fSuccess = (RegQueryValueEx(hkeySearch, NULL, NULL, NULL, (PBYTE)szSearchPath, &dwSearchPathLen) == ERROR_SUCCESS);
}
else
{
// See if we want the hardcoded intranet or internet url
UINT ids = (StrCmpI(szProvider, L"Intranet") == 0) ? IDS_SEARCH_INTRANETURL : IDS_SEARCH_URL;
// Use our internal hard-coded string
fSuccess = MLLoadString(ids, szSearchPath, ARRAYSIZE(szSearchPath));
}
}
if (fSuccess && lstrlen(szSearchPath) > 1)
{
// 1. Look in the registry and find all of the original characters and it's
// matches and store them in the DSA arrays of SURlCharReplace
// 2. Replace all of the occurences of the original characters in the
// URL search key word by their matches.
// 3. Append the search URL and the search key words
TCHAR szURLReplaced[MAX_URL_STRING];
StrCpyN(szURLReplaced, pcszQuery, ARRAYSIZE(szURLReplaced));
_ReplaceChars(hkeySearch, pcszQuery, szURLReplaced, ARRAYSIZE(szURLReplaced));
//
// If we are using our search engine, convert the string to UTF8 and escape it
// so that it appears like normal ascii
//
if (NULL == hkeySearch)
{
_ConvertToUtf8(szURLReplaced, ARRAYSIZE(szURLReplaced));
}
// If this is an old-style url, there will be a %s in it for the search string.
// Otherwise there will be the following parameters:
//
// http://whatever.com?p=%1&srch=%2&prov=%3&utf8
//
// %1 = search string
// %2 = how to display results:
// "1" = just show me results in full window
// "2" = show results in full window, but redirect if possible
// "3" = show results in the search pane, and take me to the most
// likely site in the main window if there is one
// %3 = search provider name
//
LPWSTR pszParam1 = StrStr(szSearchPath, L"%1");
if (NULL != pszParam1)
{
//
// We can't use FormatMessage because on win95 it converts to ansi
// using the system code page and the translation back is lossy.
// So we'll replace the parameters ourselves. Arrrggg.
//
// First convert %1 to %s
pszParam1[1] = L's';
// Next replace %2 with the display option in %2
LPWSTR pszParam2 = StrStr(szSearchPath, L"%2");
if (NULL != pszParam2)
{
DWORD dwValue;
if (pSC != NULL)
{
hr = pSC->GetSearchStyle(&dwValue);
}
else
{
DWORD cbValue = sizeof(dwValue);
if (SHRegGetUSValue(REGSTR_PATH_MAIN, L"AutoSearch", &dwType, &dwValue, &cbValue, FALSE, NULL, 0) != ERROR_SUCCESS ||
dwValue > 9)
{
// Default to "display results in search pane and go to most likely site"
dwValue = 3;
}
}
*pszParam2 = (WCHAR)dwValue + L'0';
StrCpyN(pszParam2 + 1, pszParam2 + 2, (int)(ARRAYSIZE(szSearchPath) - ((pszParam2 + 1) - szSearchPath)));
}
// Finally, find the third Param and convert it to %s too
LPWSTR pszParam3 = StrStr(szSearchPath, L"%3");
if (pszParam3)
{
// Insert the provider in the third param
WCHAR szTemp[MAX_URL_STRING];
StrCpyN(szTemp, pszParam3 + 2, ARRAYSIZE(szTemp));
*pszParam3 = 0;
StrCatBuff(szSearchPath, szProvider, ARRAYSIZE(szSearchPath));
StrCatBuff(szSearchPath, szTemp, ARRAYSIZE(szSearchPath));
}
}
// Now replace the %s with the search string
wnsprintf(pszTranslatedUrl, cchTranslatedUrl, szSearchPath, szURLReplaced);
hr = S_OK;
}
if (hkeySearch)
RegCloseKey(hkeySearch);
return hr;
}
HRESULT CURLSearchHook::TranslateWithSearchContext(LPWSTR lpwszSearchURL, DWORD cchBufferSize, ISearchContext * pSC)
{
HRESULT hr = E_FAIL;
TCHAR szSearchURL[MAX_URL_STRING];
SHUnicodeToTChar(lpwszSearchURL, szSearchURL, ARRAYSIZE(szSearchURL));
HKEY hkeySearch;
LPCTSTR pcszQuery;
if (_IsURLSearchable(szSearchURL, &hkeySearch, &pcszQuery) == S_OK)
{
BSTR bstrSearchUrl = NULL;
if (pSC != NULL)
{
pSC->GetSearchUrl(&bstrSearchUrl);
}
hr = _Search(hkeySearch, pcszQuery, szSearchURL, ARRAYSIZE(szSearchURL), bstrSearchUrl, pSC);
if (hr == S_OK)
SHTCharToUnicode(szSearchURL, lpwszSearchURL, cchBufferSize);
if (bstrSearchUrl != NULL)
{
SysFreeString(bstrSearchUrl);
}
}
return hr;
}
HRESULT CURLSearchHook::Translate(LPWSTR lpwszSearchURL, DWORD cchBufferSize)
{
return TranslateWithSearchContext(lpwszSearchURL, cchBufferSize, NULL);
}
#ifdef DEBUG
extern void remove_from_memlist(void *pv);
#endif
STDAPI CURLSearchHook_CreateInstance(IUnknown* pUnkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
{
// aggregation checking is handled in class factory
HRESULT hr = E_OUTOFMEMORY;
CURLSearchHook *pcush = new CURLSearchHook;
if (pcush)
{
//
// HACK:(dli)
//
// IURLSearchHook objects are free-threaded objects, meaning that
// they are cacheed and shared between different IEXPLORE processes,
// and they are only deleted when the SHDOCVW DLL ref count is 0.
// So, we can remove them from the SATOSHI's memlist.
//
// By the way, SATOSHI has Okayed this. Don't copy this code without
// talking to SATOSHI.
//
*ppunk = (IUnknown *) pcush;
hr = S_OK;
}
return hr;
}