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.

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