Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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