Leaked source code of windows server 2003
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.

495 lines
17 KiB

  1. #include "priv.h"
  2. #include "dochost.h"
  3. #include "resource.h"
  4. #include "urlprop.h"
  5. #include "ishcut.h"
  6. #include "shlguid.h"
  7. #include "mlang.h"
  8. #include <mluisupp.h>
  9. #define DM_HISTORY 0
  10. HRESULT PersistShortcut(IUniformResourceLocator * purl, LPCWSTR pwszFile)
  11. {
  12. IPersistFile *ppf;
  13. HRESULT hres = purl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
  14. if (SUCCEEDED(hres))
  15. {
  16. hres = ppf->Save(pwszFile, TRUE);
  17. if (SUCCEEDED(hres))
  18. ppf->SaveCompleted(pwszFile); // return value always S_OK
  19. ppf->Release();
  20. }
  21. return hres;
  22. }
  23. /*************************************************************\
  24. FUNCTION: GenerateUnknownShortcutName
  25. PARAMETERS:
  26. pwzSourceFilename - TCHAR Source Path and Filename that cannot be created.
  27. This value will be changed to a valid path\filename
  28. pwzDestFilename - After pwzSourceFilename is converted to a valid filename,
  29. the valid path will be returned here in a UNICODE string.
  30. dwSize - Size of the pwzDestFilename buffer in chars.
  31. DESCRIPTION:
  32. This function will replace the filename at the end of
  33. the path in pwzFilename with "Untitled.url". If that file
  34. exists, it will try, "Untitled1.url" and so on until it can
  35. be unique.
  36. WARNING:
  37. This function will only allow the incoming value be in ANSI
  38. because these helper functions (like PathRemoveFileSpecW) won't
  39. work on Win95 when compiled in UNICODE. (CharNextW isn't supported
  40. on Win95)
  41. \*************************************************************/
  42. #define MAX_GEN_TRIES 100
  43. #define GEN_EXTION_LEN (7 * sizeof(TCHAR)) // size == L"000.url" in chars
  44. BOOL GenerateUnknownShortcutName(
  45. IN LPCTSTR pszSourceFilename,
  46. IN LPWSTR pwzDestFilename,
  47. IN DWORD dwSize)
  48. {
  49. TCHAR szUntitledStr[MAX_PATH];
  50. if (MLLoadString(IDS_UNTITLE_SHORTCUT, szUntitledStr, ARRAYSIZE(szUntitledStr)))
  51. {
  52. TCHAR szTempFilename[MAX_PATH];
  53. StrCpyN(szTempFilename, pszSourceFilename, ARRAYSIZE(szTempFilename));
  54. PathRemoveFileSpec(szTempFilename); // "Path"
  55. if (PathAddBackslash(szTempFilename)) // returns NULL in failure case
  56. {
  57. // Make sure the string is large enough (including terminator). (Counting chars, not bytes)
  58. if (dwSize > (DWORD)(lstrlen(szTempFilename) + lstrlen(szUntitledStr) + GEN_EXTION_LEN))
  59. {
  60. PathCombine(szUntitledStr, szTempFilename, szUntitledStr); // "Path\untitled"
  61. StringCchPrintf(szTempFilename, ARRAYSIZE(szTempFilename), TEXT("%s.url"), szUntitledStr); // "Path\untitled.url"
  62. // Make a reasonable number of tries (MAX_GEN_TRIES) to find a unique
  63. // filename. "path\Untitled.url", "path\Untitled1.url", ...
  64. LONG lTry = 1;
  65. while ((PathFileExists(szTempFilename)) && (lTry < MAX_GEN_TRIES))
  66. StringCchPrintf(szTempFilename, ARRAYSIZE(szTempFilename), TEXT("%s%ld.url"), szUntitledStr, lTry++);
  67. if (!PathFileExists(szTempFilename))
  68. {
  69. if (SHTCharToUnicode(szTempFilename, pwzDestFilename, dwSize) > 0)
  70. return(TRUE);
  71. }
  72. }
  73. }
  74. }
  75. return(FALSE);
  76. }
  77. //
  78. // If no directory is specified then it is simply made into a path name without any dir attached
  79. //
  80. STDAPI_(BOOL) GetShortcutFileName(LPCTSTR pszTarget, LPCTSTR pszTitle, LPCTSTR pszDir, LPTSTR pszOut, int cchOut)
  81. {
  82. TCHAR szFullName[MAX_PATH];
  83. BOOL fAddDotUrl = TRUE;
  84. UINT cchMax;
  85. static const TCHAR c_szDotURL[] = TEXT(".url");
  86. TraceMsg(DM_HISTORY, "GetShortcutFileName pszDir = %s", pszDir);
  87. cchMax = ARRAYSIZE(szFullName) - lstrlen(c_szDotURL);
  88. if (pszTitle && pszTitle[0])
  89. StrCpyN(szFullName, pszTitle, cchMax);
  90. else if (pszTarget && pszTarget[0])
  91. {
  92. StrCpyN(szFullName, PathFindFileName(pszTarget), cchMax);
  93. UINT cchLen = lstrlen(szFullName);
  94. if (szFullName[cchLen -1] == TEXT('/')) // Catch the common case of ftp://foo/
  95. szFullName[cchLen -1] = TEXT('\0');
  96. PathRemoveExtension(szFullName);
  97. }
  98. else
  99. {
  100. fAddDotUrl = FALSE;
  101. MLLoadString(IDS_NEW_INTSHCUT, szFullName, SIZECHARS(szFullName));
  102. }
  103. // We need at least this many characters for the directory + extension + " (nn)" + the null terminator
  104. // If there are multiple shortcuts with the same beginning, we'll append " (nn)", where
  105. // nn represents a two-digit maxiumum
  106. DWORD cc = (DWORD)(lstrlen(pszDir) + (fAddDotUrl ? ARRAYSIZE(c_szDotURL) : 1) + 5);
  107. // We want to allow for at least a one letter filename
  108. if ((cc + 1) > ARRAYSIZE(szFullName))
  109. {
  110. return FALSE;
  111. }
  112. szFullName[ARRAYSIZE(szFullName)-cc] = TEXT('\0');
  113. if (fAddDotUrl)
  114. StrCatBuff(szFullName, c_szDotURL, ARRAYSIZE(szFullName));
  115. if (pszDir)
  116. {
  117. if (cchOut < MAX_PATH || (PathCleanupSpec(pszDir, szFullName) & PCS_FATAL))
  118. {
  119. return FALSE;
  120. }
  121. PathCombine(pszOut, pszDir, szFullName);
  122. }
  123. else
  124. {
  125. StrCpyN(pszOut, szFullName, cchOut);
  126. }
  127. TraceMsg(DM_HISTORY, "GetShortcutFileName pszOut = %s", pszOut);
  128. return TRUE;
  129. }
  130. // Unfortunately we do not already have something like this around
  131. // If you find duplicate, please nuke this (dli)
  132. // Warning: This function does not consider all possible URL cases.
  133. BOOL _GetPrettyURLName(LPCTSTR pcszURL, LPCTSTR pcszDir, LPTSTR pszUrlFile, int cchUrlFile)
  134. {
  135. BOOL bRet = FALSE;
  136. PARSEDURL pu = {0};
  137. pu.cbSize = sizeof(PARSEDURL);
  138. if (SUCCEEDED(ParseURL(pcszURL, &pu)))
  139. {
  140. LPCTSTR pszPrettyName = pu.pszSuffix;
  141. // Get rid of the forward '/'
  142. while (*pszPrettyName && *pszPrettyName == TEXT('/'))
  143. pszPrettyName++;
  144. if (!StrCmpN(pszPrettyName, TEXT("www."), 4))
  145. pszPrettyName += 4;
  146. if (*pszPrettyName)
  147. bRet = GetShortcutFileName(pcszURL, pszPrettyName, pcszDir, pszUrlFile, cchUrlFile);
  148. }
  149. return bRet;
  150. }
  151. /*
  152. * pcszURL -> "ftp://ftp.microsoft.com"
  153. * pcszPath -> "c:\windows\desktop\internet\Microsoft FTP.url"
  154. */
  155. HRESULT
  156. CreateNewURLShortcut(
  157. IN LPCTSTR pcszURL,
  158. IN LPCITEMIDLIST pidlURL,
  159. IN LPCTSTR pcszURLFile,
  160. IN LPCTSTR pcszDir,
  161. OUT LPTSTR pszOut,
  162. IN int cchOut,
  163. IN BOOL bUpdateProperties,
  164. IN BOOL bUpdateIcon,
  165. IN IOleCommandTarget *pCommandTarget)
  166. {
  167. HRESULT hr;
  168. WCHAR wszFile[MAX_URL_STRING];
  169. if (SHTCharToUnicode(pcszURLFile, wszFile, ARRAYSIZE(wszFile)))
  170. {
  171. IUniformResourceLocator *purl;
  172. hr = CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER,
  173. IID_PPV_ARG(IUniformResourceLocator, &purl));
  174. if (SUCCEEDED(hr))
  175. {
  176. if (pidlURL)
  177. {
  178. // if we're given a pidl, try to set pidl first.
  179. IShellLink *psl;
  180. hr = purl->QueryInterface(IID_PPV_ARG(IShellLink, &psl));
  181. if (SUCCEEDED(hr))
  182. {
  183. hr = psl->SetIDList(pidlURL);
  184. psl->Release();
  185. }
  186. }
  187. if (!pidlURL || FAILED(hr))
  188. hr = purl->SetURL(pcszURL, 0);
  189. if (S_OK == hr)
  190. IUnknown_SetSite(purl, pCommandTarget);
  191. if (SUCCEEDED(hr))
  192. {
  193. // Persist the internet shortcut
  194. hr = PersistShortcut(purl, wszFile);
  195. // If the previous call fails, try again with a new Filename.
  196. // This is needed because the other filename could have been invalid,
  197. // which will happen if the web page's title was stored in DBCS with
  198. // a non-English code page.
  199. // (dli) First try a file name related to the URL, then the default untitled
  200. if (FAILED(hr))
  201. {
  202. TCHAR tszFile[MAX_PATH];
  203. BOOL bURLname = _GetPrettyURLName(pcszURL, pcszDir, tszFile, ARRAYSIZE(tszFile));
  204. if ((bURLname && SHTCharToUnicode(tszFile, wszFile, ARRAYSIZE(wszFile)) > 0) ||
  205. (!bURLname && GenerateUnknownShortcutName(pcszURLFile, wszFile, ARRAYSIZE(wszFile))))
  206. {
  207. hr = PersistShortcut(purl, wszFile);
  208. }
  209. }
  210. if (SUCCEEDED(hr))
  211. {
  212. VARIANT varIn = {0};
  213. if (bUpdateIcon)
  214. {
  215. HRESULT hrTemp = IUnknown_Exec(purl, &CGID_ShortCut, ISHCUTCMDID_DOWNLOADICON, 0, NULL, NULL);
  216. ASSERT(SUCCEEDED(hrTemp));
  217. }
  218. varIn.vt = VT_UNKNOWN;
  219. varIn.punkVal = purl;
  220. TCHAR szFile[MAX_PATH];
  221. SHUnicodeToTChar(wszFile, szFile, ARRAYSIZE(szFile));
  222. SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szFile, NULL);
  223. if (pszOut)
  224. {
  225. StrCpyN(pszOut, wszFile, cchOut);
  226. }
  227. }
  228. }
  229. purl->Release();
  230. }
  231. }
  232. else
  233. hr = E_FAIL;
  234. return(hr);
  235. }
  236. BOOL ILCanCreateLNK(LPCITEMIDLIST pidl)
  237. {
  238. HRESULT hr = S_FALSE;
  239. DWORD dwAttributes = SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR;
  240. // Should call IsBrowserFrameOptionsPidlSet(BIF_PREFER_INTERNET_SHORTCUT) instead. Some URL delegate
  241. // NSEs (FTP for one) may want .lnks instead of .url files.
  242. // This would be great for CDocObjFolder to not set this bit so
  243. // for .doc files so they will use the .lnk versions.
  244. if (!pidl || IsURLChild(pidl, TRUE))
  245. return FALSE;
  246. hr = IEGetAttributesOf(pidl, &dwAttributes);
  247. return (SUCCEEDED(hr) &&
  248. (IsFlagSet(dwAttributes, SFGAO_FOLDER) ||
  249. IsFlagSet(dwAttributes, SFGAO_FILESYSANCESTOR) )
  250. );
  251. }
  252. // This API makes a callback via the IN parameter
  253. // pCommand to inform that shortcut creation is over.
  254. // The callback it currently sends back are :
  255. //
  256. STDAPI
  257. CreateShortcutInDirEx(ISHCUT_PARAMS *pParams)
  258. {
  259. LPCITEMIDLIST pidlTarget = pParams->pidlTarget;
  260. TCHAR szFileName[MAX_PATH];
  261. TCHAR szTarget[MAX_URL_STRING];
  262. HRESULT hres;
  263. BOOL bIsURL = IsURLChild(pidlTarget, TRUE);
  264. if (!ILCanCreateLNK(pidlTarget) &&
  265. SUCCEEDED(IEGetDisplayName(pidlTarget, szTarget, SHGDN_FORPARSING)) &&
  266. _ValidateURL(szTarget, UQF_DEFAULT))
  267. {
  268. SHCleanupUrlForDisplay(szTarget);
  269. BOOL bUsePidl;
  270. // Note that _ValidateURL() calls IURLQualify() which adds "file://"
  271. // prefix to szTarget as appropriate.
  272. if (bIsURL ||
  273. (GetUrlScheme(szTarget) == URL_SCHEME_FILE))
  274. {
  275. bUsePidl = FALSE;
  276. }
  277. else
  278. {
  279. // use pidl if it's not URL or file: compatible.
  280. bUsePidl = TRUE;
  281. }
  282. GetShortcutFileName(szTarget, pParams->pszTitle, pParams->pszDir, szFileName, ARRAYSIZE(szFileName));
  283. if (pParams->bUniqueName)
  284. PathYetAnotherMakeUniqueName(szFileName, szFileName, NULL, NULL);
  285. hres = CreateNewURLShortcut(szTarget, bUsePidl ? pidlTarget : NULL, szFileName, pParams->pszDir,
  286. pParams->pszOut, pParams->cchOut, pParams->bUpdateProperties,
  287. pParams->bUpdateIcon, pParams->pCommand);
  288. }
  289. else
  290. {
  291. hres = CreateLinkToPidl(pidlTarget, pParams->pszDir, pParams->pszTitle, pParams->pszOut, pParams->cchOut);
  292. }
  293. return hres;
  294. }
  295. // pidlTarget ... the thing the shortcut is going to point to
  296. // pszDir .. the directory that should hold the shortcut
  297. // WARNING: if you change any parameters for this function, you
  298. // need to fix up explorer.exe
  299. STDAPI CreateShortcutInDirA(
  300. IN LPCITEMIDLIST pidlTarget,
  301. IN LPSTR pszTitle,
  302. IN LPCSTR pszDir,
  303. OUT LPSTR pszOut,
  304. IN BOOL bUpdateProperties)
  305. {
  306. HRESULT hres = E_FAIL;
  307. TCHAR szTitle[MAX_PATH];
  308. TCHAR szDir[MAX_PATH];
  309. TCHAR szOut[MAX_URL_STRING];
  310. ISHCUT_PARAMS ShCutParams = {0};
  311. SHAnsiToTChar(pszTitle, szTitle, ARRAYSIZE(szTitle));
  312. SHAnsiToTChar(pszDir, szDir, ARRAYSIZE(szDir));
  313. ShCutParams.pidlTarget = pidlTarget;
  314. ShCutParams.pszTitle = szTitle;
  315. ShCutParams.pszDir = szDir;
  316. ShCutParams.pszOut = (pszOut ? szOut : NULL);
  317. ShCutParams.cchOut = (int)((pszOut ? ARRAYSIZE(szOut) : 0));
  318. ShCutParams.bUpdateProperties = bUpdateProperties;
  319. ShCutParams.bUniqueName = FALSE;
  320. ShCutParams.bUpdateIcon = FALSE;
  321. ShCutParams.pCommand = NULL;
  322. ShCutParams.pDoc = NULL;
  323. hres = CreateShortcutInDirEx(&ShCutParams);
  324. if (pszOut && SUCCEEDED(hres))
  325. SHTCharToAnsi(szOut, pszOut, MAX_URL_STRING);
  326. return hres;
  327. }
  328. STDAPI CreateShortcutInDirW(
  329. IN LPCITEMIDLIST pidlTarget,
  330. IN LPWSTR pwszTitle,
  331. IN LPCWSTR pwszDir,
  332. OUT LPWSTR pwszOut,
  333. IN BOOL bUpdateProperties)
  334. {
  335. HRESULT hres = E_FAIL;
  336. TCHAR szTitle[MAX_PATH];
  337. TCHAR szDir[MAX_PATH];
  338. TCHAR szOut[MAX_URL_STRING];
  339. ISHCUT_PARAMS ShCutParams = {0};
  340. SHUnicodeToTChar(pwszTitle, szTitle, ARRAYSIZE(szTitle));
  341. SHUnicodeToTChar(pwszDir, szDir, ARRAYSIZE(szDir));
  342. ShCutParams.pidlTarget = pidlTarget;
  343. ShCutParams.pszTitle = szTitle;
  344. ShCutParams.pszDir = szDir;
  345. ShCutParams.pszOut = (pwszOut ? szOut : NULL);
  346. ShCutParams.cchOut = (int)((pwszOut ? ARRAYSIZE(szOut) : 0));
  347. ShCutParams.bUpdateProperties = bUpdateProperties;
  348. ShCutParams.bUniqueName = FALSE;
  349. ShCutParams.bUpdateIcon = FALSE;
  350. ShCutParams.pCommand = NULL;
  351. ShCutParams.pDoc = NULL;
  352. hres = CreateShortcutInDirEx(&ShCutParams);
  353. if (pwszOut && SUCCEEDED(hres))
  354. SHTCharToUnicode(szOut, pwszOut, MAX_URL_STRING);
  355. return hres;
  356. }
  357. //////////////////////////////
  358. //
  359. // Adds the given URL to the history storage
  360. //
  361. // pwzTitle may be NULL if no title exists
  362. //
  363. // Note this function may be called multiple times in a single
  364. // page-visit. bUpdateProperties is TRUE only once during
  365. // those sequence of calls.
  366. //
  367. HRESULT
  368. AddUrlToUrlHistoryStg(
  369. IN LPCWSTR pwszUrl,
  370. IN LPCWSTR pwszTitle,
  371. IN LPUNKNOWN punk,
  372. IN BOOL fWriteHistory,
  373. IN IOleCommandTarget *poctNotify,
  374. IN IUnknown *punkSFHistory,
  375. OUT UINT* pcodepage)
  376. {
  377. TraceMsg(DM_HISTORY, "AddUrlToUrlHistoryStg() entered url = %s, title = %s, punk = %X, fwrite = %d, poct = %X, punkHist = %X, cp = %d",
  378. pwszUrl, pwszTitle, punk,fWriteHistory,poctNotify,punkSFHistory,pcodepage);
  379. IUrlHistoryPriv *pUrlHistStg;
  380. HRESULT hr;
  381. if (!pwszUrl)
  382. return E_POINTER;
  383. if (punk == NULL)
  384. {
  385. hr = CoCreateInstance(CLSID_CUrlHistory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUrlHistoryPriv, &pUrlHistStg));
  386. }
  387. else
  388. {
  389. // query the pointer for IServiceProvider so we can get the IUrlHistoryStg
  390. hr = IUnknown_QueryService(punk, SID_SUrlHistory, IID_PPV_ARG(IUrlHistoryPriv, &pUrlHistStg));
  391. }
  392. if (SUCCEEDED(hr))
  393. {
  394. //
  395. // This demostrate the mechanism to get the codepage for URL.
  396. //
  397. hr = pUrlHistStg->AddUrlAndNotifyCP(pwszUrl,
  398. pwszTitle,
  399. 0,
  400. fWriteHistory,
  401. poctNotify,
  402. punkSFHistory,
  403. pcodepage);
  404. pUrlHistStg->Release();
  405. }
  406. return hr;
  407. }