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.

469 lines
14 KiB

  1. /*
  2. * url.cpp - IUniformResourceLocator implementation for InternetShortcut class.
  3. */
  4. /* Headers
  5. **********/
  6. #include "priv.h"
  7. #pragma hdrstop
  8. #define INC_OLE2
  9. #include "intshcut.h"
  10. /* Module Constants
  11. *******************/
  12. const TCHAR c_szURLPrefixesKey[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\URL\\Prefixes");
  13. const TCHAR c_szDefaultURLPrefixKey[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\URL\\DefaultPrefix");
  14. // DPA array that holds the IURLSearchHook Pointers
  15. static HDPA g_hdpaHooks = NULL;
  16. // CURRENT_USER
  17. static const TCHAR c_szURLSearchHook[] = TSZIEPATH TEXT("\\URLSearchHooks");
  18. /***************************** Private Functions *****************************/
  19. int DPA_DestroyURLSearchHooksCallback(LPVOID p, LPVOID d)
  20. {
  21. IURLSearchHook * psuh = (IURLSearchHook *)p;
  22. ASSERT(psuh);
  23. ATOMICRELEASET(psuh, IURLSearchHook);
  24. return 1;
  25. }
  26. extern "C" {
  27. void DestroyHdpaHooks()
  28. {
  29. if (g_hdpaHooks)
  30. {
  31. ENTERCRITICAL;
  32. //---------------------------- Critical Section -------------------------
  33. HDPA hdpa = g_hdpaHooks;
  34. g_hdpaHooks = NULL;
  35. //-----------------------------------------------------------------------
  36. LEAVECRITICAL;
  37. if (hdpa)
  38. {
  39. DPA_DestroyCallback(hdpa, DPA_DestroyURLSearchHooksCallback, 0);
  40. hdpa = NULL;
  41. }
  42. }
  43. }
  44. }
  45. HRESULT InvokeURLSearchHook(IURLSearchHook * pusHook, LPCTSTR pcszQuery, LPTSTR pszResult, ISearchContext * pSC)
  46. {
  47. HRESULT hr = E_FAIL;
  48. ASSERT(pusHook);
  49. WCHAR szSearchURL[MAX_URL_STRING];
  50. SHTCharToUnicode(pcszQuery, szSearchURL, ARRAYSIZE(szSearchURL));
  51. // if we can get an IURLSearchHook2, we'll pass in the
  52. // search context, otherwise we'll just do without
  53. IURLSearchHook2 * pUSH2 = NULL;
  54. hr = pusHook->QueryInterface(IID_IURLSearchHook2, (void **)&pUSH2);
  55. if (SUCCEEDED(hr))
  56. {
  57. RIP(pUSH2 != NULL);
  58. hr = pUSH2->TranslateWithSearchContext(szSearchURL, ARRAYSIZE(szSearchURL), pSC);
  59. pUSH2->Release();
  60. }
  61. else
  62. {
  63. hr = pusHook->Translate(szSearchURL, ARRAYSIZE(szSearchURL));
  64. }
  65. // In case the URLSearchHook worked, convert result to TCHAR
  66. // This includes two cases: S_OK and S_FALSE
  67. if (SUCCEEDED(hr))
  68. {
  69. //WARNING: (dli) Assuming pszResult size = MAX_URL_STRING
  70. SHUnicodeToTChar(szSearchURL, pszResult, MAX_URL_STRING);
  71. }
  72. return hr;
  73. }
  74. /*
  75. * Returns:
  76. * S_OK Search handled completely, pszResult has the full URL to browse to.
  77. * 0x00000000 Stop running any further IURLSearchHooks and pass this URL back to
  78. * the browser for browsing.
  79. *
  80. * S_FALSE Query has been preprocessed, pszResult has the result of the preprocess,
  81. * 0x00000001 further search still needed. Go on executing the rest of the IURLSearchHooks
  82. * The preprocessing steps can be: 1. replaced certain characters
  83. * 2. added more hints
  84. *
  85. * E_ABORT Search handled completely, stop running any further IURLSearchHooks,
  86. * 0x80004004 but NO BROWSING NEEDED as a result, pszResult is a copy of pcszQuery.
  87. * FEATURE: This is not fully implemented, yet, making IURLQualify return this
  88. * involves too much change.
  89. *
  90. * E_FAIL This Hook was unsuccessful. Search not handled at all, pcszQueryURL has the
  91. * 0x80004005 query string. Please go on running other IURLSearchHooks.
  92. * return
  93. */
  94. HRESULT TryURLSearchHooks(LPCTSTR pcszQuery, LPTSTR pszResult, ISearchContext * pSC)
  95. {
  96. HRESULT hr = E_FAIL;
  97. TCHAR szNewQuery[MAX_URL_STRING];
  98. StrCpyN(szNewQuery, pcszQuery, ARRAYSIZE(szNewQuery));
  99. int ihdpa;
  100. for (ihdpa = 0; ihdpa < (g_hdpaHooks ? DPA_GetPtrCount(g_hdpaHooks) : 0); ihdpa++)
  101. {
  102. IURLSearchHook * pusHook;
  103. pusHook = (IURLSearchHook *) DPA_GetPtr(g_hdpaHooks, ihdpa);
  104. if (!pusHook)
  105. return E_FAIL;
  106. hr = InvokeURLSearchHook(pusHook, szNewQuery, pszResult, pSC);
  107. if ((hr == S_OK) || (hr == E_ABORT))
  108. break;
  109. else if (hr == S_FALSE)
  110. StrCpyN(szNewQuery, pszResult, ARRAYSIZE(szNewQuery));
  111. }
  112. return hr;
  113. }
  114. void InitURLSearchHooks()
  115. {
  116. HDPA hdpa = DPA_Create(4);
  117. // We need to look in LOCAL_MACHINE if this registry entry doesn't exist in CURRENT_USER.
  118. // The installer needs to install the values into LOCAL_MACHINE so they are accessable
  119. // to all users. Then anyone wanting to modify the value, will need to determine if they
  120. // want to add it to a specific user's CURRENT_USER or modify the LOCAL_MACHINE value to
  121. // apply the change to all users. (bryanst - #6722)
  122. HUSKEY hkeyHooks;
  123. if ((hdpa) && (SHRegOpenUSKey(c_szURLSearchHook, KEY_READ, NULL, &hkeyHooks, FALSE) == ERROR_SUCCESS))
  124. {
  125. TCHAR szCLSID[GUIDSTR_MAX];
  126. DWORD dwccCLSIDLen;
  127. LONG lEnumReturn;
  128. DWORD dwiValue = 0;
  129. do {
  130. dwccCLSIDLen = ARRAYSIZE(szCLSID);
  131. lEnumReturn = SHRegEnumUSValue(hkeyHooks, dwiValue, szCLSID, &dwccCLSIDLen,
  132. NULL, NULL, NULL, SHREGENUM_DEFAULT);
  133. if (lEnumReturn == ERROR_SUCCESS)
  134. {
  135. CLSID clsidHook;
  136. if (SUCCEEDED(SHCLSIDFromString(szCLSID, &clsidHook)))
  137. {
  138. IURLSearchHook * pusHook;
  139. HRESULT hr = CoCreateInstance(clsidHook, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
  140. IID_IURLSearchHook, (LPVOID *)&pusHook);
  141. if (SUCCEEDED(hr))
  142. DPA_AppendPtr(hdpa, pusHook);
  143. }
  144. }
  145. dwiValue++;
  146. } while (lEnumReturn == ERROR_SUCCESS);
  147. SHRegCloseUSKey(hkeyHooks);
  148. }
  149. ENTERCRITICAL;
  150. //---------------------------- Critical Section --------------------------
  151. if (!g_hdpaHooks)
  152. {
  153. g_hdpaHooks = hdpa;
  154. hdpa = NULL;
  155. }
  156. //------------------------------------------------------------------------
  157. LEAVECRITICAL;
  158. if (hdpa)
  159. {
  160. DPA_DestroyCallback(hdpa, DPA_DestroyURLSearchHooksCallback, 0);
  161. hdpa = NULL;
  162. }
  163. }
  164. HRESULT ApplyURLSearch(LPCTSTR pcszQuery, LPTSTR pszTranslatedUrl, ISearchContext * pSC)
  165. {
  166. if (!g_hdpaHooks)
  167. InitURLSearchHooks();
  168. return TryURLSearchHooks(pcszQuery, pszTranslatedUrl, pSC);
  169. }
  170. /*----------------------------------------------------------
  171. Purpose: This function qualifies a string as a URL. Strings
  172. such as "www.foo.com" would have the scheme guessed
  173. if the correct flags are given. Local paths are
  174. converted to "file:" URLs.
  175. pszTranslatedURL may point to the same buffer as
  176. pcszURL.
  177. If the given string is already a URL (not necessarily
  178. canonicalized, though), this function will not touch it,
  179. unless UQF_CANONICALIZE is set, in which case the string
  180. will be canonicalized.
  181. Returns: S_OK or S_FALSE means we filled in pszTranslatedURL.
  182. S_OK means we altered the URL to qualify it too.
  183. various failure codes too
  184. Cond: --
  185. */
  186. SHDOCAPI
  187. IURLQualifyWithContext(
  188. IN LPCWSTR pcszURL,
  189. IN DWORD dwFlags, // UQF_*
  190. OUT LPWSTR pszTranslatedURL,
  191. LPBOOL pbWasSearchURL,
  192. LPBOOL pbWasCorrected,
  193. ISearchContext * pSC)
  194. {
  195. HRESULT hres = S_FALSE;
  196. DWORD cchSize;
  197. SHSTR strOut;
  198. BOOL bWasCorrected = FALSE;
  199. ASSERT(IS_VALID_STRING_PTR(pcszURL, -1));
  200. ASSERT(IS_VALID_WRITE_BUFFER(pszTranslatedURL, TCHAR, MAX_URL_STRING));
  201. if (pbWasSearchURL)
  202. *pbWasSearchURL = FALSE;
  203. // Special cases: URLs of the form <drive>:<filename>
  204. // URLs of the form \<filename>
  205. // we'll assume that if the second character is a : or |, this is an url of
  206. // that form, and we will guess "file://" for the prefix.
  207. // we'll assume any url that begins with a single \ is a file: url
  208. // NOTE: We do this here because these are cases where the protocol is
  209. // left off, and is likely to be incorrectly guessed, such as a
  210. // relative path \data\ftp\docs, would wrongly be turned
  211. // into "ftp://\data\ftp\docs".
  212. // Note: PathIsURL returns TRUE for non-canonicalized URLs too
  213. if (PathIsURL(pcszURL))
  214. {
  215. LPCWSTR pcszTemp = pcszURL;
  216. cchSize = MAX_URL_STRING;
  217. if (IsFlagSet(dwFlags, UQF_AUTOCORRECT))
  218. {
  219. hres = UrlFixup(pcszURL, pszTranslatedURL, cchSize);
  220. if (hres == S_OK)
  221. {
  222. bWasCorrected = TRUE;
  223. pcszTemp = pszTranslatedURL;
  224. }
  225. }
  226. if (dwFlags & UQF_CANONICALIZE)
  227. hres = UrlCanonicalize(pcszTemp, pszTranslatedURL, &cchSize, 0);
  228. else if (pszTranslatedURL != pcszTemp)
  229. StrCpyN(pszTranslatedURL, pcszTemp, MAX_URL_STRING);
  230. hres = S_OK;
  231. }
  232. else
  233. {
  234. // Look for file paths
  235. if (IsFlagClear(dwFlags, UQF_IGNORE_FILEPATHS) && (
  236. #ifdef UNIX
  237. pcszURL[0] == TEXT('/') ||
  238. #endif
  239. pcszURL[1] == TEXT(':') || pcszURL[1] == TEXT('|') || pcszURL[0] == TEXT('\\')))
  240. {
  241. hres = strOut.SetSize(MAX_PATH);
  242. if(SUCCEEDED(hres))
  243. {
  244. // SHSTRs have a size granularity, so the size
  245. // will be equal to or greater than what was set.
  246. // this means we need to get it our self.
  247. DWORD cchOut = strOut.GetSize();
  248. TCHAR szCurrentDir[MAX_PATH];
  249. //
  250. // APPCOMPAT - IE30 compatibility - zekel 8-Jan-97
  251. // we need to GetCurrentDirectory() in order to
  252. // put a default drive letter on the path
  253. // if necessary.
  254. //
  255. if(GetCurrentDirectory(ARRAYSIZE(szCurrentDir), szCurrentDir))
  256. PathCombine(strOut.GetInplaceStr(), szCurrentDir, pcszURL);
  257. else
  258. hres = strOut.SetStr(pcszURL);
  259. if(SUCCEEDED(hres))
  260. {
  261. hres = UrlCreateFromPath(strOut, strOut.GetInplaceStr(), &cchOut, 0);
  262. if (E_POINTER == hres && SUCCEEDED(hres = strOut.SetSize(cchOut)))
  263. {
  264. cchOut = strOut.GetSize();
  265. hres = UrlCreateFromPath(strOut, strOut.GetInplaceStr(), &cchOut, 0);
  266. }
  267. }
  268. }
  269. }
  270. else if (SUCCEEDED(hres = strOut.SetSize(MAX_URL_STRING)))
  271. {
  272. // all the Apply*() below rely on MAX_URL_STRING
  273. // No; begin processing general-case URLs. Try to guess the
  274. // protocol or resort to the default protocol.
  275. DWORD cchOut = strOut.GetSize();
  276. if (IsFlagSet(dwFlags, UQF_GUESS_PROTOCOL))
  277. hres = UrlApplyScheme(pcszURL, strOut.GetInplaceStr(), &cchOut, URL_APPLY_GUESSSCHEME);
  278. //
  279. // Try to auto-correct the protocol
  280. //
  281. if (hres == S_FALSE &&
  282. IsFlagSet(dwFlags, UQF_AUTOCORRECT))
  283. {
  284. hres = UrlFixup(pcszURL, strOut.GetInplaceStr(), strOut.GetSize());
  285. bWasCorrected = (hres == S_OK);
  286. }
  287. if (hres == S_FALSE &&
  288. IsFlagSet(dwFlags, UQF_USE_DEFAULT_PROTOCOL))
  289. {
  290. // run the search with or without the search context
  291. hres = ApplyURLSearch(pcszURL, strOut.GetInplaceStr(), pSC);
  292. if (SUCCEEDED(hres) && pbWasSearchURL) {
  293. *pbWasSearchURL = TRUE;
  294. }
  295. // If that fails, then tack on the default protocol
  296. if (FAILED(hres) || hres == S_FALSE)
  297. {
  298. cchOut = strOut.GetSize();
  299. hres = UrlApplyScheme(pcszURL, strOut.GetInplaceStr(), &cchOut, URL_APPLY_DEFAULT);
  300. }
  301. }
  302. // Did the above fail?
  303. if (S_FALSE == hres)
  304. {
  305. // Yes; return the real reason why the URL is bad
  306. hres = URL_E_INVALID_SYNTAX;
  307. }
  308. else if (dwFlags & UQF_CANONICALIZE)
  309. {
  310. // No; canonicalize
  311. cchSize = strOut.GetSize();
  312. hres = UrlCanonicalize(strOut, strOut.GetInplaceStr(), &cchSize, 0);
  313. }
  314. }
  315. if (SUCCEEDED(hres))
  316. {
  317. StrCpyN(pszTranslatedURL, strOut, MAX_URL_STRING);
  318. }
  319. }
  320. if (pbWasCorrected)
  321. *pbWasCorrected = bWasCorrected;
  322. return hres;
  323. }
  324. SHDOCAPI
  325. IURLQualify(
  326. IN LPCWSTR pcszURL,
  327. IN DWORD dwFlags, // UQF_*
  328. OUT LPWSTR pszTranslatedURL,
  329. LPBOOL pbWasSearchURL,
  330. LPBOOL pbWasCorrected)
  331. {
  332. return IURLQualifyWithContext(pcszURL, dwFlags, pszTranslatedURL, pbWasSearchURL, pbWasCorrected, NULL);
  333. }
  334. /***************************** Exported Functions ****************************/
  335. STDAPI
  336. URLQualifyA(
  337. LPCSTR pszURL,
  338. DWORD dwFlags, // UQF_*
  339. LPSTR *ppszOut)
  340. {
  341. HRESULT hres;
  342. ASSERT(IS_VALID_STRING_PTRA(pszURL, -1));
  343. ASSERT(IS_VALID_WRITE_PTR(ppszOut, LPSTR));
  344. *ppszOut = NULL;
  345. WCHAR szTempTranslatedURL[MAX_URL_STRING];
  346. WCHAR szURL[MAX_URL_STRING];
  347. SHAnsiToUnicode(pszURL, szURL, ARRAYSIZE(szURL));
  348. hres = IURLQualify(szURL, dwFlags, szTempTranslatedURL, NULL, NULL);
  349. if (SUCCEEDED(hres))
  350. {
  351. CHAR szOut[MAX_URL_STRING];
  352. SHUnicodeToAnsi(szTempTranslatedURL, szOut, ARRAYSIZE(szOut));
  353. *ppszOut = StrDupA(szOut);
  354. if (!*ppszOut)
  355. hres = E_OUTOFMEMORY;
  356. }
  357. return hres;
  358. }
  359. STDAPI
  360. URLQualifyW(
  361. LPCWSTR pszURL,
  362. DWORD dwFlags, // UQF_*
  363. LPWSTR *ppszOut)
  364. {
  365. HRESULT hres;
  366. ASSERT(IS_VALID_STRING_PTRW(pszURL, -1));
  367. ASSERT(IS_VALID_WRITE_PTR(ppszOut, LPWSTR));
  368. WCHAR szTempTranslatedURL[MAX_URL_STRING];
  369. hres = IURLQualify(pszURL, dwFlags, szTempTranslatedURL, NULL, NULL);
  370. if (SUCCEEDED(hres))
  371. {
  372. *ppszOut = StrDup(szTempTranslatedURL);
  373. if (!*ppszOut)
  374. hres = E_OUTOFMEMORY;
  375. }
  376. return hres;
  377. }