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.

627 lines
20 KiB

  1. /*****************************************************************************
  2. *
  3. * ftppl.cpp - FTP LPITEMIDLIST List object
  4. *
  5. *****************************************************************************/
  6. #include "priv.h"
  7. #include "ftppl.h"
  8. #include "ftpurl.h"
  9. typedef struct tagINETENUM
  10. {
  11. HINTERNET hint;
  12. BOOL * pfValidhinst;
  13. LPVOID pvData;
  14. LPFNPROCESSITEMCB pfnProcessItemCB;
  15. LPCITEMIDLIST pidlRoot;
  16. HRESULT hr;
  17. } INETENUM;
  18. /*****************************************************************************\
  19. FUNCTION: RecursiveEnum
  20. DESCRIPTION:
  21. This function will pack the parameters needed during the enum.
  22. \*****************************************************************************/
  23. HRESULT CFtpPidlList::RecursiveEnum(LPCITEMIDLIST pidlRoot, LPFNPROCESSITEMCB pfnProcessItemCB, HINTERNET hint, LPVOID pvData)
  24. {
  25. INETENUM inetEnum = {hint, NULL, pvData, pfnProcessItemCB, pidlRoot, S_OK};
  26. Enum(RecursiveProcessPidl, (LPVOID) &inetEnum);
  27. return inetEnum.hr;
  28. }
  29. // lParam can be: 0 == do a case sensitive search. 1 == do a case insensitive search.
  30. int CFtpPidlList::ComparePidlName(LPVOID pvPidl1, LPVOID pvPidl2, LPARAM lParam)
  31. {
  32. DWORD dwFlags = FCMP_NORMAL;
  33. if (lParam)
  34. dwFlags |= FCMP_CASEINSENSE;
  35. // return < 0 for pvPidl1 before pvPidl2.
  36. // return == 0 for pvPidl1 equals pvPidl2.
  37. // return > 0 for pvPidl1 after pvPidl2.
  38. return FtpItemID_CompareIDsInt(COL_NAME, (LPCITEMIDLIST)pvPidl1, (LPCITEMIDLIST)pvPidl2, dwFlags);
  39. }
  40. HRESULT CFtpPidlList::InsertSorted(LPCITEMIDLIST pidl)
  41. {
  42. m_pfl->InsertSorted(ILClone(pidl), CFtpPidlList::ComparePidlName, FALSE /*Case Insensitive*/);
  43. return S_OK;
  44. };
  45. int CFtpPidlList::FindPidlIndex(LPCITEMIDLIST pidlToFind, BOOL fCaseInsensitive)
  46. {
  47. return m_pfl->SortedSearch((LPVOID) pidlToFind, CFtpPidlList::ComparePidlName, (LPARAM)fCaseInsensitive, DPAS_SORTED);
  48. }
  49. LPITEMIDLIST CFtpPidlList::FindPidl(LPCITEMIDLIST pidlToFind, BOOL fCaseInsensitive)
  50. {
  51. LPITEMIDLIST pidlFound = NULL;
  52. int nIndex = FindPidlIndex(pidlToFind, fCaseInsensitive);
  53. if (-1 != nIndex)
  54. {
  55. pidlFound = ILClone(GetPidl(nIndex));
  56. }
  57. return pidlFound;
  58. }
  59. HRESULT CFtpPidlList::CompareAndDeletePidl(LPCITEMIDLIST pidlToDelete)
  60. {
  61. HRESULT hr = S_FALSE;
  62. int nIndex = FindPidlIndex(pidlToDelete, FALSE /*Case Insensitive*/);
  63. if (-1 != nIndex)
  64. {
  65. LPITEMIDLIST pidlCurrent = GetPidl((UINT)nIndex);
  66. if (EVAL(pidlCurrent))
  67. {
  68. ASSERT(0 == FtpItemID_CompareIDsInt(COL_NAME, pidlCurrent, pidlToDelete, FCMP_NORMAL));
  69. m_pfl->DeletePtrByIndex(nIndex);
  70. ILFree(pidlCurrent); // Deallocate the memory
  71. hr = S_OK; // Found and deleted.
  72. }
  73. }
  74. return hr;
  75. }
  76. void CFtpPidlList::Delete(int nIndex)
  77. {
  78. LPITEMIDLIST pidlToDelete = GetPidl(nIndex);
  79. ILFree(pidlToDelete); // Free the memory.
  80. m_pfl->DeletePtrByIndex(nIndex);
  81. }
  82. HRESULT CFtpPidlList::ReplacePidl(LPCITEMIDLIST pidlSrc, LPCITEMIDLIST pidlDest)
  83. {
  84. HRESULT hr = S_FALSE;
  85. int nIndex = FindPidlIndex(pidlSrc, FALSE);
  86. if (-1 != nIndex)
  87. {
  88. LPITEMIDLIST pidlCurrent = GetPidl((UINT)nIndex);
  89. if (EVAL(pidlCurrent))
  90. {
  91. ASSERT(0 == FtpItemID_CompareIDsInt(COL_NAME, pidlCurrent, pidlSrc, FCMP_NORMAL));
  92. ILFree(pidlCurrent); // Deallocate the memory
  93. m_pfl->DeletePtrByIndex(nIndex);
  94. InsertSorted(pidlDest); // This function does the ILClone()
  95. hr = S_OK; // Found and deleted.
  96. }
  97. }
  98. return hr;
  99. }
  100. void CFtpPidlList::AssertSorted(void)
  101. {
  102. #ifdef DEBUG
  103. // For perf reasons, we need to keep this list in order.
  104. // This is mainly because parse display name looks thru
  105. // the list, so we want that to be fast.
  106. for (int nIndex = (GetCount() - 2); (nIndex >= 0); nIndex--)
  107. {
  108. LPITEMIDLIST pidl1 = GetPidl((UINT)nIndex);
  109. LPITEMIDLIST pidl2 = GetPidl((UINT)nIndex + 1);
  110. // Assert that pidl1 comes before pidl2.
  111. if (!EVAL(0 >= FtpItemID_CompareIDsInt(COL_NAME, pidl1, pidl2, FCMP_NORMAL)))
  112. {
  113. TCHAR szPidl1[MAX_PATH];
  114. TCHAR szPidl2[MAX_PATH];
  115. if (FtpID_IsServerItemID(pidl1))
  116. FtpPidl_GetServer(pidl1, szPidl1, ARRAYSIZE(szPidl1));
  117. else
  118. FtpPidl_GetDisplayName(pidl1, szPidl1, ARRAYSIZE(szPidl1));
  119. if (FtpID_IsServerItemID(pidl2))
  120. FtpPidl_GetServer(pidl2, szPidl2, ARRAYSIZE(szPidl2));
  121. else
  122. FtpPidl_GetDisplayName(pidl2, szPidl2, ARRAYSIZE(szPidl2));
  123. TraceMsg(TF_ERROR, "CFtpPidlList::AssertSorted() '%s' & '%s' where found out of order", szPidl1, szPidl2);
  124. }
  125. // We do NOT need to free pidl1 or pidl2 because we get a pointer to someone else's copy.
  126. }
  127. #endif // DEBUG
  128. }
  129. void CFtpPidlList::TraceDump(LPCITEMIDLIST pidl, LPCTSTR pszCaller)
  130. {
  131. #ifdef DEBUG
  132. /*
  133. TCHAR szUrl[MAX_URL_STRING];
  134. UrlCreateFromPidl(pidl, SHGDN_FORPARSING, szUrl, ARRAYSIZE(szUrl), ICU_USERNAME, FALSE);
  135. TraceMsg(TF_PIDLLIST_DUMP, "CFtpPidlList::TraceDump() root is '%s', called from '%s'", szUrl, pszCaller);
  136. // Let's look at the contents.
  137. for (int nIndex = (GetCount() - 1); (nIndex >= 0); nIndex--)
  138. {
  139. LPITEMIDLIST pidlFull = ILCombine(pidl, GetPidl((UINT)nIndex));
  140. if (pidlFull)
  141. {
  142. UrlCreateFromPidl(pidlFull, SHGDN_FORPARSING, szUrl, ARRAYSIZE(szUrl), ICU_USERNAME, FALSE);
  143. TraceMsg(TF_PIDLLIST_DUMP, "CFtpPidlList::TraceDump() Index=%d, url=%s", nIndex, szUrl);
  144. ILFree(pidlFull);
  145. }
  146. }
  147. */
  148. #endif // DEBUG
  149. }
  150. void CFtpPidlList::UseCachedDirListings(BOOL fUseCachedDirListings)
  151. {
  152. // Normally we do two passes in the tree walker code. The first
  153. // pass is to count up the time required to do the download. We
  154. // normally force WININET to not use cached results because someone
  155. // else could have changed the contents on the server.
  156. // On the second pass, we normally do the work (upload, download, delete)
  157. // and we want to use the cached results to get the perf advantage
  158. // and the results shouldn't be more than a minute out of date.
  159. if (fUseCachedDirListings)
  160. m_dwInetFlags = INTERNET_NO_CALLBACK;
  161. else
  162. m_dwInetFlags = (INTERNET_NO_CALLBACK | INTERNET_FLAG_RESYNCHRONIZE | INTERNET_FLAG_RELOAD);
  163. }
  164. BOOL CFtpPidlList::AreAllFolders(void)
  165. {
  166. BOOL fAllFolder = TRUE;
  167. for (int nIndex = (GetCount() - 1); fAllFolder && (nIndex >= 0); nIndex--)
  168. {
  169. LPITEMIDLIST pidl = GetPidl((UINT)nIndex);
  170. if (EVAL(pidl))
  171. fAllFolder = FtpPidl_IsDirectory(pidl, TRUE);
  172. // We do NOT need to free pidl because we get a pointer to someone else's copy.
  173. }
  174. return fAllFolder;
  175. }
  176. BOOL CFtpPidlList::AreAllFiles(void)
  177. {
  178. BOOL fAllFiles = TRUE;
  179. for (int nIndex = (GetCount() - 1); fAllFiles && (nIndex >= 0); nIndex--)
  180. {
  181. LPITEMIDLIST pidl = GetPidl((UINT)nIndex);
  182. if (EVAL(pidl))
  183. fAllFiles = !FtpPidl_IsDirectory(pidl, TRUE);
  184. // We do NOT need to free pidl because we get a pointer to someone else's copy.
  185. }
  186. return fAllFiles;
  187. }
  188. /*****************************************************************************
  189. *
  190. * CFtpPidlList::_Fill
  191. *
  192. * Fill a list with an array.
  193. *
  194. * The elements in the array are copied rather than stolen.
  195. *
  196. *****************************************************************************/
  197. HRESULT CFtpPidlList::_Fill(int cpidl, LPCITEMIDLIST rgpidl[])
  198. {
  199. HRESULT hres = S_OK;
  200. for (int ipidl = 0; (ipidl < cpidl) && SUCCEEDED(hres); ipidl++)
  201. {
  202. ASSERT(IsValidPIDL(rgpidl[ipidl]));
  203. hres = InsertSorted(rgpidl[ipidl]);
  204. }
  205. return hres;
  206. }
  207. /*****************************************************************************
  208. *
  209. * CFtpPidlList::GetPidlList
  210. *
  211. *****************************************************************************/
  212. LPCITEMIDLIST * CFtpPidlList::GetPidlList(void)
  213. {
  214. LPITEMIDLIST * ppidl;
  215. ppidl = (LPITEMIDLIST *) LocalAlloc(LPTR, sizeof(LPITEMIDLIST) * GetCount());
  216. if (ppidl)
  217. {
  218. int nIndex;
  219. for (nIndex = 0; nIndex < GetCount(); nIndex++)
  220. {
  221. // Later we can make this user ILClone() if we want to be able to wack on the
  222. // pidl list while this list is being used.
  223. ppidl[nIndex] = GetPidl(nIndex);
  224. }
  225. }
  226. return (LPCITEMIDLIST *) ppidl;
  227. }
  228. /*****************************************************************************
  229. *
  230. * CFtpPidlList::FreePidlList
  231. *
  232. *****************************************************************************/
  233. void CFtpPidlList::FreePidlList(LPCITEMIDLIST * ppidl)
  234. {
  235. LocalFree(ppidl);
  236. }
  237. /*****************************************************************************
  238. *
  239. * CFtpPidlList_Create
  240. *
  241. * Start up a new pv list, with a recommended initial size and other
  242. * callback info.
  243. *
  244. *****************************************************************************/
  245. HRESULT CFtpPidlList_Create(int cpidl, LPCITEMIDLIST rgpidl[], CFtpPidlList ** ppflpidl)
  246. {
  247. HRESULT hres = E_OUTOFMEMORY;
  248. CFtpPidlList * pflpidl;
  249. *ppflpidl = pflpidl = new CFtpPidlList();
  250. if (pflpidl)
  251. {
  252. hres = pflpidl->_Fill(cpidl, rgpidl);
  253. if (!EVAL(SUCCEEDED(hres)))
  254. {
  255. ASSERT(pflpidl->GetCount() == 0);
  256. IUnknown_Set(ppflpidl, NULL);
  257. }
  258. }
  259. return hres;
  260. }
  261. int CALLBACK PidlListDestroyCallback(LPVOID p, LPVOID pData)
  262. {
  263. ILFree((LPITEMIDLIST) p);
  264. return 1;
  265. }
  266. /****************************************************\
  267. Constructor
  268. \****************************************************/
  269. CFtpPidlList::CFtpPidlList() : m_cRef(1)
  270. {
  271. DllAddRef();
  272. // This needs to be allocated in Zero Inited Memory.
  273. // Assert that all Member Variables are inited to Zero.
  274. ASSERT(!m_pfl);
  275. CFtpList_Create(100, PidlListDestroyCallback, 100, &m_pfl);
  276. ASSERT(m_pfl); // BUGBUG can fail in low memory
  277. UseCachedDirListings(FALSE);
  278. LEAK_ADDREF(LEAK_CFtpPidlList);
  279. }
  280. /****************************************************\
  281. Destructor
  282. \****************************************************/
  283. CFtpPidlList::~CFtpPidlList()
  284. {
  285. AssertSorted();
  286. if (m_pfl)
  287. m_pfl->Release();
  288. DllRelease();
  289. LEAK_DELREF(LEAK_CFtpPidlList);
  290. }
  291. //===========================
  292. // *** IUnknown Interface ***
  293. //===========================
  294. ULONG CFtpPidlList::AddRef()
  295. {
  296. m_cRef++;
  297. return m_cRef;
  298. }
  299. ULONG CFtpPidlList::Release()
  300. {
  301. ASSERT(m_cRef > 0);
  302. m_cRef--;
  303. if (m_cRef > 0)
  304. return m_cRef;
  305. delete this;
  306. return 0;
  307. }
  308. HRESULT CFtpPidlList::QueryInterface(REFIID riid, void **ppvObj)
  309. {
  310. if (IsEqualIID(riid, IID_IUnknown))
  311. {
  312. *ppvObj = SAFECAST(this, IUnknown *);
  313. }
  314. else
  315. {
  316. TraceMsg(TF_FTPQI, "CFtpPidlList::QueryInterface() failed.");
  317. *ppvObj = NULL;
  318. return E_NOINTERFACE;
  319. }
  320. AddRef();
  321. return S_OK;
  322. }
  323. ////////////////////////////////////////////////////////////////////
  324. // Pild List Enum Helpers
  325. ////////////////////////////////////////////////////////////////////
  326. /*****************************************************************************\
  327. FUNCTION: RecursiveProcessPidl
  328. DESCRIPTION:
  329. This function will will be called for each item in the initial Pidl List
  330. (before the recursion occurs). This is a wrapper because the first list is
  331. a list of pidls. The subsequent lists are of WIN32_FIND_DATA types.
  332. \*****************************************************************************/
  333. int RecursiveProcessPidl(LPVOID pvPidl, LPVOID pvInetEnum)
  334. {
  335. LPCITEMIDLIST pidl = (LPCITEMIDLIST) pvPidl;
  336. INETENUM * pInetEnum = (INETENUM *) pvInetEnum;
  337. LPITEMIDLIST pidlFull = ILCombine(pInetEnum->pidlRoot, pidl);
  338. if (pidlFull)
  339. {
  340. pInetEnum->hr = pInetEnum->pfnProcessItemCB((LPVOID) pInetEnum->pfnProcessItemCB, pInetEnum->hint, pidlFull, pInetEnum->pfValidhinst, pInetEnum->pvData);
  341. ILFree(pidlFull);
  342. }
  343. return (SUCCEEDED(pInetEnum->hr) ? TRUE : FALSE);
  344. }
  345. /*****************************************************************************\
  346. FUNCTION: _EnumFolderPrep
  347. DESCRIPTION:
  348. This function will step into the pszDir directory and enum all of it's
  349. contents. For each item, it will call the callback function provided (pfnProcessItemCB).
  350. That callback function can then call EnumFolder() again (recursively) if
  351. there is a subfolder.
  352. NOTE:
  353. This function needs to first find all the items and then in a second
  354. loop call the callback function. This is because the WININET FTP APIs
  355. only allow one enum to occur at a time, which may not happen if half way through
  356. enuming one dir, a recursive call starts enuming a sub dir.
  357. \*****************************************************************************/
  358. HRESULT _EnumFolderPrep(HINTERNET hint, LPCITEMIDLIST pidlFull, CFtpPidlList * pPidlList, CWireEncoding * pwe, LPITEMIDLIST * ppidlCurrFtpPath)
  359. {
  360. HRESULT hr = S_OK;
  361. // 1. Get Current Directory (To restore later).
  362. hr = FtpGetCurrentDirectoryPidlWrap(hint, TRUE, pwe, ppidlCurrFtpPath);
  363. if (SUCCEEDED(hr))
  364. {
  365. CMultiLanguageCache cmlc;
  366. CWireEncoding we;
  367. if (!pwe)
  368. pwe = &we;
  369. // It's important that this is a relative CD.
  370. // 2. Change Directory Into the subdirectory.
  371. hr = FtpSetCurrentDirectoryWrap(hint, TRUE, FtpPidl_GetLastItemWireName(pidlFull));
  372. if (SUCCEEDED(hr))
  373. {
  374. LPITEMIDLIST pidlItem;
  375. HINTERNET hInetFind = NULL;
  376. hr = FtpFindFirstFilePidlWrap(hint, TRUE, &cmlc, pwe, NULL, &pidlItem, pPidlList->m_dwInetFlags, NULL, &hInetFind);
  377. if (hInetFind)
  378. {
  379. do
  380. {
  381. LPCWIRESTR pwireStr = FtpPidl_GetLastItemWireName(pidlFull);
  382. if (IS_VALID_FILE(pwireStr))
  383. {
  384. // Store entire pidl (containing WIN32_FIND_DATA) so we can get
  385. // the attributes and other info later. Seeing if it's a dir
  386. // is one need...
  387. pPidlList->InsertSorted(pidlItem);
  388. }
  389. ILFree(pidlItem);
  390. hr = InternetFindNextFilePidlWrap(hInetFind, TRUE, &cmlc, pwe, &pidlItem);
  391. }
  392. while (SUCCEEDED(hr));
  393. ILFree(pidlItem);
  394. InternetCloseHandle(hInetFind);
  395. }
  396. if (ERROR_NO_MORE_FILES == HRESULT_CODE(hr))
  397. hr = S_OK;
  398. }
  399. EVAL(SUCCEEDED(pwe->ReSetCodePages(&cmlc, pPidlList)));
  400. }
  401. return hr;
  402. }
  403. /*****************************************************************************\
  404. FUNCTION: _GetPathDifference
  405. DESCRIPTION:
  406. This function will step into the pszDir directory and enum all of it's
  407. contents. For each item, it will call the callback function provided (pfnProcessItemCB).
  408. That callback function can then call EnumFolder() again (recursively) if
  409. there is a subfolder.
  410. NOTE:
  411. This function needs to first find all the items and then in a second
  412. loop call the callback function. This is because the WININET FTP APIs
  413. only allow one enum to occur at a time, which may not happen if half way through
  414. enuming one dir, a recursive call starts enuming a sub dir.
  415. PARAMETERS:
  416. pszBaseUrl - This needs to be escaped.
  417. pszDir - This needs to be escaped.
  418. *ppszUrlPathDiff - This will be UnEscaped.
  419. \*****************************************************************************/
  420. void _GetPathDifference(LPCTSTR pszBaseUrl, LPCTSTR pszDir, LPTSTR * ppszUrlPathDiff)
  421. {
  422. TCHAR szUrlPathDiff[MAX_URL_STRING];
  423. TCHAR szFullUrl[MAX_URL_STRING];
  424. DWORD cchSize = ARRAYSIZE(szFullUrl);
  425. // This is needed for this case:
  426. // pszBaseUrl="ftp://server/subdir1/", pszDir="/subdir1/subdir2/file.txt"
  427. // So, szUrlPathDiff="subdir2/file.txt" instead of pszDir
  428. //
  429. // ICU_NO_ENCODE is needed because Download Dlg may have paths with
  430. // spaces that can't be escaped.
  431. InternetCombineUrl(pszBaseUrl, pszDir, szFullUrl, &cchSize, ICU_NO_ENCODE);
  432. UrlGetDifference(pszBaseUrl, szFullUrl, szUrlPathDiff, ARRAYSIZE(szUrlPathDiff));
  433. // We will now use szFullUrl to store the UnEscaped version since these buffers
  434. // are so large.
  435. UnEscapeString(szUrlPathDiff, szFullUrl, ARRAYSIZE(szFullUrl));
  436. Str_SetPtr(ppszUrlPathDiff, szFullUrl);
  437. }
  438. /*****************************************************************************\
  439. FUNCTION: EnumFolder
  440. DESCRIPTION:
  441. This function will step into the pszDir directory and enum all of it's
  442. contents. For each item, it will call the callback function provided (pfnProcessItemCB).
  443. That callback function can then call EnumFolder() again (recursively) if
  444. there is a subfolder.
  445. PARAMETERS:
  446. (pszBaseUrl=ftp://server/dir1/, pszDir=dir2, DirToEnum=ftp://server/dir1/dir2/)
  447. pszDir - This is the directory we are enumerating. (dir2) It is relative to pszBaseUrl.
  448. hint - The current working directory will be set to pszBaseUrl. _EnumFolderPrep will make it go into pszDir.
  449. NOTE:
  450. This function needs to first find all the items and then in a second
  451. loop call the callback function. This is because the WININET FTP APIs
  452. only allow one enum to occur at a time, which may not happen if half way through
  453. enuming one dir, a recursive call starts enuming a sub dir.
  454. \*****************************************************************************/
  455. HRESULT EnumFolder(LPFNPROCESSITEMCB pfnProcessItemCB, HINTERNET hint, LPCITEMIDLIST pidlFull, CWireEncoding * pwe, BOOL * pfValidhinst, LPVOID pvData)
  456. {
  457. CFtpPidlList * pPidlList;
  458. BOOL fValidhinst = TRUE;
  459. HRESULT hr = CFtpPidlList_Create(0, &pidlFull, &pPidlList);
  460. if (SUCCEEDED(hr))
  461. {
  462. LPITEMIDLIST pidlCurrFtpPath = NULL;
  463. hr = _EnumFolderPrep(hint, pidlFull, pPidlList, pwe, &pidlCurrFtpPath);
  464. if (SUCCEEDED(hr))
  465. {
  466. hr = S_OK;
  467. // 4. Process each file name, which may be recursive.
  468. // This loop and the while loop above need to be
  469. // separated because it's not possible to create
  470. // more than one FTP Find File handle based on the
  471. // same session.
  472. for (int nIndex = 0; SUCCEEDED(hr) && (nIndex < pPidlList->GetCount()); nIndex++)
  473. {
  474. LPITEMIDLIST pidlNewFull = ILCombine(pidlFull, pPidlList->GetPidl(nIndex));
  475. hr = pfnProcessItemCB(pfnProcessItemCB, hint, pidlNewFull, &fValidhinst, pvData);
  476. ILFree(pidlNewFull);
  477. }
  478. // 5. Go back to original directory (from Step 2)
  479. // The only time we don't want to return to the original directory is if
  480. // the hinst was freed in an wininet callback function. We may cache the hinst
  481. // so we need the directory to be valid later.
  482. if (fValidhinst)
  483. {
  484. if (SUCCEEDED(hr))
  485. {
  486. // We still want to reset the directory but we don't want to over write
  487. // the original error message.
  488. hr = FtpSetCurrentDirectoryPidlWrap(hint, TRUE, pidlCurrFtpPath, TRUE, TRUE);
  489. }
  490. }
  491. Pidl_Set(&pidlCurrFtpPath, NULL);
  492. }
  493. pPidlList->Release();
  494. }
  495. if (pfValidhinst)
  496. *pfValidhinst = fValidhinst;
  497. return hr;
  498. }