567 lines
18 KiB
C++
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;
|
|
}
|
|
|