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.

2921 lines
91 KiB

  1. /*****************************************************************************\
  2. ftppidl.cpp - Pointers to Item ID Lists
  3. This is the only file that knows the internal format of our IDLs.
  4. \*****************************************************************************/
  5. #include "priv.h"
  6. #include "ftppidl.h"
  7. #include "ftpurl.h"
  8. #include "cookie.h"
  9. #define NOT_INITIALIZED 10
  10. DWORD g_fNoPasswordsInAddressBar = NOT_INITIALIZED;
  11. #define SESSIONKEY FILETIME
  12. // Private FtpServerID Helpers
  13. HRESULT FtpServerID_GetServer(LPCITEMIDLIST pidl, LPTSTR szServer, DWORD cchSize);
  14. DWORD FtpItemID_GetTypeID(LPCITEMIDLIST pidl);
  15. // v0 never went to customers but was used in NT5 before 1799 - Shipped in: Never.
  16. // v1 This switch was to use password cookies for a security fix. - Shipped in: Never.
  17. // v2 this was done to not use the IDelegate's IMalloc for non-first ItemIDs - Shipped in: Never (5/15/98)
  18. // v3 add extra padding to ItemIDs so their dwType matches that of ServerIDs - Shipped in: IE5b1, IE5b2, NT5b2 (5/25/98)
  19. // v4 add wzDisplayName to FtpItemID - Shipped in: IE5 RTM & NT5 b3 (11/16/98)
  20. #define PIDL_VERSION_NUMBER_UPGRADE 3
  21. #define PIDL_VERSION_NUMBER 4
  22. #define SIZE_ITEMID_SIZEFIELD (sizeof(DWORD) + sizeof(WORD))
  23. #define SIZE_ITEMID_TERMINATOR (sizeof(DWORD))
  24. /****************************************************\
  25. IDType
  26. DESCRIPTION:
  27. These bits go into FTPIDLIST.dwIDType and describe
  28. what type of pidl it is AND which areas of the
  29. data structure have been verified by getting the
  30. data directly from the server.
  31. \****************************************************/
  32. #define IDTYPE_ISVALID 0x00000001 // Set if TYPE is valid
  33. #define IDTYPE_SERVER (0x00000002 | IDTYPE_ISVALID) // Server
  34. #define IDTYPE_DIR (0x00000004 | IDTYPE_ISVALID) // Folder/Dir
  35. #define IDTYPE_FILE (0x00000008 | IDTYPE_ISVALID) // File
  36. #define IDTYPE_FILEORDIR (0x00000010 | IDTYPE_ISVALID) // File or Dir. Wasn't specified.
  37. #define IDTYPE_FRAGMENT (0x00000020 | IDTYPE_ISVALID) // File Fragment (i.e. foobar.htm#SECTION_3)
  38. // These are bits that indicate
  39. // For Server ItemIDs
  40. #define IDVALID_PORT_NUM 0x00000100 // Was the port number specified
  41. #define IDVALID_USERNAME 0x00000200 // Was the login name specified
  42. #define IDVALID_PASSWORD 0x00000400 // Was the password specified
  43. #define IDVALID_DLTYPE 0x00000800 // Download Type is specified.
  44. #define IDVALID_DL_ASCII 0x00001000 // Download as ASCII if set, otherwise, download as BINARY.
  45. #define IDVALID_HIDE_PASSWORD 0x00002000 // The Password entry is invalid so use the sessionkey to look it up.
  46. #define VALID_SERVER_BITS (IDTYPE_ISVALID|IDTYPE_SERVER|IDVALID_PORT_NUM|IDVALID_USERNAME|IDVALID_PASSWORD|IDVALID_DLTYPE|IDVALID_DL_ASCII|IDVALID_HIDE_PASSWORD)
  47. #define IS_VALID_SERVER_ITEMID(pItemId) (!(pItemId & ~VALID_SERVER_BITS))
  48. // For Dir/File ItemIDs
  49. #define IDVALID_FILESIZE 0x00010000 // Did we get the file size from the server?
  50. #define IDVALID_MOD_DATE 0x00020000 // Did we get the modification date from the server?
  51. #define VALID_DIRORFILE_BITS (IDTYPE_ISVALID|IDTYPE_DIR|IDTYPE_FILE|IDTYPE_FILEORDIR|IDTYPE_FRAGMENT|IDVALID_FILESIZE|IDVALID_MOD_DATE)
  52. #define IS_VALID_DIRORFILE_ITEMID(pItemId) (!(pItemId & (~VALID_DIRORFILE_BITS & ~IDTYPE_ISVALID)))
  53. #define IS_FRAGMENT(pFtpIDList) (IDTYPE_ISVALID != (IDTYPE_FRAGMENT & pFtpIDList->dwIDType))
  54. ///////////////////////////////////////////////////////////
  55. // FTP Pidl Helper Functions
  56. ///////////////////////////////////////////////////////////
  57. /*****************************************************************************\
  58. FUNCTION: UrlGetAbstractPathFromPidl
  59. DESCRIPTION:
  60. pszUrlPath will be UNEscaped and in Wire Bytes.
  61. \*****************************************************************************/
  62. HRESULT UrlGetAbstractPathFromPidl(LPCITEMIDLIST pidl, BOOL fDirsOnly, BOOL fInWireBytes, void * pvPath, DWORD cchUrlPathSize)
  63. {
  64. HRESULT hr = S_OK;
  65. LPWIRESTR pwWirePath = (LPWIRESTR) pvPath;
  66. LPWSTR pwzDisplayPath = (LPWSTR) pvPath;
  67. if (!EVAL(FtpPidl_IsValid(pidl)))
  68. return E_INVALIDARG;
  69. ASSERT(pvPath && (0 < cchUrlPathSize));
  70. ASSERT(IsValidPIDL(pidl));
  71. if (fInWireBytes)
  72. {
  73. pwWirePath[0] = '/';
  74. pwWirePath[1] = '\0'; // Make this path absolute.
  75. }
  76. else
  77. {
  78. pwzDisplayPath[0] = L'/';
  79. pwzDisplayPath[1] = L'\0'; // Make this path absolute.
  80. }
  81. if (!ILIsEmpty(pidl) && FtpID_IsServerItemID(pidl)) // If it's not a server, we are in trouble.
  82. pidl = _ILNext(pidl); // Skip past the Server Pidl.
  83. for (; !ILIsEmpty(pidl); pidl = _ILNext(pidl))
  84. {
  85. if (!fDirsOnly || FtpItemID_IsDirectory(pidl, TRUE) || !ILIsEmpty(_ILNext(pidl)))
  86. {
  87. if (!FtpItemID_IsFragment(pidl))
  88. {
  89. if (fInWireBytes)
  90. {
  91. LPCWIRESTR pwWireName = FtpItemID_GetWireNameReference(pidl);
  92. if (pwWireName)
  93. {
  94. // The caller should never need the URL Path escaped because
  95. // that will happen when it's converted into an URL.
  96. WirePathAppend(pwWirePath, cchUrlPathSize, pwWireName);
  97. }
  98. }
  99. else
  100. {
  101. WCHAR szDisplayName[MAX_PATH];
  102. if (SUCCEEDED(FtpItemID_GetDisplayName(pidl, szDisplayName, ARRAYSIZE(szDisplayName))))
  103. {
  104. // The caller should never need the URL Path escaped because
  105. // that will happen when it's converted into an URL.
  106. DisplayPathAppend(pwzDisplayPath, cchUrlPathSize, szDisplayName);
  107. }
  108. }
  109. }
  110. }
  111. if (SUCCEEDED(hr) &&
  112. (FtpItemID_IsDirectory(pidl, FALSE) || (FtpItemID_GetCompatFlags(pidl) & COMPAT_APPENDSLASHTOURL)))
  113. {
  114. if (fInWireBytes)
  115. WirePathAppendSlash(pwWirePath, cchUrlPathSize); // Always make sure dirs end in '/'.
  116. else
  117. DisplayPathAppendSlash(pwzDisplayPath, cchUrlPathSize); // Always make sure dirs end in '/'.
  118. }
  119. }
  120. return hr;
  121. }
  122. /*****************************************************************************\
  123. FUNCTION: GetDisplayPathFromPidl
  124. DESCRIPTION:
  125. pwzDisplayPath will be UNEscaped and in display unicode.
  126. \*****************************************************************************/
  127. HRESULT GetDisplayPathFromPidl(LPCITEMIDLIST pidl, LPWSTR pwzDisplayPath, DWORD cchUrlPathSize, BOOL fDirsOnly)
  128. {
  129. return UrlGetAbstractPathFromPidl(pidl, fDirsOnly, FALSE, (void *) pwzDisplayPath, cchUrlPathSize);
  130. }
  131. /*****************************************************************************\
  132. FUNCTION: GetWirePathFromPidl
  133. DESCRIPTION:
  134. pszUrlPath will be UNEscaped and in Wire Bytes.
  135. \*****************************************************************************/
  136. HRESULT GetWirePathFromPidl(LPCITEMIDLIST pidl, LPWIRESTR pwWirePath, DWORD cchUrlPathSize, BOOL fDirsOnly)
  137. {
  138. return UrlGetAbstractPathFromPidl(pidl, fDirsOnly, TRUE, (void *) pwWirePath, cchUrlPathSize);
  139. }
  140. #ifndef UNICODE
  141. /*****************************************************************************\
  142. FUNCTION: UrlCreateFromPidlW
  143. DESCRIPTION:
  144. \*****************************************************************************/
  145. HRESULT UrlCreateFromPidlW(LPCITEMIDLIST pidl, DWORD shgno, LPWSTR pwzUrl, DWORD cchSize, DWORD dwFlags, BOOL fHidePassword)
  146. {
  147. HRESULT hr;
  148. TCHAR szUrl[MAX_URL_STRING];
  149. hr = UrlCreateFromPidl(pidl, shgno, szUrl, ARRAYSIZE(szUrl), dwFlags, fHidePassword);
  150. if (SUCCEEDED(hr))
  151. SHTCharToUnicode(szUrl, pwzUrl, cchSize);
  152. return hr;
  153. }
  154. #else // UNICODE
  155. /*****************************************************************************\
  156. FUNCTION: UrlCreateFromPidlA
  157. DESCRIPTION:
  158. \*****************************************************************************/
  159. HRESULT UrlCreateFromPidlA(LPCITEMIDLIST pidl, DWORD shgno, LPSTR pszUrl, DWORD cchSize, DWORD dwFlags, BOOL fHidePassword)
  160. {
  161. HRESULT hr;
  162. TCHAR szUrl[MAX_URL_STRING];
  163. hr = UrlCreateFromPidl(pidl, shgno, szUrl, ARRAYSIZE(szUrl), dwFlags, fHidePassword);
  164. if (SUCCEEDED(hr))
  165. SHTCharToAnsi(szUrl, pszUrl, cchSize);
  166. return hr;
  167. }
  168. #endif // UNICODE
  169. BOOL IncludePassword(void)
  170. {
  171. if (NOT_INITIALIZED == g_fNoPasswordsInAddressBar)
  172. g_fNoPasswordsInAddressBar = !SHRegGetBoolUSValue(SZ_REGKEY_FTPFOLDER, SZ_REGVALUE_PASSWDSIN_ADDRBAR, FALSE, TRUE);
  173. return g_fNoPasswordsInAddressBar;
  174. }
  175. HRESULT ParseUrlCreateFromPidl(LPCITEMIDLIST pidl, LPTSTR pszUrl, DWORD cchSize, DWORD dwFlags, BOOL fHidePassword)
  176. {
  177. HRESULT hr = S_OK;
  178. TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
  179. TCHAR szUrlPath[MAX_URL_STRING];
  180. TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH];
  181. TCHAR szPassword[INTERNET_MAX_PASSWORD_LENGTH];
  182. TCHAR szFragment[MAX_PATH];
  183. TCHAR szDownloadType[MAX_PATH] = TEXT("");
  184. INTERNET_PORT ipPortNum = INTERNET_DEFAULT_FTP_PORT;
  185. if (ILIsEmpty(pidl))
  186. {
  187. ASSERT(0); // We should never have an empty pidl. Get BryanSt if we hit this. Why does CFtpFolder have ILIsEmpty(m_pidlHere).
  188. szServer[0] = szUrlPath[0] = szUserName[0] = szPassword[0] = TEXT('\0');
  189. hr = E_FAIL;
  190. }
  191. else
  192. {
  193. FtpPidl_GetServer(pidl, szServer, ARRAYSIZE(szServer));
  194. GetDisplayPathFromPidl(pidl, szUrlPath, ARRAYSIZE(szUrlPath), FALSE);
  195. FtpPidl_GetUserName(pidl, szUserName, ARRAYSIZE(szUserName));
  196. if (FAILED(FtpPidl_GetPassword(pidl, szPassword, ARRAYSIZE(szPassword), !fHidePassword)))
  197. szPassword[0] = 0;
  198. FtpPidl_GetFragment(pidl, szFragment, ARRAYSIZE(szPassword));
  199. FtpPidl_GetDownloadTypeStr(pidl, szDownloadType, ARRAYSIZE(szDownloadType));
  200. UrlPathAdd(szUrlPath, ARRAYSIZE(szUrlPath), szDownloadType);
  201. ipPortNum = FtpPidl_GetPortNum(pidl);
  202. }
  203. if (SUCCEEDED(hr))
  204. {
  205. TCHAR szUserNameEscaped[INTERNET_MAX_USER_NAME_LENGTH];
  206. szUserNameEscaped[0] = 0;
  207. if (szUserName[0])
  208. EscapeString(szUserName, szUserNameEscaped, ARRAYSIZE(szUserNameEscaped));
  209. hr = UrlCreateEx(szServer, NULL_FOR_EMPTYSTR(szUserNameEscaped), szPassword, szUrlPath, szFragment, ipPortNum, szDownloadType, pszUrl, cchSize, dwFlags);
  210. }
  211. return hr;
  212. }
  213. /*****************************************************************************\
  214. FUNCTION: GetFullPrettyName
  215. DESCRIPTION:
  216. The user wants a pretty name so these are the cases we need to worry
  217. about:
  218. URL: Pretty Name:
  219. ---------------------------------- ---------------------
  220. ftp://joe:psswd@serv/ serv
  221. ftp://joe:psswd@serv/dir1/ dir1 on serv
  222. ftp://joe:psswd@serv/dir1/dir2/ dir2 on serv
  223. ftp://joe:psswd@serv/dir1/dir2/file.txt file.txt on serv
  224. \*****************************************************************************/
  225. HRESULT GetFullPrettyName(LPCITEMIDLIST pidl, LPTSTR pszUrl, DWORD cchSize)
  226. {
  227. HRESULT hr = S_OK;
  228. TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
  229. FtpPidl_GetServer(pidl, szServer, ARRAYSIZE(szServer));
  230. // Is there anything after the ServerItemID?
  231. if (!ILIsEmpty(_ILNext(pidl)))
  232. {
  233. // Yes, so let's get the name of the last item and
  234. // make the string "<LastItemName> on <Server>".
  235. WCHAR szLastItem[MAX_PATH];
  236. FtpItemID_GetDisplayName(ILFindLastID(pidl), szLastItem, ARRAYSIZE(szLastItem));
  237. LPTSTR pszStrArray[] = {szServer, (LPTSTR)szLastItem};
  238. // IE #56648: Akabir found that FormatMessageW & FormatMessageWrapW() do not
  239. // correctly handle UNICODE strings on Win9x. Therefore, we need to use
  240. // FormatMessageA() in that case.
  241. if (IsOSNT())
  242. {
  243. TCHAR szTemplate[MAX_PATH];
  244. LoadString(HINST_THISDLL, IDS_PRETTYNAMEFORMAT, szTemplate, ARRAYSIZE(szTemplate));
  245. EVAL(FormatMessage((FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY), (LPVOID)szTemplate,
  246. 0, 0, pszUrl, cchSize, (va_list*)pszStrArray));
  247. }
  248. else
  249. {
  250. CHAR szTemplateAnsi[MAX_PATH];
  251. CHAR szURLAnsi[MAX_URL_STRING];
  252. CHAR szServerAnsi[INTERNET_MAX_HOST_NAME_LENGTH];
  253. CHAR szFileNameAnsi[MAX_PATH];
  254. LPCSTR pszStrArrayAnsi[] = {szServerAnsi, szFileNameAnsi};
  255. SHTCharToAnsi(szServer, szServerAnsi, ARRAYSIZE(szServerAnsi));
  256. SHUnicodeToAnsi(szLastItem, szFileNameAnsi, ARRAYSIZE(szFileNameAnsi));
  257. LoadStringA(HINST_THISDLL, IDS_PRETTYNAMEFORMATA, szTemplateAnsi, ARRAYSIZE(szTemplateAnsi));
  258. EVAL(FormatMessageA((FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY), (LPVOID)szTemplateAnsi,
  259. 0, 0, szURLAnsi, ARRAYSIZE(szURLAnsi), (va_list*)pszStrArrayAnsi));
  260. SHAnsiToTChar(szURLAnsi, pszUrl, cchSize);
  261. }
  262. }
  263. else
  264. {
  265. // No, so we are done.
  266. StrCpyN(pszUrl, szServer, cchSize);
  267. }
  268. return hr;
  269. }
  270. /*****************************************************************************\
  271. FUNCTION: UrlCreateFromPidl
  272. DESCRIPTION:
  273. Common worker that handles SHGDN_FORPARSING style GetDisplayNameOf's.
  274. Note! that since we do not support junctions (duh), we can
  275. safely walk down the pidl generating goop as we go, secure
  276. in the knowledge that we are in charge of every subpidl.
  277. _CHARSET_: Since FTP filenames are always in the ANSI character
  278. set, by RFC 1738, we can return ANSI display names without loss
  279. of fidelity. In a general folder implementation, we should be
  280. using cStr to return display names, so that the UNICODE
  281. version of the shell extension can handle UNICODE names.
  282. \*****************************************************************************/
  283. HRESULT UrlCreateFromPidl(LPCITEMIDLIST pidl, DWORD shgno, LPTSTR pszUrl, DWORD cchSize, DWORD dwFlags, BOOL fHidePassword)
  284. {
  285. HRESULT hr = S_OK;
  286. pszUrl[0] = 0;
  287. if (!EVAL(pidl) ||
  288. !EVAL(IsValidPIDL(pidl)) ||
  289. !FtpPidl_IsValid(pidl) ||
  290. !FtpID_IsServerItemID(pidl) ||
  291. !EVAL(pszUrl && (0 < cchSize)))
  292. {
  293. return E_INVALIDARG;
  294. }
  295. if (shgno & SHGDN_INFOLDER)
  296. {
  297. // shgno & SHGDN_INFOLDER ?
  298. LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
  299. if (EVAL(pidlLast && !ILIsEmpty(pidlLast)))
  300. {
  301. hr = FtpPidl_GetDisplayName(pidlLast, pszUrl, cchSize);
  302. // Do they want to reparse it later? If they do and it's
  303. // a server, we need to give out the scheme also.
  304. // (SHGDN_INFOLDER) = "ServerName"
  305. // (SHGDN_INFOLDER|SHGDN_FORPARSING) = "ftp://ServerName/"
  306. if ((shgno & SHGDN_FORPARSING) &&
  307. (FtpID_IsServerItemID(pidlLast)))
  308. {
  309. // Yes, so we need to add the server name.
  310. TCHAR szServerName[MAX_PATH];
  311. StrCpyN(szServerName, pszUrl, ARRAYSIZE(szServerName));
  312. wnsprintf(pszUrl, cchSize, TEXT("ftp://%s/"), szServerName);
  313. }
  314. }
  315. else
  316. hr = E_FAIL;
  317. }
  318. else
  319. {
  320. // Assume they want the full URL.
  321. if (!EVAL((shgno & SHGDN_FORPARSING) ||
  322. (shgno & SHGDN_FORADDRESSBAR) ||
  323. (shgno == SHGDN_NORMAL)))
  324. {
  325. TraceMsg(TF_ALWAYS, "UrlCreateFromPidl() shgno=%#08lx and I dont know what to do with that.", shgno);
  326. }
  327. if ((shgno & SHGDN_FORPARSING) || (shgno & SHGDN_FORADDRESSBAR))
  328. {
  329. hr = ParseUrlCreateFromPidl(pidl, pszUrl, cchSize, dwFlags, fHidePassword);
  330. }
  331. else
  332. hr = GetFullPrettyName(pidl, pszUrl, cchSize);
  333. }
  334. // TraceMsg(TF_FTPURL_UTILS, "UrlCreateFromPidl() pszUrl=%ls, shgno=%#08lX", pszUrl, shgno);
  335. return hr;
  336. }
  337. /*****************************************************************************\
  338. FUNCTION: CreateFtpPidlFromDisplayPathHelper
  339. DESCRIPTION:
  340. The work done in CreateFtpPidlFromUrlPath requires a fair amount of
  341. stack space so we do most of the work in CreateFtpPidlFromDisplayPathHelper
  342. to prevent overflowing the stack.
  343. \*****************************************************************************/
  344. HRESULT CreateFtpPidlFromDisplayPathHelper(LPCWSTR pwzFullPath, CWireEncoding * pwe, ULONG *pcchEaten, LPITEMIDLIST * ppidl, BOOL fIsTypeKnown, BOOL fIsDir, LPITEMIDLIST * ppidlCurrentID, LPWSTR * ppwzRemaining)
  345. {
  346. HRESULT hr = E_FAIL;
  347. LPITEMIDLIST pidl;
  348. WCHAR wzFirstItem[MAX_PATH];
  349. WIRECHAR wFirstWireItem[MAX_PATH];
  350. WCHAR wzRemaining[MAX_PATH];
  351. BOOL fIsCurrSegmentADir = FALSE;
  352. BOOL fIsCurrSegmentTypeKnown = fIsTypeKnown;
  353. BOOL fIsFragSeparator = FALSE;
  354. *ppwzRemaining = NULL;
  355. *ppidl = 0;
  356. if (pcchEaten)
  357. *pcchEaten = 0; // The caller will parse the entire URL so we don't need to fill this in.
  358. if (L'/' == pwzFullPath[0])
  359. pwzFullPath = (LPWSTR) CharNextW(pwzFullPath);
  360. DisplayPathGetFirstSegment(pwzFullPath, wzFirstItem, ARRAYSIZE(wzFirstItem), NULL, wzRemaining, ARRAYSIZE(wzRemaining), &fIsCurrSegmentADir);
  361. // Is this the last segment?
  362. if (!wzRemaining[0])
  363. {
  364. // Yes, so if the caller knows the type of the last segment, use it now.
  365. if (fIsTypeKnown)
  366. fIsCurrSegmentADir = fIsDir;
  367. }
  368. else
  369. {
  370. // No, so we are assured that fIsDirCurrent is correct because it must have been followed
  371. // by a '/', or how could it be followed by another path segment?
  372. fIsCurrSegmentTypeKnown = TRUE;
  373. ASSERT(fIsCurrSegmentADir);
  374. }
  375. // NOTE: If the user entered "ftp://serv/Dir1/Dir2" fIsDir will be false for Dir2.
  376. // It will be marked as ambigious. (TODO: Check for extension?)
  377. EVAL(SUCCEEDED(pwe->UnicodeToWireBytes(NULL, wzFirstItem, ((pwe && pwe->IsUTF8Supported()) ? WIREENC_USE_UTF8 : WIREENC_NONE), wFirstWireItem, ARRAYSIZE(wFirstWireItem))));
  378. hr = FtpItemID_CreateFake(wzFirstItem, wFirstWireItem, fIsCurrSegmentTypeKnown, !fIsCurrSegmentADir, FALSE, &pidl);
  379. ASSERT(IsValidPIDL(pidl));
  380. if (SUCCEEDED(hr))
  381. {
  382. if (wzRemaining[0])
  383. {
  384. Str_SetPtrW(ppwzRemaining, wzRemaining);
  385. *ppidlCurrentID = pidl;
  386. }
  387. else
  388. *ppidl = pidl;
  389. }
  390. return hr;
  391. }
  392. /*****************************************************************************\
  393. FUNCTION: CreateFtpPidlFromUrlPath
  394. DESCRIPTION:
  395. This function will be passed the 'Path' of the URL and will create
  396. each of the IDs for each path segment. This will happen by creating an ID
  397. for the first path segment and then Combining that with the remaining
  398. IDs which are obtained by a recursive call.
  399. URL = "ftp://<UserName>:<Password>@<HostName>:<PortNum>/Dir1/Dir2/Dir3/file.txt[;Type=[a|b|d]]"
  400. Url Path = "Dir1/Dir2/Dir3/file.txt"
  401. pszFullPath - This URL will contain an URL Path (/Dir1/Dir2/MayBeFileOrDir).
  402. fIsTypeKnown - We can detect all directories w/o ambiguity because they end
  403. end '/' except for the last directory. fIsTypeKnown is used
  404. if this information is known. If TRUE, fIsDir will be used to
  405. disambiguate the last item. If FALSE, the last item will be marked
  406. a directory if it doesn't have an extension.
  407. The incoming name is %-encoded, but if we see an illegal %-sequence,
  408. just leave the % alone.
  409. Note that we return E_FAIL when given an unparseable path,
  410. not E_INVALIDARG.
  411. \*****************************************************************************/
  412. HRESULT CreateFtpPidlFromDisplayPath(LPCWSTR pwzFullPath, CWireEncoding * pwe, ULONG *pcchEaten, LPITEMIDLIST * ppidl, BOOL fIsTypeKnown, BOOL fIsDir)
  413. {
  414. HRESULT hr = E_FAIL;
  415. LPWSTR pwzRemaining = NULL;
  416. LPITEMIDLIST pidlCurrentID = NULL;
  417. hr = CreateFtpPidlFromDisplayPathHelper(pwzFullPath, pwe, pcchEaten, ppidl, fIsTypeKnown, fIsDir, &pidlCurrentID, &pwzRemaining);
  418. if (SUCCEEDED(hr) && pwzRemaining)
  419. {
  420. LPITEMIDLIST pidlSub;
  421. hr = CreateFtpPidlFromDisplayPath(pwzRemaining, pwe, pcchEaten, &pidlSub, fIsTypeKnown, fIsDir);
  422. if (SUCCEEDED(hr))
  423. {
  424. *ppidl = ILCombine(pidlCurrentID, pidlSub);
  425. hr = *ppidl ? S_OK : E_OUTOFMEMORY;
  426. ILFree(pidlSub);
  427. }
  428. ILFree(pidlCurrentID);
  429. Str_SetPtrW(&pwzRemaining, NULL);
  430. }
  431. return hr;
  432. }
  433. /*****************************************************************************\
  434. FUNCTION: CreateFtpPidlFromDisplayPathHelper
  435. DESCRIPTION:
  436. The work done in CreateFtpPidlFromUrlPath requires a fair amount of
  437. stack space so we do most of the work in CreateFtpPidlFromDisplayPathHelper
  438. to prevent overflowing the stack.
  439. \*****************************************************************************/
  440. HRESULT CreateFtpPidlFromFtpWirePathHelper(LPCWIRESTR pwFtpWirePath, CWireEncoding * pwe, ULONG *pcchEaten, LPITEMIDLIST * ppidl, BOOL fIsTypeKnown, BOOL fIsDir, LPITEMIDLIST * ppidlCurrentID, LPWIRESTR * ppwRemaining)
  441. {
  442. HRESULT hr = E_FAIL;
  443. LPITEMIDLIST pidl;
  444. WIRECHAR wFirstItem[MAX_PATH];
  445. WCHAR wzFirstItemDisplayName[MAX_PATH];
  446. WIRECHAR wRemaining[MAX_PATH];
  447. BOOL fIsCurrSegmentADir = FALSE;
  448. BOOL fIsCurrSegmentTypeKnown = fIsTypeKnown;
  449. BOOL fIsFragSeparator = FALSE;
  450. *ppwRemaining = NULL;
  451. *ppidl = 0;
  452. if (pcchEaten)
  453. *pcchEaten = 0; // The caller will parse the entire URL so we don't need to fill this in.
  454. if ('/' == pwFtpWirePath[0])
  455. pwFtpWirePath = (LPWIRESTR) CharNextA(pwFtpWirePath);
  456. WirePathGetFirstSegment(pwFtpWirePath, wFirstItem, ARRAYSIZE(wFirstItem), NULL, wRemaining, ARRAYSIZE(wRemaining), &fIsCurrSegmentADir);
  457. // Is this the last segment?
  458. if (!wRemaining[0])
  459. {
  460. // Yes, so if the caller knows the type of the last segment, use it now.
  461. if (fIsTypeKnown)
  462. fIsCurrSegmentADir = fIsDir;
  463. }
  464. else
  465. {
  466. // No, so we are assured that fIsDirCurrent is correct because it must have been followed
  467. // by a '/', or how could it be followed by another path segment?
  468. fIsCurrSegmentTypeKnown = TRUE;
  469. ASSERT(fIsCurrSegmentADir);
  470. }
  471. // NOTE: If the user entered "ftp://serv/Dir1/Dir2" fIsDir will be false for Dir2.
  472. // It will be marked as ambigious. (TODO: Check for extension?)
  473. EVAL(SUCCEEDED(pwe->WireBytesToUnicode(NULL, wFirstItem, WIREENC_IMPROVE_ACCURACY, wzFirstItemDisplayName, ARRAYSIZE(wzFirstItemDisplayName))));
  474. hr = FtpItemID_CreateFake(wzFirstItemDisplayName, wFirstItem, fIsCurrSegmentTypeKnown, !fIsCurrSegmentADir, FALSE, &pidl);
  475. ASSERT(IsValidPIDL(pidl));
  476. if (SUCCEEDED(hr))
  477. {
  478. if (wRemaining[0])
  479. {
  480. Str_SetPtrA(ppwRemaining, wRemaining);
  481. *ppidlCurrentID = pidl;
  482. }
  483. else
  484. *ppidl = pidl;
  485. }
  486. return hr;
  487. }
  488. HRESULT CreateFtpPidlFromFtpWirePath(LPCWIRESTR pwFtpWirePath, CWireEncoding * pwe, ULONG *pcchEaten, LPITEMIDLIST * ppidl, BOOL fIsTypeKnown, BOOL fIsDir)
  489. {
  490. HRESULT hr = E_FAIL;
  491. LPWIRESTR pwRemaining = NULL;
  492. LPITEMIDLIST pidlCurrentID = NULL;
  493. *ppidl = NULL;
  494. if (!pwFtpWirePath[0] || (0 == StrCmpA(pwFtpWirePath, SZ_URL_SLASHA)))
  495. return S_OK;
  496. hr = CreateFtpPidlFromFtpWirePathHelper(pwFtpWirePath, pwe, pcchEaten, ppidl, fIsTypeKnown, fIsDir, &pidlCurrentID, &pwRemaining);
  497. if (SUCCEEDED(hr) && pwRemaining)
  498. {
  499. LPITEMIDLIST pidlSub;
  500. hr = CreateFtpPidlFromFtpWirePath(pwRemaining, pwe, pcchEaten, &pidlSub, fIsTypeKnown, fIsDir);
  501. if (SUCCEEDED(hr))
  502. {
  503. *ppidl = ILCombine(pidlCurrentID, pidlSub);
  504. hr = *ppidl ? S_OK : E_OUTOFMEMORY;
  505. ILFree(pidlSub);
  506. }
  507. ILFree(pidlCurrentID);
  508. Str_SetPtrA(&pwRemaining, NULL);
  509. }
  510. return hr;
  511. }
  512. HRESULT CreateFtpPidlFromUrlPathAndPidl(LPCITEMIDLIST pidl, CWireEncoding * pwe, LPCWIRESTR pwFtpWirePath, LPITEMIDLIST * ppidl)
  513. {
  514. HRESULT hr = E_FAIL;
  515. LPITEMIDLIST pidlNew = ILClone(pidl);
  516. if (pidlNew)
  517. {
  518. LPITEMIDLIST pidlLast = (LPITEMIDLIST) ILGetLastID(pidlNew);
  519. while (!FtpID_IsServerItemID(pidlLast))
  520. {
  521. pidlLast->mkid.cb = 0; // Remove this ID.
  522. pidlLast = (LPITEMIDLIST) ILGetLastID(pidlNew);
  523. }
  524. LPITEMIDLIST pidlUrlPath = NULL;
  525. hr = CreateFtpPidlFromFtpWirePath(pwFtpWirePath, pwe, NULL, &pidlUrlPath, TRUE, TRUE);
  526. if (SUCCEEDED(hr))
  527. {
  528. *ppidl = ILCombine(pidlNew, pidlUrlPath);
  529. }
  530. if (pidlLast)
  531. ILFree(pidlLast);
  532. if (pidlUrlPath)
  533. ILFree(pidlUrlPath);
  534. }
  535. return hr;
  536. }
  537. /*****************************************************************************\
  538. CreateFtpPidlFromUrl
  539. The incoming name is %-encoded, but if we see an illegal %-sequence,
  540. just leave the % alone.
  541. Note that we return E_FAIL when given an unparseable path,
  542. not E_INVALIDARG.
  543. \*****************************************************************************/
  544. HRESULT CreateFtpPidlFromUrl(LPCTSTR pszUrl, CWireEncoding * pwe, ULONG *pcchEaten, LPITEMIDLIST * ppidl, IMalloc * pm, BOOL fHidePassword)
  545. {
  546. return CreateFtpPidlFromUrlEx(pszUrl, pwe, pcchEaten, ppidl, pm, fHidePassword, FALSE, FALSE);
  547. }
  548. /*****************************************************************************\
  549. FUNCTION: CreateFtpPidlFromUrlEx
  550. DESCRIPTION:
  551. pszUrl - This URL will contain an URL Path (/Dir1/Dir2/MayBeFileOrDir).
  552. fIsTypeKnown - We can detect all directories w/o ambiguity because they end
  553. end '/' except for the last directory. fIsTypeKnown is used
  554. if this information is known. If TRUE, fIsDir will be used to
  555. disambiguate the last item. If FALSE, the last item will be marked
  556. a directory if it doesn't have an extension.
  557. The incoming name is %-encoded, but if we see an illegal %-sequence,
  558. just leave the % alone.
  559. Note that we return E_FAIL when given an unparseable path,
  560. not E_INVALIDARG.
  561. \*****************************************************************************/
  562. HRESULT CreateFtpPidlFromUrlEx(LPCTSTR pszUrl, CWireEncoding * pwe, ULONG *pcchEaten, LPITEMIDLIST * ppidl, IMalloc * pm, BOOL fHidePassword, BOOL fIsTypeKnown, BOOL fIsDir)
  563. {
  564. URL_COMPONENTS urlComps = {0};
  565. HRESULT hr = E_FAIL;
  566. // URL = "ftp://<UserName>:<Password>@<HostName>:<PortNum>/Dir1/Dir2/Dir3/file.txt[;Type=[a|b|d]]"
  567. TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
  568. TCHAR szUrlPath[MAX_URL_STRING];
  569. TCHAR szExtraInfo[MAX_PATH]; // Includes Port Number and download type (ASCII, Binary, Detect)
  570. TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH];
  571. TCHAR szPassword[INTERNET_MAX_PASSWORD_LENGTH];
  572. *ppidl = 0;
  573. urlComps.dwStructSize = sizeof(urlComps);
  574. urlComps.lpszHostName = szServer;
  575. urlComps.dwHostNameLength = ARRAYSIZE(szServer);
  576. urlComps.lpszUrlPath = szUrlPath;
  577. urlComps.dwUrlPathLength = ARRAYSIZE(szUrlPath);
  578. urlComps.lpszUserName = szUserName;
  579. urlComps.dwUserNameLength = ARRAYSIZE(szUserName);
  580. urlComps.lpszPassword = szPassword;
  581. urlComps.dwPasswordLength = ARRAYSIZE(szPassword);
  582. urlComps.lpszExtraInfo = szExtraInfo;
  583. urlComps.dwExtraInfoLength = ARRAYSIZE(szExtraInfo);
  584. BOOL fResult = InternetCrackUrl(pszUrl, 0, ICU_DECODE, &urlComps);
  585. if (fResult && (INTERNET_SCHEME_FTP == urlComps.nScheme))
  586. {
  587. LPITEMIDLIST pidl;
  588. DWORD dwDownloadType = 0; // Indicate that it hasn't yet been specified.
  589. BOOL fASCII;
  590. ASSERT(INTERNET_SCHEME_FTP == urlComps.nScheme);
  591. // NOTE:
  592. // If the user is trying to give an NT UserName/DomainName pair, a bug will be encountered.
  593. // Url in AddressBand="ftp://DomainName\UserName:Password@ServerName/"
  594. // Url passed to us="ftp://DomainName/UserName:Password@ServerName/"
  595. // We need to detect this case and fix it because this will cause "DomainName" to become
  596. // the server name and the rest will become the UrlPath.
  597. // ASSERT(!StrChr(szUrlPath, TEXT(':')) && !StrChr(szUrlPath, TEXT('@')));
  598. if (S_OK == UrlRemoveDownloadType(szUrlPath, NULL, &fASCII))
  599. {
  600. if (fASCII)
  601. dwDownloadType = (IDVALID_DLTYPE | IDVALID_DL_ASCII);
  602. else
  603. dwDownloadType = IDVALID_DLTYPE;
  604. }
  605. if (!szServer[0])
  606. {
  607. TraceMsg(TF_FTPURL_UTILS, "CreateFtpPidlFromUrl() failed because szServer=%s", szServer);
  608. hr = E_FAIL; // Bad URL so fail.
  609. }
  610. else
  611. {
  612. //TraceMsg(TF_FTPURL_UTILS, "CreateFtpPidlFromUrl() szServer=%s, szUrlPath=%s, szUserName=%s, szPassword=%s", szServer, szUrlPath, szUserName, szPassword);
  613. hr = FtpServerID_Create(szServer, szUserName, szPassword, dwDownloadType, urlComps.nPort, &pidl, pm, fHidePassword);
  614. if (SUCCEEDED(hr))
  615. {
  616. ASSERT(IsValidPIDL(pidl));
  617. if (szUrlPath[0] && StrCmp(szUrlPath, SZ_URL_SLASH))
  618. {
  619. LPITEMIDLIST pidlSub;
  620. hr = CreateFtpPidlFromDisplayPath(szUrlPath, pwe, pcchEaten, &pidlSub, fIsTypeKnown, fIsDir);
  621. if (SUCCEEDED(hr))
  622. {
  623. // Wininet chokes during requests through Netscape proxies when the GET is
  624. // redirected by the proxy to include the slash. Both FTP folder and
  625. // web-based FTP navigations are affected by this.
  626. if (szUrlPath[lstrlen(szUrlPath)-1] == TEXT(CH_URL_URL_SLASHA))
  627. {
  628. LPCITEMIDLIST pidlLast = ILGetLastID(pidlSub);
  629. if (pidlLast)
  630. FtpItemID_SetCompatFlags(pidlLast, FtpItemID_GetCompatFlags(pidlLast) | COMPAT_APPENDSLASHTOURL);
  631. }
  632. *ppidl = ILCombine(pidl, pidlSub);
  633. if (szExtraInfo[0])
  634. {
  635. LPITEMIDLIST pidlFragment;
  636. WIRECHAR wFragment[MAX_PATH];
  637. // The code page is just whatever the user is using but oh well, I don't
  638. // care about fragments.
  639. SHUnicodeToAnsi(szExtraInfo, wFragment, ARRAYSIZE(wFragment));
  640. // There is a fragment, so we need to add it.
  641. hr = FtpItemID_CreateFake(szExtraInfo, wFragment, TRUE, FALSE, TRUE, &pidlFragment);
  642. if (SUCCEEDED(hr))
  643. {
  644. LPITEMIDLIST pidlPrevious = *ppidl;
  645. *ppidl = ILCombine(pidlPrevious, pidlFragment);
  646. ILFree(pidlPrevious);
  647. ILFree(pidlFragment);
  648. }
  649. }
  650. hr = *ppidl ? S_OK : E_OUTOFMEMORY;
  651. ILFree(pidlSub);
  652. }
  653. ILFree(pidl);
  654. }
  655. else
  656. *ppidl = pidl;
  657. if (SUCCEEDED(hr))
  658. {
  659. ASSERT(IsValidPIDL(*ppidl));
  660. if (pcchEaten)
  661. *pcchEaten = lstrlen(pszUrl); // TODO: Someday we can do this recursively.
  662. }
  663. }
  664. }
  665. }
  666. else
  667. TraceMsg(TF_FTPURL_UTILS, "CreateFtpPidlFromUrl() failed InternetCrackUrl() because pszUrl=%s, fResult=%d, urlComps.nScheme=%d", pszUrl, fResult, urlComps.nScheme);
  668. //TraceMsg(TF_FTPURL_UTILS, "CreateFtpPidlFromUrl() is returning, hr=%#08lx", hr);
  669. return hr;
  670. }
  671. /*****************************************************************************\
  672. FUNCTION: Win32FindDataFromPidl
  673. DESCRIPTION:
  674. Fill in the WIN32_FIND_DATA data structure from the info in the pidl.
  675. \*****************************************************************************/
  676. HRESULT Win32FindDataFromPidl(LPCITEMIDLIST pidl, LPWIN32_FIND_DATAW pwfd, BOOL fFullPath, BOOL fInDisplayFormat)
  677. {
  678. HRESULT hr = E_INVALIDARG;
  679. LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
  680. ASSERT(pwfd);
  681. if (!EVAL(FtpPidl_IsValid(pidl)))
  682. return E_INVALIDARG;
  683. // I don't want to lie when I pass out File Size and Date info.
  684. if ((IDVALID_FILESIZE | IDVALID_MOD_DATE) & FtpItemID_GetTypeID(pidlLast))
  685. {
  686. pwfd->dwReserved0 = 0;
  687. pwfd->dwReserved1 = 0;
  688. pwfd->cAlternateFileName[0] = 0;
  689. pwfd->nFileSizeLow = FtpItemID_GetFileSizeLo(pidlLast);
  690. pwfd->nFileSizeHigh = FtpItemID_GetFileSizeHi(pidlLast);
  691. pwfd->dwFileAttributes = FtpItemID_GetAttributes(pidlLast);
  692. // See the notes in priv.h on how time works.
  693. pwfd->ftCreationTime = FtpPidl_GetFTPFileTime(pidlLast);
  694. pwfd->ftLastWriteTime = pwfd->ftCreationTime;
  695. pwfd->ftLastAccessTime = pwfd->ftCreationTime;
  696. if (fFullPath)
  697. {
  698. if (fInDisplayFormat)
  699. hr = GetDisplayPathFromPidl(pidl, pwfd->cFileName, ARRAYSIZE(pwfd->cFileName), FALSE);
  700. else
  701. hr = GetWirePathFromPidl(pidl, (LPWIRESTR)pwfd->cFileName, ARRAYSIZE(pwfd->cFileName), FALSE);
  702. }
  703. else
  704. {
  705. hr = S_OK;
  706. if (fInDisplayFormat)
  707. {
  708. FtpPidl_GetLastFileDisplayName(pidl, pwfd->cFileName, ARRAYSIZE(pwfd->cFileName));
  709. }
  710. else
  711. {
  712. LPCWIRESTR pszName = FtpPidl_GetLastItemWireName(pidl);
  713. StrCpyNA((LPWIRESTR)pwfd->cFileName, (pszName ? pszName : ""), ARRAYSIZE(pwfd->cFileName));
  714. }
  715. }
  716. }
  717. return hr;
  718. }
  719. STDAPI_(UINT) ILGetSizeOfFirst(LPCITEMIDLIST pidl)
  720. {
  721. return pidl->mkid.cb;
  722. }
  723. /****************************************************\
  724. FTP Server ItemIDs
  725. \****************************************************/
  726. /****************************************************\
  727. FTP PIDL Cooking functions
  728. \****************************************************/
  729. /*****************************************************************************\
  730. DATA STRUCTURE: FTPIDLIST
  731. DESCRIPTION:
  732. What our private IDLIST looks like for a file, a dir, or a fragment.
  733. The bytes sent to an ftp server or received from an FTP server are
  734. wire bytes (could be UTF-8 or DBCS/MBCS) encoded. We also store
  735. a unicode version that has already been converted after trying to guess
  736. the code page.
  737. Note that the use of any TCHAR inside an IDLIST is COMPLETELY WRONG!
  738. IDLISTs can be saved in a file and reloaded later. If it were saved
  739. by an ANSI version of the shell extension but loaded by a UNICODE
  740. version, things would turn ugly real fast.
  741. \*****************************************************************************/
  742. /*****************************************************************************\
  743. FTPSERVERIDLIST structure
  744. A typical full pidl looks like this:
  745. <Not Our ItemID> [Our ItemID]
  746. <The Internet>\[server,username,password,port#,downloadtype]\[subdir]\...\[file]
  747. The <The Internet> part is whatever the shell gives us in our
  748. CFtpFolder::_Initialize, telling us where in the namespace
  749. we are rooted.
  750. We are concerned only with the parts after the <The Internet> root,
  751. the offset to which is remembered in the CFtpFolder class
  752. in m_ibPidlRoot. Ways of accessing various bits of
  753. information related to our full pidl are provided by our
  754. CFtpFolder implementation, qv.
  755. The first FTP IDList entry describes the server. The remaining
  756. entries describe objects (files or folders) on the server.
  757. \*****************************************************************************/
  758. typedef struct tagFTPSERVERIDLIST
  759. {
  760. DWORD dwIDType; // Server ItemID or Dir ItemID? Which Bits are valid?
  761. DWORD dwVersion; // version
  762. SESSIONKEY sessionKey; // Session Key
  763. DWORD dwPasswordCookie; // Password Cookie
  764. DWORD dwReserved1; // for future use
  765. DWORD dwReserved2; // for future use
  766. DWORD dwReserved3; // for future use
  767. DWORD dwPortNumber; // Port Number on server
  768. DWORD cchServerSize; // StrLen of szServer
  769. CHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH]; // Server
  770. DWORD cchUserNameSize; // StrLen of szUserName
  771. CHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH]; // User Name for Login
  772. DWORD cchPasswordSize; // StrLen of szPassword
  773. CHAR szPassword[INTERNET_MAX_PASSWORD_LENGTH]; // Password for Login
  774. } FTPSERVERIDLIST;
  775. typedef UNALIGNED FTPSERVERIDLIST * LPFTPSERVERIDLIST;
  776. /*****************************************************************************\
  777. DESCRIPTION:
  778. On ia64, we need to worry about alignment issues. The easiest way is
  779. to allocate the struct we have in our PIDL so it's quad word aligned.
  780. We can then use existing code to read out of it. The problem is that
  781. we need to be compatible with old pidls from pre-whistler that are only
  782. DWORD aligned (for alpha machines).
  783. \*****************************************************************************/
  784. LPFTPSERVERIDLIST FtpServerID_GetDataThunk(LPCITEMIDLIST pidl)
  785. {
  786. #ifndef ALIGNMENT_MACHINE
  787. LPFTPSERVERIDLIST pFtpServerItemId = (LPFTPSERVERIDLIST) ProtocolIdlInnerData(pidl);
  788. if (!FtpPidl_IsValid(pidl) ||
  789. !IS_VALID_SERVER_ITEMID(pFtpServerItemId->dwIDType)) // If any other bits are sit, it's invalid.
  790. {
  791. pFtpServerItemId = NULL;
  792. }
  793. #else
  794. LPFTPSERVERIDLIST pFtpServerItemId = NULL;
  795. LPFTPSERVERIDLIST pLocation = (LPFTPSERVERIDLIST) ProtocolIdlInnerData(pidl);
  796. if (FtpPidl_IsValid(pidl) &&
  797. IS_VALID_SERVER_ITEMID(pLocation->dwIDType)) // If any other bits are sit, it's invalid.
  798. {
  799. DWORD cbOffset = (DWORD) (((BYTE *)pLocation - (BYTE *)pidl) % ALIGN_QUAD);
  800. DWORD cbSize = ILGetSizeOfFirst(pidl);
  801. pFtpServerItemId = (LPFTPSERVERIDLIST) LocalAlloc(LPTR, cbSize + cbOffset);
  802. if (pFtpServerItemId)
  803. {
  804. CopyMemory(pFtpServerItemId, pLocation, cbSize-cbOffset);
  805. }
  806. }
  807. #endif // ALIGNMENT_MACHINE
  808. return pFtpServerItemId;
  809. }
  810. void FtpServerID_FreeThunk(LPFTPSERVERIDLIST pFtpServerItemId)
  811. {
  812. #ifndef ALIGNMENT_MACHINE
  813. // We don't need to do anything.
  814. #else
  815. if (pFtpServerItemId)
  816. {
  817. LocalFree(pFtpServerItemId);
  818. }
  819. #endif // ALIGNMENT_MACHINE
  820. }
  821. LPFTPSERVERIDLIST FtpServerID_GetDataSafe(LPCITEMIDLIST pidl)
  822. {
  823. LPFTPSERVERIDLIST pFtpServerItemId = NULL;
  824. if (EVAL(pidl) && !ILIsEmpty(pidl))
  825. {
  826. pFtpServerItemId = (LPFTPSERVERIDLIST) ProtocolIdlInnerData(pidl);
  827. }
  828. return pFtpServerItemId;
  829. }
  830. LPFTPSERVERIDLIST FtpServerID_GetDataSafeThunk(LPCITEMIDLIST pidl)
  831. {
  832. LPFTPSERVERIDLIST pFtpServerItemId = NULL;
  833. if (pidl && !ILIsEmpty(pidl))
  834. {
  835. pFtpServerItemId = FtpServerID_GetDataThunk(pidl);
  836. }
  837. return pFtpServerItemId;
  838. }
  839. BOOL FtpID_IsServerItemID(LPCITEMIDLIST pidl)
  840. {
  841. LPFTPSERVERIDLIST pFtpServerItemID = FtpServerID_GetDataSafeThunk(pidl);
  842. BOOL fIsServerItemID = FALSE;
  843. if (pFtpServerItemID && IS_VALID_SERVER_ITEMID(pFtpServerItemID->dwIDType))
  844. fIsServerItemID = TRUE;
  845. FtpServerID_FreeThunk(pFtpServerItemID);
  846. return fIsServerItemID;
  847. }
  848. LPCITEMIDLIST FtpID_GetLastIDReferense(LPCITEMIDLIST pidl)
  849. {
  850. LPCITEMIDLIST pidlCurrent = pidl;
  851. LPCITEMIDLIST pidlNext = pidl;
  852. if (!pidl || ILIsEmpty(pidl))
  853. return pidl;
  854. for (; !ILIsEmpty(pidlNext); pidl = _ILNext(pidl))
  855. {
  856. pidlCurrent = pidlNext;
  857. pidlNext = _ILNext(pidlNext);
  858. }
  859. return pidlCurrent;
  860. }
  861. CCookieList * g_pCookieList = NULL;
  862. CCookieList * GetCookieList(void)
  863. {
  864. ENTERCRITICAL;
  865. if (!g_pCookieList)
  866. g_pCookieList = new CCookieList();
  867. ASSERT(g_pCookieList);
  868. LEAVECRITICAL;
  869. return g_pCookieList;
  870. }
  871. SESSIONKEY g_SessionKey = {-1, -1};
  872. HRESULT PurgeSessionKey(void)
  873. {
  874. GetSystemTimeAsFileTime(&g_SessionKey);
  875. return S_OK;
  876. }
  877. SESSIONKEY GetSessionKey(void)
  878. {
  879. if (-1 == g_SessionKey.dwHighDateTime)
  880. PurgeSessionKey();
  881. return g_SessionKey;
  882. }
  883. BOOL AreSessionKeysEqual(SESSIONKEY sk1, SESSIONKEY sk2)
  884. {
  885. if ((sk1.dwHighDateTime == sk2.dwHighDateTime) &&
  886. (sk1.dwLowDateTime == sk2.dwLowDateTime))
  887. {
  888. return TRUE;
  889. }
  890. return FALSE;
  891. }
  892. // This is used in order to make sure Alpha machines don't get DWORD mis-aligned.
  893. #define LENGTH_AFTER_ALIGN(nLen, nAlignSize) (((nLen) % (nAlignSize)) ? ((nLen) + ((nAlignSize) - ((nLen) % (nAlignSize)))) : (nLen))
  894. /****************************************************\
  895. FUNCTION: FtpServerID_Create
  896. DESCRIPTION:
  897. Create a Ftp Server ItemID and fill it in.
  898. \****************************************************/
  899. HRESULT FtpServerID_Create(LPCTSTR pszServer, LPCTSTR pszUserName, LPCTSTR pszPassword,
  900. DWORD dwFlags, INTERNET_PORT ipPortNum, LPITEMIDLIST * ppidl, IMalloc *pm, BOOL fHidePassword)
  901. {
  902. HRESULT hr;
  903. DWORD cb;
  904. LPITEMIDLIST pidl = NULL;
  905. DWORD cchServerLen = lstrlen(pszServer);
  906. DWORD cchUserNameLen = lstrlen(pszUserName);
  907. DWORD cchPasswordLen = lstrlen(pszPassword);
  908. LPFTPSERVERIDLIST pFtpServerID = NULL;
  909. cchServerLen = LENGTH_AFTER_ALIGN(cchServerLen + 1, sizeof(DWORD));
  910. cchUserNameLen = LENGTH_AFTER_ALIGN(cchUserNameLen + 1, sizeof(DWORD));
  911. cchPasswordLen = LENGTH_AFTER_ALIGN(cchPasswordLen + 1, sizeof(DWORD));
  912. if (!(EVAL(ppidl) && pszServer[0]))
  913. return E_FAIL;
  914. // Set bits in dwFlags that are appropriate
  915. if (pszUserName[0])
  916. dwFlags |= IDVALID_USERNAME;
  917. if (pszPassword[0])
  918. dwFlags |= IDVALID_PASSWORD;
  919. // Find lenght of FTPSERVERIDLIST struct without the MAX_PATH strings
  920. cb = (sizeof(*pFtpServerID) - sizeof(pFtpServerID->szServer) - sizeof(pFtpServerID->szUserName) - sizeof(pFtpServerID->szPassword));
  921. // Add the size of the strings.
  922. cb += (cchServerLen + cchUserNameLen + cchPasswordLen);
  923. ASSERT(0 == (cb % sizeof(DWORD))); // Make sure it's DWORD aligned for Alpha machines.
  924. pFtpServerID = (LPFTPSERVERIDLIST) LocalAlloc(LPTR, cb);
  925. if (pFtpServerID)
  926. {
  927. LPSTR pszNext;
  928. pszNext = pFtpServerID->szServer;
  929. ZeroMemory(pFtpServerID, cb);
  930. pFtpServerID->dwIDType = (dwFlags | IDTYPE_ISVALID | IDTYPE_SERVER | IDVALID_PORT_NUM);
  931. ASSERT(IS_VALID_SERVER_ITEMID(pFtpServerID->dwIDType));
  932. pFtpServerID->dwVersion = PIDL_VERSION_NUMBER;
  933. pFtpServerID->sessionKey = GetSessionKey();
  934. pFtpServerID->dwPasswordCookie = -1;
  935. pFtpServerID->dwPortNumber = ipPortNum;
  936. pFtpServerID->cchServerSize = cchServerLen;
  937. SHTCharToAnsi(pszServer, pszNext, pFtpServerID->cchServerSize);
  938. pszNext += cchServerLen; // Advance to cchUserNameSize
  939. *((LPDWORD) pszNext) = cchUserNameLen; // Fill in cchUserNameSize
  940. pszNext = (LPSTR)(((UNALIGNED BYTE *) pszNext) + sizeof(DWORD)); // Advance to szUserName
  941. SHTCharToAnsi(pszUserName, pszNext, cchUserNameLen);
  942. if (fHidePassword)
  943. {
  944. pFtpServerID->dwIDType |= IDVALID_HIDE_PASSWORD;
  945. if (EVAL(GetCookieList()))
  946. pFtpServerID->dwPasswordCookie = GetCookieList()->GetCookie(pszPassword);
  947. ASSERT(-1 != pFtpServerID->dwPasswordCookie);
  948. pszPassword = TEXT("");
  949. }
  950. // TraceMsg(TF_FTPURL_UTILS, "FtpServerID_Create(\"ftp://%s:%s@%s/\") dwIDType=%#80lx", pszUserName, pszPassword, pszServer, pFtpServerID->dwIDType);
  951. pszNext += cchUserNameLen; // Advance to cchPasswordLen
  952. *((LPDWORD) pszNext) = cchPasswordLen; // Fill in cchPasswordLen
  953. pszNext = (LPSTR)(((UNALIGNED BYTE *) pszNext) + sizeof(DWORD)); // Advance to szPassword
  954. SHTCharToAnsi(pszPassword, pszNext, cchPasswordLen); // Fill in pszPassword
  955. pidl = (LPITEMIDLIST) pm->Alloc(cb);
  956. if (pidl)
  957. {
  958. LPFTPSERVERIDLIST pFtpServerIDDest = FtpServerID_GetDataSafe(pidl);
  959. if (pFtpServerIDDest)
  960. {
  961. CopyMemory(pFtpServerIDDest, pFtpServerID, cb);
  962. }
  963. }
  964. LocalFree(pFtpServerID);
  965. }
  966. *ppidl = pidl;
  967. hr = pidl ? S_OK : E_OUTOFMEMORY;
  968. ASSERT(IsValidPIDL(*ppidl));
  969. return hr;
  970. }
  971. DWORD FtpServerID_GetTypeID(LPCITEMIDLIST pidl)
  972. {
  973. DWORD dwResult = 0;
  974. LPFTPSERVERIDLIST pFtpServerID = FtpServerID_GetDataThunk(pidl);
  975. ASSERT(FtpID_IsServerItemID(pidl));
  976. if (pFtpServerID &&
  977. EVAL(FtpPidl_IsValid(pidl)))
  978. {
  979. dwResult = pFtpServerID->dwIDType;
  980. }
  981. FtpServerID_FreeThunk(pFtpServerID);
  982. return dwResult;
  983. }
  984. HRESULT FtpServerID_GetServer(LPCITEMIDLIST pidl, LPTSTR pszServer, DWORD cchSize)
  985. {
  986. HRESULT hr = E_FAIL;
  987. LPFTPSERVERIDLIST pFtpServerID = FtpServerID_GetDataThunk(pidl);
  988. if (pFtpServerID)
  989. {
  990. SHAnsiToTChar(pFtpServerID->szServer, pszServer, cchSize);
  991. FtpServerID_FreeThunk(pFtpServerID);
  992. hr = S_OK;
  993. }
  994. return hr;
  995. }
  996. BOOL FtpServerID_ServerStrCmp(LPCITEMIDLIST pidl, LPCTSTR pszServer)
  997. {
  998. BOOL fMatch = FALSE;
  999. LPFTPSERVERIDLIST pFtpServerID = FtpServerID_GetDataThunk(pidl);
  1000. #ifdef UNICODE
  1001. CHAR szServerAnsi[MAX_PATH];
  1002. SHUnicodeToAnsi(pszServer, szServerAnsi, ARRAYSIZE(szServerAnsi));
  1003. #endif // UNICODE
  1004. if (pFtpServerID)
  1005. {
  1006. #ifdef UNICODE
  1007. fMatch = (0 == StrCmpA(pFtpServerID->szServer, szServerAnsi));
  1008. #else // UNICODE
  1009. fMatch = (0 == StrCmpA(pFtpServerID->szServer, pszServer));
  1010. #endif // UNICODE
  1011. }
  1012. FtpServerID_FreeThunk(pFtpServerID);
  1013. return fMatch;
  1014. }
  1015. HRESULT FtpServerID_GetUserName(LPCITEMIDLIST pidl, LPTSTR pszUserName, DWORD cchSize)
  1016. {
  1017. HRESULT hr = E_FAIL;
  1018. LPFTPSERVERIDLIST pFtpServerID = FtpServerID_GetDataThunk(pidl);
  1019. if (pFtpServerID)
  1020. {
  1021. LPCSTR pszSourceUserName = pFtpServerID->szServer + pFtpServerID->cchServerSize + sizeof(DWORD);
  1022. SHAnsiToTChar(pszSourceUserName, pszUserName, cchSize);
  1023. hr = S_OK;
  1024. }
  1025. FtpServerID_FreeThunk(pFtpServerID);
  1026. return hr;
  1027. }
  1028. HRESULT FtpServerID_GetPassword(LPCITEMIDLIST pidl, LPTSTR pszPassword, DWORD cchSize, BOOL fIncludingHiddenPassword)
  1029. {
  1030. HRESULT hr = E_FAIL;
  1031. LPFTPSERVERIDLIST pFtpServerID = FtpServerID_GetDataThunk(pidl);
  1032. pszPassword[0] = 0;
  1033. if (pFtpServerID)
  1034. {
  1035. // Was the password hidden?
  1036. if (fIncludingHiddenPassword &&
  1037. (IDVALID_HIDE_PASSWORD & pFtpServerID->dwIDType))
  1038. {
  1039. // Yes, so get it out of the cookie jar (list)
  1040. if (EVAL(GetCookieList()) &&
  1041. AreSessionKeysEqual(pFtpServerID->sessionKey, GetSessionKey()))
  1042. {
  1043. hr = GetCookieList()->GetString(pFtpServerID->dwPasswordCookie, pszPassword, cchSize);
  1044. }
  1045. }
  1046. else
  1047. {
  1048. // No, so what's in the pidl is the real password.
  1049. BYTE * pvSizeOfUserName = (BYTE *) (pFtpServerID->szServer + pFtpServerID->cchServerSize);
  1050. DWORD dwSizeOfUserName = *(DWORD *) pvSizeOfUserName;
  1051. LPCSTR pszSourcePassword = (LPCSTR) (pvSizeOfUserName + dwSizeOfUserName + 2*sizeof(DWORD));
  1052. SHAnsiToTChar(pszSourcePassword, pszPassword, cchSize);
  1053. hr = S_OK;
  1054. }
  1055. }
  1056. FtpServerID_FreeThunk(pFtpServerID);
  1057. return hr;
  1058. }
  1059. INTERNET_PORT FtpServerID_GetPortNum(LPCITEMIDLIST pidl)
  1060. {
  1061. INTERNET_PORT portReturn = INTERNET_DEFAULT_FTP_PORT;
  1062. LPFTPSERVERIDLIST pFtpServerID = FtpServerID_GetDataThunk(pidl);
  1063. ASSERT(FtpID_IsServerItemID(pidl));
  1064. if (pFtpServerID)
  1065. {
  1066. portReturn = (INTERNET_PORT)pFtpServerID->dwPortNumber;
  1067. FtpServerID_FreeThunk(pFtpServerID);
  1068. }
  1069. return portReturn;
  1070. }
  1071. HRESULT FtpServerID_SetHiddenPassword(LPITEMIDLIST pidl, LPCTSTR pszPassword)
  1072. {
  1073. HRESULT hr = E_INVALIDARG;
  1074. LPFTPSERVERIDLIST pFtpServerID = FtpServerID_GetDataThunk(pidl);
  1075. ASSERT(FtpID_IsServerItemID(pidl));
  1076. if (pFtpServerID)
  1077. {
  1078. pFtpServerID->sessionKey = GetSessionKey();
  1079. pFtpServerID->dwIDType |= IDVALID_HIDE_PASSWORD;
  1080. if (EVAL(GetCookieList()))
  1081. pFtpServerID->dwPasswordCookie = GetCookieList()->GetCookie(pszPassword);
  1082. hr = S_OK;
  1083. }
  1084. FtpServerID_FreeThunk(pFtpServerID);
  1085. return hr;
  1086. }
  1087. HRESULT FtpServerID_GetStrRet(LPCITEMIDLIST pidl, LPSTRRET lpName)
  1088. {
  1089. LPFTPSERVERIDLIST pFtpServerID = FtpServerID_GetDataThunk(pidl);
  1090. ASSERT(FtpID_IsServerItemID(pidl));
  1091. if (pFtpServerID)
  1092. {
  1093. lpName->uType = STRRET_OFFSET;
  1094. lpName->uOffset = (DWORD) (sizeof(FTPSERVERIDLIST) - sizeof(pFtpServerID->szServer) + (LPBYTE)pFtpServerID - (LPBYTE)pidl);
  1095. }
  1096. else
  1097. {
  1098. lpName->uType = STRRET_CSTR;
  1099. lpName->cStr[0] = '\0';
  1100. }
  1101. FtpServerID_FreeThunk(pFtpServerID);
  1102. return S_OK;
  1103. }
  1104. /****************************************************\
  1105. FTP File/Dir ItemIDs
  1106. \****************************************************/
  1107. typedef struct tagFTPIDLIST
  1108. {
  1109. DWORD dwIDType; // Server ItemID or Dir ItemID? Which Bits are valid?
  1110. DWORD dwAttributes; // What are the file/dir attributes
  1111. ULARGE_INTEGER uliFileSize;
  1112. FILETIME ftModified; // Stored in Local Time Zone. (FTP Time)
  1113. DWORD dwUNIXPermission; // UNIX CHMOD Permissions (0x00000777, 4=Read, 2=Write, 1=Exec, <Owner><Group><All>)
  1114. DWORD dwCompatFlags; // Special case handling
  1115. WIRECHAR szWireName[MAX_PATH]; // Needs to go last.
  1116. WCHAR wzDisplayName[MAX_PATH]; // Converted to unicode to be displayed in the UI.
  1117. } FTPIDLIST;
  1118. typedef UNALIGNED FTPIDLIST * LPFTPIDLIST;
  1119. HRESULT FtpItemID_Alloc(LPFTPIDLIST pfi, LPITEMIDLIST * ppidl);
  1120. typedef struct _FTPIDLIST_WITHHEADER
  1121. {
  1122. USHORT cb; // size
  1123. FTPIDLIST fidListData;
  1124. USHORT cbTerminator; // size of next ID (Empty)
  1125. } FTPIDLIST_WITHHEADER;
  1126. LPFTPIDLIST FtpItemID_GetDataInternal(LPCITEMIDLIST pidl)
  1127. {
  1128. BYTE * pbData = (BYTE *) pidl;
  1129. pbData += SIZE_ITEMID_SIZEFIELD; // Skip over the size.
  1130. LPFTPIDLIST pFtpItemId = (LPFTPIDLIST) pbData;
  1131. if (!EVAL(IS_VALID_DIRORFILE_ITEMID(pFtpItemId->dwIDType))) // If any other bits are sit, it's invalid.
  1132. pFtpItemId = NULL;
  1133. return pFtpItemId;
  1134. }
  1135. /*****************************************************************************\
  1136. DESCRIPTION:
  1137. On ia64, we need to worry about alignment issues. The easiest way is
  1138. to allocate the struct we have in our PIDL so it's quad word aligned.
  1139. We can then use existing code to read out of it. The problem is that
  1140. we need to be compatible with old pidls from pre-whistler that are only
  1141. DWORD aligned (for alpha machines).
  1142. \*****************************************************************************/
  1143. LPFTPIDLIST FtpItemID_GetDataThunk(LPCITEMIDLIST pidl)
  1144. {
  1145. #ifndef ALIGNMENT_MACHINE
  1146. LPFTPIDLIST pFtpItemId = FtpItemID_GetDataInternal(pidl);
  1147. #else
  1148. LPFTPIDLIST pFtpItemId = NULL;
  1149. LPFTPIDLIST pLocation = FtpItemID_GetDataInternal(pidl);
  1150. if (pLocation)
  1151. {
  1152. DWORD cbSize = ILGetSizeOfFirst(pidl);
  1153. pFtpItemId = (LPFTPIDLIST) LocalAlloc(LPTR, cbSize);
  1154. if (pFtpItemId)
  1155. {
  1156. CopyMemory(pFtpItemId, pLocation, cbSize - SIZE_ITEMID_SIZEFIELD);
  1157. }
  1158. }
  1159. #endif // ALIGNMENT_MACHINE
  1160. return pFtpItemId;
  1161. }
  1162. void FtpItemID_FreeThunk(LPFTPIDLIST pFtpItemId)
  1163. {
  1164. #ifndef ALIGNMENT_MACHINE
  1165. // We don't need to do anything.
  1166. #else
  1167. if (pFtpItemId)
  1168. {
  1169. LocalFree(pFtpItemId);
  1170. }
  1171. #endif // ALIGNMENT_MACHINE
  1172. }
  1173. LPCUWSTR FtpItemID_GetDisplayNameReference(LPCITEMIDLIST pidl)
  1174. {
  1175. BYTE * pbData = (BYTE *) pidl;
  1176. LPCUWSTR pwzDisplayName = NULL;
  1177. DWORD cbWireName;
  1178. // Is the version OK?
  1179. // if (PIDL_VERSION_NUMBER > FtpPidl_GetVersion(pidl))
  1180. // return NULL;
  1181. pbData += SIZE_ITEMID_SIZEFIELD; // Skip over the size.
  1182. LPFTPIDLIST pFtpItemId = (LPFTPIDLIST) pbData;
  1183. cbWireName = LENGTH_AFTER_ALIGN((lstrlenA(pFtpItemId->szWireName) + 1), sizeof(DWORD));
  1184. pwzDisplayName = (LPCUWSTR) ((BYTE *)(&pFtpItemId->szWireName[0]) + cbWireName);
  1185. if (!EVAL(IS_VALID_DIRORFILE_ITEMID(pFtpItemId->dwIDType))) // If any other bits are sit, it's invalid.
  1186. pwzDisplayName = NULL;
  1187. return pwzDisplayName;
  1188. }
  1189. DWORD FtpItemID_GetTypeID(LPCITEMIDLIST pidl)
  1190. {
  1191. LPFTPIDLIST pFtpItemId = FtpItemID_GetDataThunk(pidl);
  1192. DWORD dwType = (pFtpItemId ? pFtpItemId->dwIDType : 0);
  1193. FtpItemID_FreeThunk(pFtpItemId);
  1194. return dwType;
  1195. }
  1196. void FtpItemID_SetTypeID(LPITEMIDLIST pidl, DWORD dwNewTypeID)
  1197. {
  1198. LPFTPIDLIST pFtpItemId = FtpItemID_GetDataInternal(pidl);
  1199. if (EVAL(pFtpItemId))
  1200. pFtpItemId->dwIDType = dwNewTypeID;
  1201. }
  1202. /****************************************************\
  1203. FUNCTION: FtpItemID_Alloc
  1204. DESCRIPTION:
  1205. We are passed a pointer to a FTPIDLIST data
  1206. structure and our goal is to create a ItemID from
  1207. it. This mainly includes making it only big enough
  1208. for the current string(s).
  1209. \****************************************************/
  1210. HRESULT FtpItemID_Alloc(LPFTPIDLIST pfi, LPITEMIDLIST * ppidl)
  1211. {
  1212. HRESULT hr;
  1213. WORD cbTotal;
  1214. WORD cbDataFirst;
  1215. WORD cbData;
  1216. BYTE * pbMemory;
  1217. DWORD cchSizeOfName = lstrlenA(pfi->szWireName);
  1218. DWORD cchSizeOfDispName = ualstrlenW(pfi->wzDisplayName);
  1219. ASSERT(pfi && ppidl);
  1220. // Find lenght of FTPIDLIST struct if the szName member only needed enought room
  1221. // for the string, not the full MAX_PATH.
  1222. // Size EQUALS: (Everything in the struct) - (the 2 full statusly sized strings) + (the 2 packed strings + alignment)
  1223. cbDataFirst = (WORD)((sizeof(*pfi) - sizeof(pfi->szWireName) - sizeof(pfi->wzDisplayName)) + LENGTH_AFTER_ALIGN(cchSizeOfName + 1, sizeof(DWORD)) - sizeof(DWORD));
  1224. cbData = cbDataFirst + (WORD) LENGTH_AFTER_ALIGN((cchSizeOfDispName + 1) * sizeof(WCHAR), sizeof(DWORD));
  1225. ASSERT((cbData % sizeof(DWORD)) == 0); // Verify it's DWORD aligned.
  1226. cbTotal = (SIZE_ITEMID_SIZEFIELD + cbData + SIZE_ITEMID_TERMINATOR);
  1227. pbMemory = (BYTE *) CoTaskMemAlloc(cbTotal);
  1228. if (pbMemory)
  1229. {
  1230. USHORT * pIDSize = (USHORT *)pbMemory;
  1231. BYTE * pbData = (pbMemory + SIZE_ITEMID_SIZEFIELD); // the Data starts at the second DWORD.
  1232. USHORT * pIDTerminator = (USHORT *)(pbMemory + SIZE_ITEMID_SIZEFIELD + cbData);
  1233. pIDSize[0] = (cbTotal - SIZE_ITEMID_TERMINATOR); // Set the size of the ItemID (including the next ItemID as terminator)
  1234. ASSERT(cbData <= sizeof(*pfi)); // Don't let me copy too much.
  1235. CopyMemory(pbData, pfi, cbDataFirst);
  1236. CopyMemory((pbData + cbDataFirst), &(pfi->wzDisplayName), ((cchSizeOfDispName + 1) * sizeof(WCHAR)));
  1237. pIDTerminator[0] = 0; // Terminate the next ID.
  1238. // TraceMsg(TF_FTPURL_UTILS, "FtpItemID_Alloc(\"%ls\") dwIDType=%#08lx, dwAttributes=%#08lx", pfi->wzDisplayName, pfi->dwIDType, pfi->dwAttributes);
  1239. }
  1240. *ppidl = (LPITEMIDLIST) pbMemory;
  1241. hr = pbMemory ? S_OK : E_OUTOFMEMORY;
  1242. ASSERT(IsValidPIDL(*ppidl));
  1243. ASSERT_POINTER_MATCHES_HRESULT(*ppidl, hr);
  1244. return hr;
  1245. }
  1246. /*****************************************************************************\
  1247. FUNCTION: FtpItemID_CreateReal
  1248. DESCRIPTION:
  1249. Cook up a pidl based on a WIN32_FIND_DATA.
  1250. The cFileName field is itself MAX_PATH characters long,
  1251. so its length cannot possibly exceed MAX_PATH...
  1252. \*****************************************************************************/
  1253. HRESULT FtpItemID_CreateReal(const LPFTP_FIND_DATA pwfd, LPCWSTR pwzDisplayName, LPITEMIDLIST * ppidl)
  1254. {
  1255. HRESULT hr;
  1256. FTPIDLIST fi = {0};
  1257. // Fill in fi.
  1258. fi.dwIDType = (IDTYPE_ISVALID | IDVALID_FILESIZE | IDVALID_MOD_DATE);
  1259. fi.uliFileSize.LowPart = pwfd->nFileSizeLow;
  1260. fi.uliFileSize.HighPart = pwfd->nFileSizeHigh;
  1261. fi.ftModified = pwfd->ftLastWriteTime;
  1262. fi.dwAttributes = pwfd->dwFileAttributes;
  1263. fi.dwUNIXPermission = pwfd->dwReserved0; // Set by WININET
  1264. StrCpyNA(fi.szWireName, pwfd->cFileName, ARRAYSIZE(fi.szWireName));
  1265. StrCpyN(fi.wzDisplayName, pwzDisplayName, ARRAYSIZE(fi.wzDisplayName));
  1266. if (pwfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  1267. fi.dwIDType |= IDTYPE_DIR;
  1268. else
  1269. fi.dwIDType |= IDTYPE_FILE;
  1270. hr = FtpItemID_Alloc(&fi, ppidl);
  1271. ASSERT(IsValidPIDL(*ppidl));
  1272. return hr;
  1273. }
  1274. /****************************************************\
  1275. FUNCTION: FtpItemID_CreateFake
  1276. DESCRIPTION:
  1277. Create a ItemID but we are only setting the
  1278. name. We don't know the true file attributes,
  1279. file size, or modification date yet because
  1280. we haven't touched the server yet. If we did,
  1281. we would use the returned WIN32_FIND_DATA struct
  1282. to create the ItemID by using FtpItemID_CreateReal().
  1283. \****************************************************/
  1284. HRESULT FtpItemID_CreateFake(LPCWSTR pwzDisplayName, LPCWIRESTR pwWireName, BOOL fTypeKnown, BOOL fIsFile, BOOL fIsFragment, LPITEMIDLIST * ppidl)
  1285. {
  1286. HRESULT hr;
  1287. DWORD dwType = IDTYPE_ISVALID;
  1288. FTPIDLIST fi = {0};
  1289. // Is it unknown?
  1290. if (!fTypeKnown)
  1291. {
  1292. // HACK: We will assume everything w/o a file extension is a Dir
  1293. // and everything w/an extension is a file.
  1294. fTypeKnown = TRUE;
  1295. fIsFile = (!pwzDisplayName || (0 == *PathFindExtension(pwzDisplayName))) ? FALSE : TRUE;
  1296. }
  1297. if (fTypeKnown)
  1298. {
  1299. if (fIsFile)
  1300. dwType |= IDTYPE_FILE;
  1301. else if (fIsFragment)
  1302. dwType |= IDTYPE_FRAGMENT;
  1303. else
  1304. dwType |= IDTYPE_DIR;
  1305. }
  1306. else
  1307. {
  1308. // You need to know if it's a fragment because there is no
  1309. // heuristic to find out.
  1310. ASSERT(!fIsFragment);
  1311. dwType |= IDTYPE_FILEORDIR;
  1312. }
  1313. fi.dwIDType = dwType;
  1314. fi.dwAttributes = (fIsFile ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_DIRECTORY);
  1315. fi.uliFileSize.QuadPart = 0;
  1316. StrCpyNW(fi.wzDisplayName, pwzDisplayName, ARRAYSIZE(fi.wzDisplayName));
  1317. StrCpyNA(fi.szWireName, pwWireName, ARRAYSIZE(fi.szWireName));
  1318. hr = FtpItemID_Alloc(&fi, ppidl);
  1319. ASSERT(IsValidPIDL(*ppidl));
  1320. ASSERT_POINTER_MATCHES_HRESULT(*ppidl, hr);
  1321. return hr;
  1322. }
  1323. /*****************************************************************************\
  1324. FUNCTION: FtpItemID_SetName
  1325. DESCRIPTION:
  1326. The user chose a new name for the ftp file or dir (in unicode). We
  1327. now need to create the name in wire bytes and we will use the original
  1328. wire byte name to decide how to do that (from pidl).
  1329. \*****************************************************************************/
  1330. HRESULT FtpItemID_CreateWithNewName(LPCITEMIDLIST pidl, LPCWSTR pwzDisplayName, LPCWIRESTR pwWireName, LPITEMIDLIST * ppidlOut)
  1331. {
  1332. HRESULT hr = E_FAIL;
  1333. FTPIDLIST fi;
  1334. const FTPIDLIST UNALIGNED * pfi = FtpItemID_GetDataThunk(pidl);
  1335. if (pfi)
  1336. {
  1337. CWireEncoding cWireEncoding;
  1338. CopyMemory(&fi, pfi, sizeof(FTPIDLIST) - sizeof(fi.szWireName) - sizeof(fi.wzDisplayName));
  1339. StrCpyNW(fi.wzDisplayName, pwzDisplayName, ARRAYSIZE(fi.wzDisplayName));
  1340. StrCpyNA(fi.szWireName, pwWireName, ARRAYSIZE(fi.szWireName));
  1341. hr = FtpItemID_Alloc(&fi, ppidlOut);
  1342. ASSERT(IsValidPIDL(*ppidlOut));
  1343. FtpItemID_FreeThunk((FTPIDLIST UNALIGNED *) pfi);
  1344. }
  1345. return hr;
  1346. }
  1347. HRESULT Private_GetFileInfo(SHFILEINFO *psfi, DWORD rgf, LPCTSTR pszName, DWORD dwFileAttributes)
  1348. {
  1349. HRESULT hr = E_FAIL;
  1350. if (SHGetFileInfo(pszName, dwFileAttributes, psfi, sizeof(*psfi), rgf | SHGFI_USEFILEATTRIBUTES))
  1351. hr = S_OK;
  1352. return hr;
  1353. }
  1354. /*****************************************************************************\
  1355. FUNCTION: FtpPidl_GetFileInfo
  1356. DESCRIPTION:
  1357. _UNDOCUMENTED_: We strip the Hidden and System bits so
  1358. that SHGetFileInfo won't think that we're passing something
  1359. that might be a junction.
  1360. We also force the SHGFI_USEFILEATTRIBUTES bit to remind the shell
  1361. that this isn't a file.
  1362. \*****************************************************************************/
  1363. HRESULT FtpPidl_GetFileInfo(LPCITEMIDLIST pidl, SHFILEINFO *psfi, DWORD rgf)
  1364. {
  1365. HRESULT hr = E_FAIL;
  1366. TCHAR szDisplayName[MAX_PATH];
  1367. psfi->iIcon = 0;
  1368. psfi->hIcon = NULL;
  1369. psfi->dwAttributes = 0;
  1370. psfi->szDisplayName[0] = 0;
  1371. psfi->szTypeName[0] = 0;
  1372. ASSERT(IsValidPIDL(pidl));
  1373. if (FtpID_IsServerItemID(pidl))
  1374. {
  1375. FtpServerID_GetServer(pidl, szDisplayName, ARRAYSIZE(szDisplayName));
  1376. hr = Private_GetFileInfo(psfi, rgf, szDisplayName, FILE_ATTRIBUTE_DIRECTORY);
  1377. if (psfi->hIcon)
  1378. DestroyIcon(psfi->hIcon);
  1379. psfi->hIcon = LoadIcon(HINST_THISDLL, MAKEINTRESOURCE(IDI_FTPFOLDER));
  1380. ASSERT(psfi->hIcon);
  1381. // Now replace the type (szTypeName) with "FTP Server" because
  1382. // it could go in the Properties dialog
  1383. EVAL(LoadString(HINST_THISDLL, IDS_ITEMTYPE_SERVER, psfi->szTypeName, ARRAYSIZE(psfi->szTypeName)));
  1384. }
  1385. else
  1386. {
  1387. LPFTPIDLIST pfi = FtpItemID_GetDataThunk(pidl);
  1388. if (pfi)
  1389. {
  1390. FtpItemID_GetDisplayName(pidl, szDisplayName, ARRAYSIZE(szDisplayName));
  1391. hr = Private_GetFileInfo(psfi, rgf, szDisplayName, (pfi->dwAttributes & ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)));
  1392. FtpItemID_FreeThunk(pfi);
  1393. }
  1394. }
  1395. return hr;
  1396. }
  1397. HRESULT FtpPidl_GetFileType(LPCITEMIDLIST pidl, LPTSTR pszType, DWORD cchSize)
  1398. {
  1399. SHFILEINFO sfi;
  1400. HRESULT hr;
  1401. ASSERT(IsValidPIDL(pidl));
  1402. hr = FtpPidl_GetFileInfo(pidl, &sfi, SHGFI_TYPENAME);
  1403. if (SUCCEEDED(hr))
  1404. {
  1405. StrCpyN(pszType, sfi.szTypeName, cchSize);
  1406. if (sfi.hIcon)
  1407. DestroyIcon(sfi.hIcon);
  1408. }
  1409. return hr;
  1410. }
  1411. HRESULT FtpPidl_GetFileTypeStrRet(LPCITEMIDLIST pidl, LPSTRRET pstr)
  1412. {
  1413. WCHAR szType[MAX_URL_STRING];
  1414. HRESULT hr;
  1415. ASSERT(IsValidPIDL(pidl));
  1416. hr = FtpPidl_GetFileType(pidl, szType, ARRAYSIZE(szType));
  1417. if (EVAL(SUCCEEDED(hr)))
  1418. StringToStrRetW(szType, pstr);
  1419. return hr;
  1420. }
  1421. /*****************************************************************************\
  1422. FUNCTION: _FtpItemID_CompareOneID
  1423. DESCRIPTION:
  1424. ici - attribute (column) to compare
  1425. Note! that UNIX filenames are case-*sensitive*.
  1426. We make two passes on the name. If the names are different in other
  1427. than case, we return the result of that comparison. Otherwise,
  1428. we return the result of a case-sensitive comparison.
  1429. This algorithm ensures that the items sort themselves in a
  1430. case-insensitive way, with ties broken by a case-sensitive
  1431. comparison. This makes ftp folders act "mostly" like normal
  1432. folders.
  1433. _UNDOCUMENTED_: The documentation says that the ici parameter
  1434. is undefined and must be zero. In reality, it is the column
  1435. number (defined by IShellView) for which the comparison is to
  1436. be made.
  1437. \*****************************************************************************/
  1438. HRESULT _FtpItemID_CompareOneID(LPARAM ici, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwCompFlags)
  1439. {
  1440. int iRc = 0; // 0 means we don't know.
  1441. HRESULT hr = S_OK;
  1442. ASSERT(IsValidPIDL(pidl1));
  1443. ASSERT(IsValidPIDL(pidl2));
  1444. // Are they both the same type? (Both Dirs or both files?)
  1445. if (!(dwCompFlags & FCMP_GROUPDIRS) || (!FtpPidl_IsDirectory(pidl1, FALSE) == !FtpPidl_IsDirectory(pidl2, FALSE)))
  1446. {
  1447. switch (ici & SHCIDS_COLUMNMASK)
  1448. {
  1449. case COL_NAME:
  1450. {
  1451. // Yes they are the same, so we will key off the name...
  1452. WIRECHAR szName1[MAX_PATH];
  1453. WIRECHAR szName2[MAX_PATH];
  1454. szName1[0] = TEXT('\0');
  1455. szName2[0] = TEXT('\0');
  1456. FtpPidl_GetWireName(pidl1, szName1, ARRAYSIZE(szName1));
  1457. FtpPidl_GetWireName(pidl2, szName2, ARRAYSIZE(szName2));
  1458. iRc = StrCmpIA(szName1, szName2);
  1459. if (0 == iRc)
  1460. {
  1461. if (!(dwCompFlags & FCMP_CASEINSENSE))
  1462. iRc = StrCmpA(szName1, szName2);
  1463. /*
  1464. // They are the same name, so now lets check on the username
  1465. // if they are Server IDs.
  1466. if ((0 == iRc) && (FtpID_IsServerItemID(pidl1)))
  1467. {
  1468. FtpPidl_GetUserName(pidl1, szName1, ARRAYSIZE(szName1));
  1469. FtpPidl_GetUserName(pidl2, szName2, ARRAYSIZE(szName2));
  1470. iRc = StrCmp(szName1, szName2);
  1471. }
  1472. */
  1473. }
  1474. }
  1475. break;
  1476. case COL_SIZE:
  1477. if (FtpPidl_GetFileSize(pidl1) < FtpPidl_GetFileSize(pidl2))
  1478. iRc = -1;
  1479. else if (FtpPidl_GetFileSize(pidl1) > FtpPidl_GetFileSize(pidl2))
  1480. iRc = +1;
  1481. else
  1482. iRc = 0; // I don't know
  1483. break;
  1484. case COL_TYPE:
  1485. if (!FtpID_IsServerItemID(pidl1) && !FtpID_IsServerItemID(pidl2))
  1486. {
  1487. TCHAR szType1[MAX_PATH];
  1488. hr = FtpPidl_GetFileType(pidl1, szType1, ARRAYSIZE(szType1));
  1489. if (EVAL(SUCCEEDED(hr)))
  1490. {
  1491. TCHAR szType2[MAX_PATH];
  1492. hr = FtpPidl_GetFileType(pidl2, szType2, ARRAYSIZE(szType2));
  1493. if (EVAL(SUCCEEDED(hr)))
  1494. iRc = StrCmpI(szType1, szType2);
  1495. }
  1496. }
  1497. break;
  1498. case COL_MODIFIED:
  1499. {
  1500. FILETIME ft1 = FtpPidl_GetFileTime(pidl1);
  1501. FILETIME ft2 = FtpPidl_GetFileTime(pidl2);
  1502. iRc = CompareFileTime(&ft1, &ft2);
  1503. }
  1504. break;
  1505. default:
  1506. hr = E_NOTIMPL;
  1507. break;
  1508. }
  1509. }
  1510. else
  1511. {
  1512. // No they are different. We want the Folder to always come first.
  1513. // This doesn't seam right, but it forces folders to bubble to the top
  1514. // in the most frequent case and it matches DefView's Behavior.
  1515. if (FtpPidl_IsDirectory(pidl1, FALSE))
  1516. iRc = -1;
  1517. else
  1518. iRc = 1;
  1519. }
  1520. if (S_OK == hr)
  1521. hr = HRESULT_FROM_SUCCESS_VALUE(iRc); // encode the sort value in the return code.
  1522. return hr;
  1523. }
  1524. /*****************************************************************************\
  1525. FUNCTION: FtpItemID_CompareIDs
  1526. DESCRIPTION:
  1527. ici - attribute (column) to compare
  1528. Note! that we rely on the fact that IShellFolders are
  1529. uniform; we do not need to bind to the shell folder in
  1530. order to compare its sub-itemids.
  1531. _UNDOCUMENTED_: The documentation does not say whether or not
  1532. complex pidls can be received. In fact, they can.
  1533. The reason why the shell asks you to handle complex pidls
  1534. is that you can often short-circuit the comparison by walking
  1535. the ID list directly. (Formally speaking, you need to bind
  1536. to each ID and then call yourself recursively. But if your
  1537. pidls are uniform, you can just use a loop like the one below.)
  1538. \*****************************************************************************/
  1539. HRESULT FtpItemID_CompareIDs(LPARAM ici, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwCompFlags)
  1540. {
  1541. HRESULT hr;
  1542. if (!pidl1 || ILIsEmpty(pidl1))
  1543. {
  1544. if (!pidl2 || ILIsEmpty(pidl2))
  1545. hr = HRESULT_FROM_SUCCESS_VALUE(0); // Both ID lists are empty
  1546. else
  1547. hr = HRESULT_FROM_SUCCESS_VALUE(-1); // pidl1 is empty, pidl2 is nonempty
  1548. }
  1549. else
  1550. {
  1551. if (!pidl2 || ILIsEmpty(pidl2))
  1552. hr = HRESULT_FROM_SUCCESS_VALUE(1); // pidl1 is nonempty, pidl2 is empty
  1553. else
  1554. {
  1555. ASSERT(IsValidPIDL(pidl1));
  1556. ASSERT(IsValidPIDL(pidl2));
  1557. hr = _FtpItemID_CompareOneID(ici, pidl1, pidl2, dwCompFlags); // both are nonempty
  1558. }
  1559. }
  1560. // If this level of ItemsIDs are equal, then we will compare the next
  1561. // level of ItemIDs
  1562. if ((hr == HRESULT_FROM_SUCCESS_VALUE(0)) && pidl1 && !ILIsEmpty(pidl1))
  1563. hr = FtpItemID_CompareIDs(ici, _ILNext(pidl1), _ILNext(pidl2), dwCompFlags);
  1564. return hr;
  1565. }
  1566. int FtpItemID_CompareIDsInt(LPARAM ici, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, DWORD dwCompFlags)
  1567. {
  1568. HRESULT hr = FtpItemID_CompareIDs(ici, pidl1, pidl2, dwCompFlags);
  1569. int nResult = (DWORD)(short)hr;
  1570. return nResult;
  1571. }
  1572. DWORD FtpItemID_GetAttributes(LPCITEMIDLIST pidl)
  1573. {
  1574. DWORD dwAttributes = 0;
  1575. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
  1576. if (pFtpIDList)
  1577. {
  1578. dwAttributes = pFtpIDList->dwAttributes;
  1579. FtpItemID_FreeThunk(pFtpIDList);
  1580. }
  1581. return dwAttributes;
  1582. }
  1583. HRESULT FtpItemID_SetAttributes(LPCITEMIDLIST pidl, DWORD dwAttribs)
  1584. {
  1585. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
  1586. if (!pFtpIDList)
  1587. return E_INVALIDARG;
  1588. pFtpIDList->dwAttributes = dwAttribs;
  1589. return S_OK;
  1590. }
  1591. DWORD FtpItemID_GetUNIXPermissions(LPCITEMIDLIST pidl)
  1592. {
  1593. DWORD dwPermissions = 0;
  1594. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
  1595. if (pFtpIDList)
  1596. {
  1597. dwPermissions = pFtpIDList->dwUNIXPermission;
  1598. FtpItemID_FreeThunk(pFtpIDList);
  1599. }
  1600. return dwPermissions;
  1601. }
  1602. HRESULT FtpItemID_SetUNIXPermissions(LPCITEMIDLIST pidl, DWORD dwUNIXPermission)
  1603. {
  1604. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
  1605. if (!pFtpIDList)
  1606. return E_INVALIDARG;
  1607. pFtpIDList->dwUNIXPermission = dwUNIXPermission;
  1608. return S_OK;
  1609. }
  1610. DWORD FtpItemID_GetCompatFlags(LPCITEMIDLIST pidl)
  1611. {
  1612. DWORD dwCompatFlags = 0;
  1613. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
  1614. if (pFtpIDList)
  1615. {
  1616. dwCompatFlags = pFtpIDList->dwCompatFlags;
  1617. FtpItemID_FreeThunk(pFtpIDList);
  1618. }
  1619. return dwCompatFlags;
  1620. }
  1621. HRESULT FtpItemID_SetCompatFlags(LPCITEMIDLIST pidl, DWORD dwCompatFlags)
  1622. {
  1623. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
  1624. if (!pFtpIDList)
  1625. return E_INVALIDARG;
  1626. pFtpIDList->dwCompatFlags = dwCompatFlags;
  1627. return S_OK;
  1628. }
  1629. ULONGLONG FtpItemID_GetFileSize(LPCITEMIDLIST pidl)
  1630. {
  1631. ULONGLONG uliFileSize = 0;
  1632. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
  1633. if (pFtpIDList)
  1634. {
  1635. ASSERT(IsFlagSet(pFtpIDList->dwIDType, IDVALID_FILESIZE));
  1636. uliFileSize = pFtpIDList->uliFileSize.QuadPart;
  1637. FtpItemID_FreeThunk(pFtpIDList);
  1638. }
  1639. return uliFileSize;
  1640. }
  1641. void FtpItemID_SetFileSize(LPCITEMIDLIST pidl, ULARGE_INTEGER uliFileSize)
  1642. {
  1643. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
  1644. if (!pFtpIDList)
  1645. return;
  1646. pFtpIDList->uliFileSize = uliFileSize;
  1647. pFtpIDList->dwIDType |= IDVALID_FILESIZE;
  1648. }
  1649. DWORD FtpItemID_GetFileSizeLo(LPCITEMIDLIST pidl)
  1650. {
  1651. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
  1652. if (!pFtpIDList)
  1653. return NULL;
  1654. ASSERT(IsFlagSet(pFtpIDList->dwIDType, IDVALID_FILESIZE));
  1655. DWORD dwSize = pFtpIDList->uliFileSize.LowPart;
  1656. FtpItemID_FreeThunk(pFtpIDList);
  1657. return dwSize;
  1658. }
  1659. DWORD FtpItemID_GetFileSizeHi(LPCITEMIDLIST pidl)
  1660. {
  1661. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
  1662. if (!pFtpIDList)
  1663. return NULL;
  1664. ASSERT(IsFlagSet(pFtpIDList->dwIDType, IDVALID_FILESIZE));
  1665. DWORD dwSize = pFtpIDList->uliFileSize.HighPart;
  1666. FtpItemID_FreeThunk(pFtpIDList);
  1667. return dwSize;
  1668. }
  1669. // Return value is in Local Time Zone.
  1670. FILETIME FtpItemID_GetFileTime(LPCITEMIDLIST pidl)
  1671. {
  1672. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
  1673. FILETIME ftEmpty = {0};
  1674. if (pFtpIDList)
  1675. {
  1676. ASSERT(IsFlagSet(pFtpIDList->dwIDType, IDVALID_MOD_DATE));
  1677. ftEmpty = pFtpIDList->ftModified;
  1678. FtpItemID_FreeThunk(pFtpIDList);
  1679. }
  1680. return ftEmpty;
  1681. }
  1682. LPCWIRESTR FtpItemID_GetWireNameReference(LPCITEMIDLIST pidl)
  1683. {
  1684. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
  1685. if (!pFtpIDList || IS_FRAGMENT(pFtpIDList))
  1686. return NULL;
  1687. return pFtpIDList->szWireName;
  1688. }
  1689. HRESULT FtpItemID_GetDisplayName(LPCITEMIDLIST pidl, LPWSTR pwzName, DWORD cchSize)
  1690. {
  1691. HRESULT hr = S_OK;
  1692. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
  1693. if (pFtpIDList)
  1694. {
  1695. if (!IS_FRAGMENT(pFtpIDList))
  1696. {
  1697. LPCUWSTR pszUnalignedName = FtpItemID_GetDisplayNameReference(pidl);
  1698. if (pszUnalignedName)
  1699. {
  1700. // The display name wasn't stored in v3
  1701. ualstrcpynW(pwzName, pszUnalignedName, cchSize);
  1702. }
  1703. }
  1704. else
  1705. {
  1706. pwzName[0] = TEXT('\0');
  1707. hr = E_FAIL;
  1708. }
  1709. FtpItemID_FreeThunk(pFtpIDList);
  1710. }
  1711. return hr;
  1712. }
  1713. HRESULT FtpItemID_GetWireName(LPCITEMIDLIST pidl, LPWIRESTR pszName, DWORD cchSize)
  1714. {
  1715. HRESULT hr = S_OK;
  1716. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
  1717. if (pFtpIDList && !IS_FRAGMENT(pFtpIDList))
  1718. StrCpyNA(pszName, pFtpIDList->szWireName, cchSize);
  1719. else
  1720. {
  1721. pszName[0] = TEXT('\0');
  1722. hr = E_FAIL;
  1723. }
  1724. return hr;
  1725. }
  1726. HRESULT FtpItemID_GetFragment(LPCITEMIDLIST pidl, LPWSTR pwzFragmentStr, DWORD cchSize)
  1727. {
  1728. HRESULT hr = E_FAIL;
  1729. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
  1730. if (pFtpIDList && IS_FRAGMENT(pFtpIDList))
  1731. {
  1732. LPCUWSTR pszUnalignedName = FtpItemID_GetDisplayNameReference(pidl);
  1733. if (pszUnalignedName)
  1734. {
  1735. // The display name wasn't stored in v3
  1736. ualstrcpynW(pwzFragmentStr, pszUnalignedName, cchSize);
  1737. hr = S_OK;
  1738. }
  1739. }
  1740. else
  1741. {
  1742. pwzFragmentStr[0] = TEXT('\0');
  1743. hr = E_FAIL;
  1744. }
  1745. return hr;
  1746. }
  1747. BOOL FtpItemID_IsFragment(LPCITEMIDLIST pidl)
  1748. {
  1749. BOOL fIsFrag = FALSE;
  1750. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
  1751. if (pFtpIDList && IS_FRAGMENT(pFtpIDList))
  1752. fIsFrag = TRUE;
  1753. return fIsFrag;
  1754. }
  1755. // fileTime In UTC
  1756. void FtpItemID_SetFileTime(LPCITEMIDLIST pidl, FILETIME fileTime)
  1757. {
  1758. FILETIME fileTimeLocal;
  1759. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataInternal(pidl);
  1760. if (!pFtpIDList)
  1761. return;
  1762. FileTimeToLocalFileTime(&fileTime, &fileTimeLocal);
  1763. pFtpIDList->ftModified = fileTimeLocal;
  1764. pFtpIDList->dwIDType |= IDVALID_MOD_DATE;
  1765. }
  1766. BOOL FtpItemID_IsDirectory(LPCITEMIDLIST pidl, BOOL fAssumeDirForUnknown)
  1767. {
  1768. LPFTPIDLIST pFtpIDList = FtpItemID_GetDataThunk(pidl);
  1769. if (!pFtpIDList)
  1770. return NULL;
  1771. BOOL fIsDir = (IsFlagSet(pFtpIDList->dwIDType, IDTYPE_DIR));
  1772. if (fAssumeDirForUnknown && (IDTYPE_FILEORDIR == pFtpIDList->dwIDType))
  1773. {
  1774. // TraceMsg(TF_FTPURL_UTILS, "FtpItemID_IsDirectory() IDTYPE_FILEORDIR is set, so we assume %s", (fAssumeDirForUnknown ? TEXT("DIR") : TEXT("FILE")));
  1775. fIsDir = TRUE;
  1776. }
  1777. else
  1778. {
  1779. // TraceMsg(TF_FTPURL_UTILS, "FtpItemID_IsDirectory() It is known to be a %s", (fIsDir ? TEXT("DIR") : TEXT("FILE")));
  1780. }
  1781. FtpItemID_FreeThunk(pFtpIDList);
  1782. return fIsDir;
  1783. }
  1784. /****************************************************\
  1785. Functions to work on an entire FTP PIDLs
  1786. \****************************************************/
  1787. #define SZ_ASCII_DOWNLOAD_TYPE TEXT("a")
  1788. #define SZ_BINARY_DOWNLOAD_TYPE TEXT("b")
  1789. HRESULT FtpPidl_GetDownloadTypeStr(LPCITEMIDLIST pidl, LPTSTR szDownloadType, DWORD cchTypeStrSize)
  1790. {
  1791. HRESULT hr = S_FALSE; // We may not have a type.
  1792. DWORD dwTypeID = FtpServerID_GetTypeID(pidl);
  1793. szDownloadType[0] = TEXT('\0');
  1794. if (IDVALID_DLTYPE & dwTypeID)
  1795. {
  1796. hr = S_OK;
  1797. StrCpyN(szDownloadType, SZ_FTP_URL_TYPE, cchTypeStrSize);
  1798. if (IDVALID_DL_ASCII & dwTypeID)
  1799. StrCatBuff(szDownloadType, SZ_ASCII_DOWNLOAD_TYPE, cchTypeStrSize);
  1800. else
  1801. StrCatBuff(szDownloadType, SZ_BINARY_DOWNLOAD_TYPE, cchTypeStrSize);
  1802. }
  1803. return hr;
  1804. }
  1805. DWORD FtpPidl_GetDownloadType(LPCITEMIDLIST pidl)
  1806. {
  1807. DWORD dwAttribs = FTP_TRANSFER_TYPE_UNKNOWN;
  1808. DWORD dwTypeID = FtpServerID_GetTypeID(pidl);
  1809. ASSERT(FtpID_IsServerItemID(pidl));
  1810. if (IDVALID_DLTYPE & dwTypeID)
  1811. {
  1812. if (IDVALID_DL_ASCII & dwTypeID)
  1813. dwAttribs = FTP_TRANSFER_TYPE_ASCII;
  1814. else
  1815. dwAttribs = FTP_TRANSFER_TYPE_BINARY;
  1816. }
  1817. return dwAttribs;
  1818. }
  1819. INTERNET_PORT FtpPidl_GetPortNum(LPCITEMIDLIST pidl)
  1820. {
  1821. ASSERT(FtpID_IsServerItemID(pidl));
  1822. return FtpServerID_GetPortNum(pidl);
  1823. }
  1824. BOOL FtpPidl_IsDirectory(LPCITEMIDLIST pidl, BOOL fAssumeDirForUnknown)
  1825. {
  1826. BOOL fIsDir = FALSE;
  1827. LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
  1828. if (!FtpID_IsServerItemID(pidlLast))
  1829. fIsDir = FtpItemID_IsDirectory(pidlLast, fAssumeDirForUnknown);
  1830. return fIsDir;
  1831. }
  1832. BOOL FtpPidl_IsAnonymous(LPCITEMIDLIST pidl)
  1833. {
  1834. BOOL fIsAnonymous = TRUE;
  1835. if (IDVALID_USERNAME & FtpServerID_GetTypeID(pidl))
  1836. fIsAnonymous = FALSE;
  1837. return fIsAnonymous;
  1838. }
  1839. HRESULT FtpPidl_GetServer(LPCITEMIDLIST pidl, LPTSTR pszServer, DWORD cchSize)
  1840. {
  1841. if (!FtpID_IsServerItemID(pidl)) // Will fail if we are handed a non-server ID.
  1842. return E_FAIL;
  1843. return FtpServerID_GetServer(pidl, pszServer, cchSize);
  1844. }
  1845. BOOL FtpPidl_IsDNSServerName(LPCITEMIDLIST pidl)
  1846. {
  1847. BOOL fIsDNSServer = FALSE;
  1848. TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
  1849. if (EVAL(SUCCEEDED(FtpPidl_GetServer(pidl, szServer, ARRAYSIZE(szServer)))))
  1850. fIsDNSServer = !IsIPAddressStr(szServer);
  1851. return fIsDNSServer;
  1852. }
  1853. HRESULT FtpPidl_GetUserName(LPCITEMIDLIST pidl, LPTSTR pszUserName, DWORD cchSize)
  1854. {
  1855. ASSERT(FtpID_IsServerItemID(pidl));
  1856. return FtpServerID_GetUserName(pidl, pszUserName, cchSize);
  1857. }
  1858. HRESULT FtpPidl_GetPassword(LPCITEMIDLIST pidl, LPTSTR pszPassword, DWORD cchSize, BOOL fIncludingHiddenPassword)
  1859. {
  1860. ASSERT(FtpID_IsServerItemID(pidl));
  1861. return FtpServerID_GetPassword(pidl, pszPassword, cchSize, fIncludingHiddenPassword);
  1862. }
  1863. ULONGLONG FtpPidl_GetFileSize(LPCITEMIDLIST pidl)
  1864. {
  1865. LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
  1866. ULONGLONG ullFileSize;
  1867. ullFileSize = 0;
  1868. if (!FtpID_IsServerItemID(pidlLast))
  1869. ullFileSize = FtpItemID_GetFileSize(pidlLast);
  1870. return ullFileSize;
  1871. }
  1872. HRESULT FtpPidl_SetFileSize(LPCITEMIDLIST pidl, DWORD dwSizeHigh, DWORD dwSizeLow)
  1873. {
  1874. HRESULT hr = E_INVALIDARG;
  1875. LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
  1876. if (!FtpID_IsServerItemID(pidlLast))
  1877. {
  1878. ULARGE_INTEGER uliFileSize;
  1879. uliFileSize.HighPart = dwSizeHigh;
  1880. uliFileSize.LowPart = dwSizeLow;
  1881. FtpItemID_SetFileSize(pidlLast, uliFileSize);
  1882. hr = S_OK;
  1883. }
  1884. return hr;
  1885. }
  1886. // Return value in UTC time.
  1887. FILETIME FtpPidl_GetFileTime(LPCITEMIDLIST pidl)
  1888. {
  1889. FILETIME fileTimeFTP = FtpPidl_GetFTPFileTime(pidl); // This is what servers will be.
  1890. FILETIME fileTime;
  1891. EVAL(LocalFileTimeToFileTime(&fileTimeFTP, &fileTime));
  1892. return fileTime;
  1893. }
  1894. // Return value is in Local Time Zone.
  1895. FILETIME FtpPidl_GetFTPFileTime(LPCITEMIDLIST pidl)
  1896. {
  1897. FILETIME fileTime = {0}; // This is what servers will be.
  1898. if (!FtpID_IsServerItemID(pidl))
  1899. fileTime = FtpItemID_GetFileTime(pidl);
  1900. return fileTime;
  1901. }
  1902. HRESULT FtpPidl_GetDisplayName(LPCITEMIDLIST pidl, LPWSTR pwzName, DWORD cchSize)
  1903. {
  1904. HRESULT hr = E_INVALIDARG;
  1905. if (pidl)
  1906. {
  1907. if (FtpID_IsServerItemID(pidl))
  1908. hr = FtpServerID_GetServer(pidl, pwzName, cchSize);
  1909. else
  1910. hr = FtpItemID_GetDisplayName(pidl, pwzName, cchSize);
  1911. }
  1912. return hr;
  1913. }
  1914. HRESULT FtpPidl_GetWireName(LPCITEMIDLIST pidl, LPWIRESTR pwName, DWORD cchSize)
  1915. {
  1916. HRESULT hr = E_INVALIDARG;
  1917. if (pidl)
  1918. {
  1919. if (FtpID_IsServerItemID(pidl))
  1920. {
  1921. WCHAR wzServerName[INTERNET_MAX_HOST_NAME_LENGTH];
  1922. // It's a good thing Server Names need to be in US ANSI
  1923. hr = FtpServerID_GetServer(pidl, wzServerName, ARRAYSIZE(wzServerName));
  1924. SHUnicodeToAnsi(wzServerName, pwName, cchSize);
  1925. }
  1926. else
  1927. hr = FtpItemID_GetWireName(pidl, pwName, cchSize);
  1928. }
  1929. return hr;
  1930. }
  1931. HRESULT FtpPidl_GetFragment(LPCITEMIDLIST pidl, LPTSTR pszFragment, DWORD cchSize)
  1932. {
  1933. HRESULT hr = E_INVALIDARG;
  1934. LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
  1935. if (!FtpID_IsServerItemID(pidlLast))
  1936. hr = FtpItemID_GetFragment(pidlLast, pszFragment, cchSize);
  1937. else
  1938. {
  1939. pszFragment[0] = 0;
  1940. }
  1941. return hr;
  1942. }
  1943. DWORD FtpPidl_GetAttributes(LPCITEMIDLIST pidl)
  1944. {
  1945. DWORD dwAttribs = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_TEMPORARY;
  1946. LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
  1947. if (!FtpID_IsServerItemID(pidlLast))
  1948. dwAttribs = FtpItemID_GetAttributes(pidlLast);
  1949. else
  1950. dwAttribs = FILE_ATTRIBUTE_DIRECTORY;
  1951. return dwAttribs;
  1952. }
  1953. HRESULT FtpPidl_SetAttributes(LPCITEMIDLIST pidl, DWORD dwAttribs)
  1954. {
  1955. HRESULT hr = E_INVALIDARG;
  1956. LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
  1957. if (!FtpID_IsServerItemID(pidlLast))
  1958. hr = FtpItemID_SetAttributes(pidlLast, dwAttribs);
  1959. return hr;
  1960. }
  1961. // ftTimeDate In UTC
  1962. HRESULT FtpPidl_SetFileTime(LPCITEMIDLIST pidl, FILETIME ftTimeDate)
  1963. {
  1964. HRESULT hr = E_INVALIDARG;
  1965. LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
  1966. if (!FtpID_IsServerItemID(pidlLast))
  1967. {
  1968. FtpItemID_SetFileTime(pidlLast, ftTimeDate);
  1969. hr = S_OK;
  1970. }
  1971. return hr;
  1972. }
  1973. /****************************************************\
  1974. FUNCTION: FtpPidl_GetFileWireName
  1975. DESCRIPTION:
  1976. Get the file name.
  1977. \****************************************************/
  1978. LPCWIRESTR FtpPidl_GetFileWireName(LPCITEMIDLIST pidl)
  1979. {
  1980. if (EVAL(!FtpID_IsServerItemID(pidl)) &&
  1981. !FtpItemID_IsFragment(pidl))
  1982. {
  1983. return FtpItemID_GetWireNameReference(pidl);
  1984. }
  1985. return NULL;
  1986. }
  1987. /****************************************************\
  1988. FUNCTION: FtpPidl_GetLastItemDisplayName
  1989. DESCRIPTION:
  1990. This will get the last item name, even if that
  1991. last item is a fragment.
  1992. \****************************************************/
  1993. HRESULT FtpPidl_GetLastItemDisplayName(LPCITEMIDLIST pidl, LPWSTR pwzName, DWORD cchSize)
  1994. {
  1995. HRESULT hr = E_FAIL;
  1996. LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
  1997. if (EVAL(!FtpID_IsServerItemID(pidlLast)) &&
  1998. FtpItemID_GetDisplayNameReference(pidlLast))
  1999. {
  2000. hr = FtpItemID_GetDisplayName(pidlLast, pwzName, cchSize);
  2001. }
  2002. return hr;
  2003. }
  2004. /****************************************************\
  2005. FUNCTION: FtpPidl_GetLastItemWireName
  2006. DESCRIPTION:
  2007. This will get the last item name, even if that
  2008. last item is a fragment.
  2009. \****************************************************/
  2010. LPCWIRESTR FtpPidl_GetLastItemWireName(LPCITEMIDLIST pidl)
  2011. {
  2012. LPCWIRESTR pszName = NULL;
  2013. if (pidl)
  2014. {
  2015. LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
  2016. if (FtpItemID_IsFragment(pidlLast) && (pidlLast != pidl))
  2017. {
  2018. // Oops, we went to far. Step back one.
  2019. LPCITEMIDLIST pidlFrag = pidlLast;
  2020. pidlLast = pidl; // Start back at the beginning.
  2021. while (!FtpItemID_IsEqual(_ILNext(pidlLast), pidlFrag))
  2022. {
  2023. if (ILIsEmpty(pidlLast))
  2024. return NULL; // Break infinite loop.
  2025. pidlLast = _ILNext(pidlLast);
  2026. }
  2027. }
  2028. pszName = FtpPidl_GetFileWireName(pidlLast);
  2029. }
  2030. return pszName;
  2031. }
  2032. /****************************************************\
  2033. FUNCTION: FtpPidl_GetLastFileDisplayName
  2034. DESCRIPTION:
  2035. This will get the last item name, even if that
  2036. last item is a fragment.
  2037. \****************************************************/
  2038. HRESULT FtpPidl_GetLastFileDisplayName(LPCITEMIDLIST pidl, LPWSTR pwzName, DWORD cchSize)
  2039. {
  2040. HRESULT hr = E_INVALIDARG;
  2041. LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
  2042. if (FtpItemID_IsFragment(pidlLast) && (pidlLast != pidl))
  2043. {
  2044. // Oops, we went to far. Step back one.
  2045. LPCITEMIDLIST pidlFrag = pidlLast;
  2046. pidlLast = pidl; // Start back at the beginning.
  2047. while (!FtpItemID_IsEqual(_ILNext(pidlLast), pidlFrag))
  2048. {
  2049. if (ILIsEmpty(pidlLast))
  2050. return NULL; // Break infinite loop.
  2051. pidlLast = _ILNext(pidlLast);
  2052. }
  2053. }
  2054. if (EVAL(!FtpID_IsServerItemID(pidlLast)))
  2055. {
  2056. LPCUWSTR pszUnalignedName = FtpItemID_GetDisplayNameReference(pidlLast);
  2057. if (pszUnalignedName)
  2058. {
  2059. ualstrcpynW(pwzName, pszUnalignedName, cchSize);
  2060. hr = S_OK;
  2061. }
  2062. }
  2063. return hr;
  2064. }
  2065. /****************************************************\
  2066. FUNCTION: FtpPidl_InsertVirtualRoot
  2067. DESCRIPTION:
  2068. This function will insert the virtual root path
  2069. (pidlVirtualRoot) into pidlFtpPath.
  2070. PARAMETERS:
  2071. pidlVirtualRoot: Does not have a ServerID
  2072. pidlFtpPath: Can have a ServerID
  2073. *ppidl: Will be pidlVirtualRoot with item ItemIDs from
  2074. pidlFtpPath behind it. (No ServerID)
  2075. \****************************************************/
  2076. HRESULT FtpPidl_InsertVirtualRoot(LPCITEMIDLIST pidlVirtualRoot, LPCITEMIDLIST pidlFtpPath, LPITEMIDLIST * ppidl)
  2077. {
  2078. HRESULT hr = E_OUTOFMEMORY;
  2079. if (FtpID_IsServerItemID(pidlFtpPath))
  2080. pidlFtpPath = _ILNext(pidlFtpPath);
  2081. *ppidl = ILCombine(pidlVirtualRoot, pidlFtpPath);
  2082. if (*ppidl)
  2083. hr = S_OK;
  2084. return hr;
  2085. }
  2086. DWORD FtpPidl_GetVersion(LPCITEMIDLIST pidl)
  2087. {
  2088. DWORD dwVersion = 0;
  2089. if (EVAL(FtpID_IsServerItemID(pidl)))
  2090. {
  2091. LPFTPSERVERIDLIST pFtpServerItemID = FtpServerID_GetDataSafeThunk(pidl);
  2092. if (pFtpServerItemID)
  2093. {
  2094. dwVersion = pFtpServerItemID->dwVersion;
  2095. FtpServerID_FreeThunk(pFtpServerItemID);
  2096. }
  2097. }
  2098. return dwVersion;
  2099. }
  2100. BOOL FtpPidl_IsValid(LPCITEMIDLIST pidl)
  2101. {
  2102. if (!EVAL(IsValidPIDL(pidl)))
  2103. return FALSE;
  2104. return TRUE;
  2105. }
  2106. BOOL FtpPidl_IsValidFull(LPCITEMIDLIST pidl)
  2107. {
  2108. if (!EVAL(FtpID_IsServerItemID(pidl)))
  2109. return FALSE;
  2110. if (!EVAL(FtpPidl_IsValid(pidl)))
  2111. return FALSE;
  2112. // We consider anything older than PIDL_VERSION_NUMBER_UPGRADE
  2113. // to be invalid.
  2114. return ((PIDL_VERSION_NUMBER_UPGRADE - 1) < FtpPidl_GetVersion(pidl));
  2115. }
  2116. BOOL FtpPidl_IsValidRelative(LPCITEMIDLIST pidl)
  2117. {
  2118. if (!EVAL(!FtpID_IsServerItemID(pidl)))
  2119. return FALSE; // This is a server item id which is not relative.
  2120. return FtpPidl_IsValid(pidl);
  2121. }
  2122. LPITEMIDLIST ILCloneFirstItemID(LPITEMIDLIST pidl)
  2123. {
  2124. LPITEMIDLIST pidlCopy = ILClone(pidl);
  2125. if (pidlCopy && pidlCopy->mkid.cb)
  2126. {
  2127. LPITEMIDLIST pSecondID = (LPITEMIDLIST)_ILNext(pidlCopy);
  2128. ASSERT(pSecondID);
  2129. // Remove the last one
  2130. pSecondID->mkid.cb = 0; // null-terminator
  2131. }
  2132. return pidlCopy;
  2133. }
  2134. BOOL FtpPidl_HasPath(LPCITEMIDLIST pidl)
  2135. {
  2136. BOOL fResult = TRUE;
  2137. if (!FtpPidl_IsValid(pidl) || !EVAL(FtpID_IsServerItemID(pidl)))
  2138. return FALSE;
  2139. if (!ILIsEmpty(pidl) && ILIsEmpty(_ILNext(pidl)))
  2140. fResult = FALSE;
  2141. return fResult;
  2142. }
  2143. HRESULT FtpPidl_SetFileItemType(LPITEMIDLIST pidl, BOOL fIsDir)
  2144. {
  2145. LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
  2146. HRESULT hr = E_INVALIDARG;
  2147. if (EVAL(FtpPidl_IsValid(pidl)) && EVAL(!FtpID_IsServerItemID(pidlLast)))
  2148. {
  2149. DWORD dwIDType = FtpItemID_GetTypeID(pidlLast);
  2150. ClearFlag(dwIDType, (IDTYPE_FILEORDIR | IDTYPE_DIR | IDTYPE_FILE));
  2151. SetFlag(dwIDType, (fIsDir ? IDTYPE_DIR : IDTYPE_FILE));
  2152. FtpItemID_SetTypeID((LPITEMIDLIST) pidlLast, dwIDType);
  2153. hr = S_OK;
  2154. }
  2155. return hr;
  2156. }
  2157. BOOL IsFtpPidlQuestionable(LPCITEMIDLIST pidl)
  2158. {
  2159. BOOL fIsQuestionable = FALSE;
  2160. LPCITEMIDLIST pidlLast = ILGetLastID(pidl);
  2161. // Is it a Server Pidl? (All Server pidls aren't questionable)
  2162. if (FtpPidl_IsValid(pidl) && !FtpID_IsServerItemID(pidlLast))
  2163. {
  2164. // No, so it might be questionable.
  2165. // Does it have "File or Dir" bit set?
  2166. if (IsFlagSet(FtpItemID_GetTypeID(pidlLast), IDTYPE_FILEORDIR))
  2167. fIsQuestionable = TRUE;
  2168. }
  2169. return fIsQuestionable;
  2170. }
  2171. LPITEMIDLIST FtpCloneServerID(LPCITEMIDLIST pidl)
  2172. {
  2173. LPITEMIDLIST pidlResult = NULL;
  2174. if (EVAL(FtpID_IsServerItemID(pidl)))
  2175. {
  2176. pidlResult = ILClone(pidl);
  2177. while (!ILIsEmpty(_ILNext(pidlResult)))
  2178. ILRemoveLastID(pidlResult);
  2179. }
  2180. return pidlResult;
  2181. }
  2182. /*****************************************************************************\
  2183. FUNCTION: FtpPidl_ReplacePath
  2184. DESCRIPTION:
  2185. This function will fill in *ppidlOut with a pidl that contains the
  2186. FtpServerID from pidlServer and the FtpItemIDs from pidlFtpPath.
  2187. \*****************************************************************************/
  2188. HRESULT FtpPidl_ReplacePath(LPCITEMIDLIST pidlServer, LPCITEMIDLIST pidlFtpPath, LPITEMIDLIST * ppidlOut)
  2189. {
  2190. HRESULT hr = E_INVALIDARG;
  2191. *ppidlOut = NULL;
  2192. if (EVAL(FtpID_IsServerItemID(pidlServer)))
  2193. {
  2194. LPITEMIDLIST pidlServerOnly = FtpCloneServerID(pidlServer);
  2195. if (pidlServerOnly)
  2196. {
  2197. if (FtpID_IsServerItemID(pidlFtpPath))
  2198. pidlFtpPath = _ILNext(pidlFtpPath);
  2199. *ppidlOut = ILCombine(pidlServerOnly, pidlFtpPath);
  2200. if (*ppidlOut)
  2201. hr = S_OK;
  2202. else
  2203. hr = E_FAIL;
  2204. ILFree(pidlServerOnly);
  2205. }
  2206. }
  2207. ASSERT_POINTER_MATCHES_HRESULT(*ppidlOut, hr);
  2208. return hr;
  2209. }
  2210. BOOL FtpItemID_IsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  2211. {
  2212. // Don't repeat recursively.
  2213. return (S_OK == _FtpItemID_CompareOneID(COL_NAME, pidl1, pidl2, FALSE));
  2214. }
  2215. BOOL FtpPidl_IsPathEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  2216. {
  2217. // This works recursively.
  2218. return ((0 == FtpItemID_CompareIDsInt(COL_NAME, pidl1, pidl2, FCMP_NORMAL)) ? TRUE : FALSE);
  2219. }
  2220. // is pidlChild a child of pidlParent
  2221. BOOL FtpItemID_IsParent(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlChild)
  2222. {
  2223. BOOL fIsChild = TRUE;
  2224. if (pidlChild)
  2225. {
  2226. LPITEMIDLIST pidl1Iterate = (LPITEMIDLIST) pidlParent;
  2227. LPITEMIDLIST pidl2Iterate = (LPITEMIDLIST) pidlChild;
  2228. ASSERT(!FtpID_IsServerItemID(pidl1Iterate) && pidlParent && !FtpID_IsServerItemID(pidl2Iterate));
  2229. // Let's see if pidl starts off with
  2230. while (fIsChild && pidl1Iterate && !ILIsEmpty(pidl1Iterate) &&
  2231. pidl2Iterate && !ILIsEmpty(pidl2Iterate) &&
  2232. FtpItemID_IsEqual(pidl1Iterate, pidl2Iterate))
  2233. {
  2234. fIsChild = FtpItemID_IsEqual(pidl1Iterate, pidl2Iterate);
  2235. pidl1Iterate = _ILNext(pidl1Iterate);
  2236. pidl2Iterate = _ILNext(pidl2Iterate);
  2237. }
  2238. if (!(ILIsEmpty(pidl1Iterate) && !ILIsEmpty(pidl2Iterate)))
  2239. fIsChild = FALSE;
  2240. }
  2241. else
  2242. fIsChild = FALSE;
  2243. return fIsChild;
  2244. }
  2245. // is pidlChild a child of pidlParent, so all the itemIDs in
  2246. // pidlParent are in pidlChild, but pidlChild has more.
  2247. // This will return a pointer to those itemIDs.
  2248. LPCITEMIDLIST FtpItemID_FindDifference(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlChild)
  2249. {
  2250. LPCITEMIDLIST pidlDiff = (LPITEMIDLIST) pidlChild;
  2251. if (pidlChild)
  2252. {
  2253. LPITEMIDLIST pidl1Iterate = (LPITEMIDLIST) pidlParent;
  2254. if (FtpID_IsServerItemID(pidl1Iterate))
  2255. pidl1Iterate = _ILNext(pidl1Iterate);
  2256. if (FtpID_IsServerItemID(pidlDiff))
  2257. pidlDiff = _ILNext(pidlDiff);
  2258. // Let's see if pidl starts off with
  2259. while (pidl1Iterate && !ILIsEmpty(pidl1Iterate) &&
  2260. pidlDiff && !ILIsEmpty(pidlDiff) &&
  2261. FtpItemID_IsEqual(pidl1Iterate, pidlDiff))
  2262. {
  2263. pidlDiff = _ILNext(pidlDiff);
  2264. pidl1Iterate = _ILNext(pidl1Iterate);
  2265. }
  2266. }
  2267. return pidlDiff;
  2268. }