Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

566 lines
18 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation
  4. //
  5. // File: urlhook.cpp
  6. //
  7. // History:
  8. // 9-24-96 by dli
  9. //------------------------------------------------------------------------
  10. #include "priv.h"
  11. #include "sccls.h"
  12. #include "resource.h"
  13. #include <mluisupp.h>
  14. // CURRENT_USER
  15. static const TCHAR c_szSearchUrl[] = TSZIEPATH TEXT("\\SearchUrl");
  16. #define TF_URLSEARCHHOOK 0
  17. // structure for the character replacement in URL searches
  18. typedef struct _SUrlCharReplace {
  19. TCHAR from;
  20. TCHAR to[10];
  21. } SUrlCharReplace;
  22. class CURLSearchHook : public IURLSearchHook2
  23. {
  24. public:
  25. CURLSearchHook();
  26. // *** IUnknown Methods
  27. virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
  28. virtual STDMETHODIMP_(ULONG) AddRef(void) ;
  29. virtual STDMETHODIMP_(ULONG) Release(void);
  30. // *** IURLSearchHook
  31. virtual STDMETHODIMP Translate(LPWSTR lpwszSearchURL, DWORD cchBufferSize);
  32. // *** IURLSearchHook2
  33. virtual STDMETHODIMP TranslateWithSearchContext(LPWSTR lpwszSearchURL, DWORD cchBufferSize, ISearchContext * pSearchContext);
  34. protected:
  35. // IUnknown
  36. UINT _cRef;
  37. HRESULT _IsKeyWordSearch(LPCTSTR pcszURL);
  38. HRESULT _IsURLSearchable(LPTSTR pszURL, HKEY * phkeySearch, LPCTSTR * pcszQuery);
  39. HRESULT _ReplaceChars(HKEY hkeySearch, LPCTSTR pcszQuery, PTSTR pszReplaced, int cchReplaced);
  40. HRESULT _Search(HKEY hkeySearch, LPCTSTR pcszQuery, PTSTR pszTranslatedURL, DWORD cchTranslatedUrl, PTSTR pszSearchUrl, ISearchContext * pSC);
  41. void _ConvertToUtf8(LPWSTR pszQuery, int cch);
  42. };
  43. #ifdef DEBUG
  44. #define _AddRef(psz) { ++_cRef; TraceMsg(TF_URLSEARCHHOOK, "CURLSearchHook(%x)::QI(%s) is AddRefing _cRef=%lX", this, psz, _cRef); }
  45. #else
  46. #define _AddRef(psz) ++_cRef
  47. #endif
  48. CURLSearchHook::CURLSearchHook()
  49. : _cRef(1)
  50. {
  51. DllAddRef();
  52. }
  53. HRESULT CURLSearchHook::QueryInterface(REFIID riid, LPVOID * ppvObj)
  54. {
  55. // ppvObj must not be NULL
  56. ASSERT(ppvObj != NULL);
  57. if (ppvObj == NULL)
  58. return E_INVALIDARG;
  59. *ppvObj = NULL;
  60. if (IsEqualIID(riid, IID_IUnknown) ||
  61. IsEqualIID(riid, IID_IURLSearchHook) ||
  62. IsEqualIID(riid, IID_IURLSearchHook2))
  63. {
  64. *ppvObj = SAFECAST(this, IURLSearchHook2 *);
  65. TraceMsg(TF_URLSEARCHHOOK, "QI IURLSEARCHHOOK succeeded");
  66. }
  67. else
  68. return E_NOINTERFACE; // Otherwise, don't delegate to HTMLObj!!
  69. _AddRef(TEXT("IURLSearchHook"));
  70. return S_OK;
  71. }
  72. ULONG CURLSearchHook::AddRef()
  73. {
  74. _cRef++;
  75. TraceMsg(TF_URLSEARCHHOOK, "CURLSearchHook(%x)::AddRef called, new _cRef=%lX", this, _cRef);
  76. return _cRef;
  77. }
  78. ULONG CURLSearchHook::Release()
  79. {
  80. _cRef--;
  81. TraceMsg(TF_URLSEARCHHOOK, "CURLSearchHook(%x)::Release called, new _cRef=%lX", this, _cRef);
  82. if (_cRef > 0)
  83. return _cRef;
  84. delete this;
  85. DllRelease();
  86. return 0;
  87. }
  88. HRESULT CURLSearchHook::_IsKeyWordSearch(LPCTSTR pcszURL)
  89. {
  90. TCHAR szAcceptedRequestKey[256];
  91. LPTSTR lpsz = szAcceptedRequestKey;
  92. LPTSTR lpszKey = szAcceptedRequestKey;
  93. // load the accepted request keywords and compare them with what the user typed in
  94. MLLoadString(IDS_URL_SEARCH_KEY, szAcceptedRequestKey, ARRAYSIZE(szAcceptedRequestKey)-1);
  95. int RequestKeyLen = 0;
  96. while (*lpsz) {
  97. if (*lpsz == TEXT(' ')){
  98. if (! StrCmpNI(pcszURL, lpszKey, RequestKeyLen+1))
  99. return S_OK;
  100. else {
  101. lpsz++;
  102. lpszKey = lpsz;
  103. RequestKeyLen = 0;
  104. }
  105. }
  106. else {
  107. lpsz++;
  108. RequestKeyLen++;
  109. }
  110. }
  111. return S_FALSE;
  112. }
  113. // This function determines if we will do an autosearch on the string user typed in
  114. //
  115. // Priorities:
  116. // 1 --- Key word search: search with "go", "find" and so on
  117. // 2 --- possible URL address: contains '.', ':', '/' and '\\', so don't search
  118. // 3 --- Space triggered search.
  119. // 4 --- Don't search.
  120. HRESULT CURLSearchHook::_IsURLSearchable(LPTSTR pszURL, HKEY * phkeySearch, LPCTSTR * ppcszQuery)
  121. {
  122. BOOL fExtendedChar = FALSE;
  123. TCHAR szRegSearchKey[MAX_PATH];
  124. LPTSTR pszKey = StrChr(pszURL, TEXT(' '));
  125. if (pszKey == NULL)
  126. {
  127. // No keyword, but if any of the characters are non-ascii, we will default
  128. // to search because it's likely not a url
  129. fExtendedChar = HasExtendedChar(pszURL);
  130. if (!fExtendedChar)
  131. return S_FALSE;
  132. pszKey = pszURL;
  133. }
  134. StrCpyN(szRegSearchKey, c_szSearchUrl, ARRAYSIZE(szRegSearchKey));
  135. if ((_IsKeyWordSearch(pszURL) == S_FALSE) && !fExtendedChar)
  136. {
  137. // Find the end of the default Registry Subkey and
  138. // append the keyword so the regkey becomes:
  139. // Software\Microsoft\Internet Explorer\SearchUrl\go
  140. ASSERT((ARRAYSIZE(c_szSearchUrl) + 1) < ARRAYSIZE(szRegSearchKey));
  141. PTSTR pszEnd = &szRegSearchKey[ARRAYSIZE(c_szSearchUrl) - 1];
  142. *pszEnd++ = TEXT('\\');
  143. const int cchBuf = ARRAYSIZE(szRegSearchKey) - (ARRAYSIZE(c_szSearchUrl) + 1);
  144. const int cchToCopy = (int) (pszKey - pszURL + 1);
  145. StrCpyN(pszEnd, pszURL, min(cchBuf, cchToCopy));
  146. // See if this is a search keyword in the registry
  147. if (OpenRegUSKey(szRegSearchKey, 0, KEY_READ, phkeySearch) == ERROR_SUCCESS)
  148. {
  149. PathRemoveBlanks(pszKey);
  150. *ppcszQuery = pszKey;
  151. return S_OK;
  152. }
  153. // No keyword so use entire "url" for the search
  154. pszKey = pszURL;
  155. if (StrCSpn(pszURL, TEXT(":/\\")) != lstrlen(pszURL))
  156. {
  157. return S_FALSE;
  158. }
  159. }
  160. // Null out the key to signal that we should use the internal hard-coded search string
  161. *phkeySearch = NULL;
  162. PathRemoveBlanks(pszKey);
  163. *ppcszQuery = pszKey;
  164. return S_OK;
  165. }
  166. HRESULT CURLSearchHook::_ReplaceChars(HKEY hkeySearch, LPCTSTR pcszQuery, LPTSTR pszReplaced, int cchReplaced)
  167. {
  168. // The following are strings and its lengthes passed in RegEnumValue
  169. TCHAR szOrig[2];
  170. DWORD dwOrigLen;
  171. TCHAR szMatch[10];
  172. DWORD dwMatchLen;
  173. HDSA hdsaReplace = NULL;
  174. // If we are using our hard-coded search url, we get the char replacements from the string table
  175. if (NULL == hkeySearch)
  176. {
  177. WCHAR szSub[MAX_PATH];
  178. if (MLLoadString(IDS_SEARCH_SUBSTITUTIONS, szSub, ARRAYSIZE(szSub)) && *szSub != NULL)
  179. {
  180. // The first char is our deliminator followed by replacement pairs (", ,+,#,%23,&,%26,?,%3F,+,%2B,=,%3d")
  181. WCHAR chDelim = szSub[0];
  182. LPWSTR pszFrom = &szSub[1];
  183. BOOL fDone = FALSE;
  184. LPWSTR pszNext;
  185. do
  186. {
  187. // Null terminater our source string
  188. LPWSTR pszTo = StrChr(pszFrom, chDelim);
  189. if (NULL == pszTo)
  190. {
  191. break;
  192. }
  193. *pszTo = L'\0';
  194. // Null terminate the dest string
  195. ++pszTo;
  196. LPWSTR pszToEnd = StrChr(pszTo, chDelim);
  197. if (pszToEnd)
  198. {
  199. *pszToEnd = L'\0';
  200. pszNext = pszToEnd + 1;
  201. }
  202. else
  203. {
  204. pszNext = NULL;
  205. }
  206. // If the "from" string is one char and the "to" substitution fits, store this pair
  207. SUrlCharReplace scr;
  208. if (pszFrom[1] == L'\0' && lstrlen(pszTo) < ARRAYSIZE(scr.to))
  209. {
  210. scr.from = pszFrom[0];
  211. StrCpyN(scr.to, pszTo, ARRAYSIZE(scr.to));
  212. if (!hdsaReplace)
  213. hdsaReplace = DSA_Create(SIZEOF(SUrlCharReplace), 4);
  214. if (hdsaReplace)
  215. DSA_AppendItem(hdsaReplace, &scr);
  216. }
  217. pszFrom = pszNext;
  218. }
  219. while (pszNext != NULL);
  220. }
  221. }
  222. // The search url is in the registry, so get the char substitutions from there
  223. else
  224. {
  225. DWORD dwType;
  226. LONG lRegEnumResult;
  227. DWORD dwiValue = 0;
  228. do
  229. {
  230. dwOrigLen = ARRAYSIZE(szOrig);
  231. dwMatchLen = SIZEOF(szMatch);
  232. lRegEnumResult = RegEnumValue(hkeySearch, dwiValue, szOrig,
  233. &dwOrigLen, NULL, &dwType, (PBYTE)szMatch,
  234. &dwMatchLen);
  235. dwiValue++;
  236. SUrlCharReplace scr;
  237. if ((lRegEnumResult == ERROR_SUCCESS) && (dwType == REG_SZ) && (dwOrigLen == 1)
  238. && dwMatchLen < ARRAYSIZE(scr.to))
  239. {
  240. scr.from = szOrig[0];
  241. StrCpyN(scr.to, szMatch, ARRAYSIZE(scr.to));
  242. if (!hdsaReplace)
  243. hdsaReplace = DSA_Create(SIZEOF(SUrlCharReplace), 4);
  244. if (hdsaReplace)
  245. DSA_AppendItem(hdsaReplace, &scr);
  246. }
  247. } while ((lRegEnumResult == ERROR_SUCCESS) || (lRegEnumResult == ERROR_MORE_DATA));
  248. }
  249. if (hdsaReplace)
  250. {
  251. // Replace all characters found in the registry by their matches in the search key word
  252. LPTSTR lpHead = pszReplaced;
  253. int cchHead = cchReplaced;
  254. int ich;
  255. int ihdsa;
  256. BOOL bCharFound;
  257. int querylen = lstrlen(pcszQuery);
  258. for (ich = 0; ich < querylen && cchHead > 1; ich++)
  259. {
  260. bCharFound = FALSE;
  261. // First look through the DSA array to find a match
  262. for (ihdsa = 0; ihdsa < DSA_GetItemCount(hdsaReplace); ihdsa++)
  263. {
  264. SUrlCharReplace *pscr;
  265. pscr = (SUrlCharReplace *)DSA_GetItemPtr(hdsaReplace, ihdsa);
  266. if (pscr && pscr->from == pcszQuery[ich])
  267. {
  268. int szLen = lstrlen(pscr->to);
  269. StrCpyN(lpHead, pscr->to, cchHead);
  270. lpHead += szLen;
  271. cchHead -= szLen;
  272. bCharFound = TRUE;
  273. break;
  274. }
  275. }
  276. // Copy the character over if there is no replacements
  277. if (!bCharFound)
  278. {
  279. *lpHead = pcszQuery[ich];
  280. lpHead++;
  281. cchHead--;
  282. }
  283. }
  284. if (cchHead > 0)
  285. *lpHead = 0;
  286. DSA_Destroy(hdsaReplace);
  287. hdsaReplace = NULL;
  288. }
  289. return S_OK;
  290. }
  291. void CURLSearchHook::_ConvertToUtf8(LPWSTR pszQuery, int cch)
  292. {
  293. // Only need to covert if extended characters found
  294. if (HasExtendedChar(pszQuery))
  295. {
  296. ConvertToUtf8Escaped(pszQuery, cch);
  297. }
  298. }
  299. // pszTranslatedUrl is the output of this function
  300. HRESULT CURLSearchHook::_Search(HKEY hkeySearch, LPCTSTR pcszQuery, PTSTR pszTranslatedUrl, DWORD cchTranslatedUrl, PTSTR pszSearchUrl, ISearchContext * pSC)
  301. {
  302. HRESULT hr = E_FAIL;
  303. // Get the search provider from the registry
  304. DWORD dwType;
  305. WCHAR szProvider[MAX_PATH];
  306. szProvider[0] = 0;
  307. DWORD cbProvider = sizeof(szProvider);
  308. if (SHRegGetUSValue(c_szSearchUrl, L"Provider", &dwType, &szProvider, &cbProvider, FALSE, NULL, 0) != ERROR_SUCCESS ||
  309. dwType != REG_SZ)
  310. {
  311. szProvider[0] = 0;
  312. }
  313. TCHAR szSearchPath[MAX_URL_STRING];
  314. DWORD dwSearchPathLen = SIZEOF(szSearchPath);
  315. BOOL fSuccess;
  316. if (pszSearchUrl != NULL)
  317. {
  318. StrCpyNW(szSearchPath, pszSearchUrl, ARRAYSIZE(szSearchPath));
  319. fSuccess = TRUE;
  320. }
  321. else
  322. {
  323. // Find the search URL in the registry or our string table
  324. if (hkeySearch)
  325. {
  326. fSuccess = (RegQueryValueEx(hkeySearch, NULL, NULL, NULL, (PBYTE)szSearchPath, &dwSearchPathLen) == ERROR_SUCCESS);
  327. }
  328. else
  329. {
  330. // See if we want the hardcoded intranet or internet url
  331. UINT ids = (StrCmpI(szProvider, L"Intranet") == 0) ? IDS_SEARCH_INTRANETURL : IDS_SEARCH_URL;
  332. // Use our internal hard-coded string
  333. fSuccess = MLLoadString(ids, szSearchPath, ARRAYSIZE(szSearchPath));
  334. }
  335. }
  336. if (fSuccess && lstrlen(szSearchPath) > 1)
  337. {
  338. // 1. Look in the registry and find all of the original characters and it's
  339. // matches and store them in the DSA arrays of SURlCharReplace
  340. // 2. Replace all of the occurences of the original characters in the
  341. // URL search key word by their matches.
  342. // 3. Append the search URL and the search key words
  343. TCHAR szURLReplaced[MAX_URL_STRING];
  344. StrCpyN(szURLReplaced, pcszQuery, ARRAYSIZE(szURLReplaced));
  345. _ReplaceChars(hkeySearch, pcszQuery, szURLReplaced, ARRAYSIZE(szURLReplaced));
  346. //
  347. // If we are using our search engine, convert the string to UTF8 and escape it
  348. // so that it appears like normal ascii
  349. //
  350. if (NULL == hkeySearch)
  351. {
  352. _ConvertToUtf8(szURLReplaced, ARRAYSIZE(szURLReplaced));
  353. }
  354. // If this is an old-style url, there will be a %s in it for the search string.
  355. // Otherwise there will be the following parameters:
  356. //
  357. // http://whatever.com?p=%1&srch=%2&prov=%3&utf8
  358. //
  359. // %1 = search string
  360. // %2 = how to display results:
  361. // "1" = just show me results in full window
  362. // "2" = show results in full window, but redirect if possible
  363. // "3" = show results in the search pane, and take me to the most
  364. // likely site in the main window if there is one
  365. // %3 = search provider name
  366. //
  367. LPWSTR pszParam1 = StrStr(szSearchPath, L"%1");
  368. if (NULL != pszParam1)
  369. {
  370. //
  371. // We can't use FormatMessage because on win95 it converts to ansi
  372. // using the system code page and the translation back is lossy.
  373. // So we'll replace the parameters ourselves. Arrrggg.
  374. //
  375. // First convert %1 to %s
  376. pszParam1[1] = L's';
  377. // Next replace %2 with the display option in %2
  378. LPWSTR pszParam2 = StrStr(szSearchPath, L"%2");
  379. if (NULL != pszParam2)
  380. {
  381. DWORD dwValue;
  382. if (pSC != NULL)
  383. {
  384. hr = pSC->GetSearchStyle(&dwValue);
  385. }
  386. else
  387. {
  388. DWORD cbValue = sizeof(dwValue);
  389. if (SHRegGetUSValue(REGSTR_PATH_MAIN, L"AutoSearch", &dwType, &dwValue, &cbValue, FALSE, NULL, 0) != ERROR_SUCCESS ||
  390. dwValue > 9)
  391. {
  392. // Default to "display results in search pane and go to most likely site"
  393. dwValue = 3;
  394. }
  395. }
  396. *pszParam2 = (WCHAR)dwValue + L'0';
  397. StrCpyN(pszParam2 + 1, pszParam2 + 2, (int)(ARRAYSIZE(szSearchPath) - ((pszParam2 + 1) - szSearchPath)));
  398. }
  399. // Finally, find the third Param and convert it to %s too
  400. LPWSTR pszParam3 = StrStr(szSearchPath, L"%3");
  401. if (pszParam3)
  402. {
  403. // Insert the provider in the third param
  404. WCHAR szTemp[MAX_URL_STRING];
  405. StrCpyN(szTemp, pszParam3 + 2, ARRAYSIZE(szTemp));
  406. *pszParam3 = 0;
  407. StrCatBuff(szSearchPath, szProvider, ARRAYSIZE(szSearchPath));
  408. StrCatBuff(szSearchPath, szTemp, ARRAYSIZE(szSearchPath));
  409. }
  410. }
  411. // Now replace the %s with the search string
  412. wnsprintf(pszTranslatedUrl, cchTranslatedUrl, szSearchPath, szURLReplaced);
  413. hr = S_OK;
  414. }
  415. if (hkeySearch)
  416. RegCloseKey(hkeySearch);
  417. return hr;
  418. }
  419. HRESULT CURLSearchHook::TranslateWithSearchContext(LPWSTR lpwszSearchURL, DWORD cchBufferSize, ISearchContext * pSC)
  420. {
  421. HRESULT hr = E_FAIL;
  422. TCHAR szSearchURL[MAX_URL_STRING];
  423. SHUnicodeToTChar(lpwszSearchURL, szSearchURL, ARRAYSIZE(szSearchURL));
  424. HKEY hkeySearch;
  425. LPCTSTR pcszQuery;
  426. if (_IsURLSearchable(szSearchURL, &hkeySearch, &pcszQuery) == S_OK)
  427. {
  428. BSTR bstrSearchUrl = NULL;
  429. if (pSC != NULL)
  430. {
  431. pSC->GetSearchUrl(&bstrSearchUrl);
  432. }
  433. hr = _Search(hkeySearch, pcszQuery, szSearchURL, ARRAYSIZE(szSearchURL), bstrSearchUrl, pSC);
  434. if (hr == S_OK)
  435. SHTCharToUnicode(szSearchURL, lpwszSearchURL, cchBufferSize);
  436. if (bstrSearchUrl != NULL)
  437. {
  438. SysFreeString(bstrSearchUrl);
  439. }
  440. }
  441. return hr;
  442. }
  443. HRESULT CURLSearchHook::Translate(LPWSTR lpwszSearchURL, DWORD cchBufferSize)
  444. {
  445. return TranslateWithSearchContext(lpwszSearchURL, cchBufferSize, NULL);
  446. }
  447. #ifdef DEBUG
  448. extern void remove_from_memlist(void *pv);
  449. #endif
  450. STDAPI CURLSearchHook_CreateInstance(IUnknown* pUnkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
  451. {
  452. // aggregation checking is handled in class factory
  453. HRESULT hr = E_OUTOFMEMORY;
  454. CURLSearchHook *pcush = new CURLSearchHook;
  455. if (pcush)
  456. {
  457. //
  458. // HACK:(dli)
  459. //
  460. // IURLSearchHook objects are free-threaded objects, meaning that
  461. // they are cacheed and shared between different IEXPLORE processes,
  462. // and they are only deleted when the SHDOCVW DLL ref count is 0.
  463. // So, we can remove them from the SATOSHI's memlist.
  464. //
  465. // By the way, SATOSHI has Okayed this. Don't copy this code without
  466. // talking to SATOSHI.
  467. //
  468. *ppunk = (IUnknown *) pcush;
  469. hr = S_OK;
  470. }
  471. return hr;
  472. }