Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

694 lines
22 KiB

  1. /*****************************************************************************\
  2. FILE: ftpdir.cpp
  3. DESCRIPTION:
  4. Internal object that manages a single FTP directory
  5. The idea is that each FtpSite maintains a linked list of the
  6. FtpDir's that it owns. Gets and Releases are done through the
  7. FtpSite. Each FtpDir retains a non-refcounted pointer back
  8. to the FtpSite that owns it.
  9. The reason this is necessary is that there might be multiple
  10. IShellFolder's all looking at the same physical directory. Since
  11. enumerations are expensive, we cache the enumeration information
  12. here, so that each IShellFolder client can use the information.
  13. This also lets us hold the motd, so that multiple clients can
  14. query for the motd without constantly pinging the site.
  15. \*****************************************************************************/
  16. #include "priv.h"
  17. #include "ftpdir.h"
  18. #include "ftpsite.h"
  19. #include "ftppidl.h"
  20. #include "ftpurl.h"
  21. #include "ftppidl.h"
  22. #include "statusbr.h"
  23. /*****************************************************************************\
  24. FUNCTION: GetDisplayPath
  25. DESCRIPTION:
  26. \*****************************************************************************/
  27. HRESULT CFtpDir::GetDisplayPath(LPWSTR pwzDisplayPath, DWORD cchSize)
  28. {
  29. return GetDisplayPathFromPidl(m_pidlFtpDir, pwzDisplayPath, cchSize, FALSE);
  30. }
  31. /*****************************************************************************\
  32. FUNCTION: CollectMotd
  33. DESCRIPTION:
  34. An InternetConnect has just completed. Get the motd and cache it.
  35. hint - the connected handle, possibly 0 if error
  36. \*****************************************************************************/
  37. void CFtpDir::CollectMotd(HINTERNET hint)
  38. {
  39. CFtpGlob * pfg = GetFtpResponse(GetFtpSite()->GetCWireEncoding());
  40. if (m_pfgMotd)
  41. m_pfgMotd->Release();
  42. m_pfgMotd = pfg; // m_pfgMotd will take pfg's ref.
  43. }
  44. /*****************************************************************************\
  45. FUNCTION: CollectMotd
  46. DESCRIPTION:
  47. Shove a value into the cached list.
  48. \*****************************************************************************/
  49. void CFtpDir::SetCache(CFtpPidlList * pflHfpl)
  50. {
  51. IUnknown_Set(&m_pflHfpl, pflHfpl);
  52. // If we are flushing the cache, then flush the Ratings info also.
  53. // This way the user can reenter the parent password if wanted.
  54. if (!pflHfpl && m_pfs)
  55. m_pfs->FlushRatingsInfo();
  56. }
  57. /*****************************************************************************\
  58. FUNCTION: CollectMotd
  59. DESCRIPTION:
  60. Get the value out of the cache.
  61. \*****************************************************************************/
  62. CFtpPidlList * CFtpDir::GetHfpl(void)
  63. {
  64. CFtpPidlList * pfl;
  65. pfl = m_pflHfpl;
  66. if (pfl)
  67. pfl->AddRef();
  68. return pfl;
  69. }
  70. /*****************************************************************************\
  71. FUNCTION: CollectMotd
  72. DESCRIPTION:
  73. Get the FTP site associated with a directory.
  74. This doesn't AddRef the return value.
  75. \*****************************************************************************/
  76. CFtpSite * CFtpDir::GetFtpSite(void)
  77. {
  78. return m_pfs;
  79. }
  80. CFtpDir * CFtpDir::GetSubFtpDir(CFtpFolder * pff, LPCITEMIDLIST pidl, BOOL fPublic)
  81. {
  82. CFtpDir * pfd = NULL;
  83. if (EVAL(pidl))
  84. {
  85. LPITEMIDLIST pidlChild = GetSubPidl(pff, pidl, fPublic);
  86. if (EVAL(pidlChild))
  87. {
  88. m_pfs->GetFtpDir(pidlChild, &pfd);
  89. ILFree(pidlChild);
  90. }
  91. }
  92. return pfd;
  93. }
  94. LPITEMIDLIST CFtpDir::GetSubPidl(CFtpFolder * pff, LPCITEMIDLIST pidlRelative, BOOL fPublic)
  95. {
  96. LPITEMIDLIST pidlRoot = ((fPublic && pff) ? pff->GetPublicPidlRootIDClone() : NULL);
  97. LPITEMIDLIST pidlPublic = ILCombine(pidlRoot, m_pidl);
  98. LPITEMIDLIST pidlFull = NULL;
  99. if (pidlPublic)
  100. {
  101. pidlFull = ILCombine(pidlPublic, pidlRelative);
  102. ILFree(pidlPublic);
  103. }
  104. ILFree(pidlRoot);
  105. return pidlFull;
  106. }
  107. HRESULT CFtpDir::AddItem(LPCITEMIDLIST pidl)
  108. {
  109. if (!m_pflHfpl)
  110. return S_OK;
  111. return m_pflHfpl->InsertSorted(pidl);
  112. }
  113. /*****************************************************************************\
  114. FUNCTION: CollectMotd
  115. DESCRIPTION:
  116. Get a HINTERNET for this directory.
  117. \*****************************************************************************/
  118. HRESULT CFtpDir::GetHint(HWND hwnd, CStatusBar * psb, HINTERNET * phint, IUnknown * punkSite, CFtpFolder * pff)
  119. {
  120. HRESULT hr = m_pfs->GetHint(hwnd, m_pidlFtpDir, psb, phint, punkSite, pff);
  121. return hr;
  122. }
  123. /*****************************************************************************\
  124. FUNCTION: CollectMotd
  125. DESCRIPTION:
  126. Give a HINTERNET back to the FtpSite.
  127. \*****************************************************************************/
  128. void CFtpDir::ReleaseHint(HINTERNET hint)
  129. {
  130. ASSERT(!hint || m_pfs); // If we have a hint to release, we need to call ::ReleaseHint()
  131. if (m_pfs)
  132. m_pfs->ReleaseHint(m_pidlFtpDir, hint);
  133. }
  134. /*****************************************************************************\
  135. FUNCTION: CollectMotd
  136. DESCRIPTION:
  137. Perform an operation with a temporary internet handle which is
  138. already connected to the site and resides in the correct directory.
  139. \*****************************************************************************/
  140. STDMETHODIMP CFtpDir::WithHint(CStatusBar * psb, HWND hwnd, HINTPROC hp, LPCVOID pv, IUnknown * punkSite, CFtpFolder * pff)
  141. {
  142. HRESULT hr = E_FAIL;
  143. // Did the user turn off FTP Folders?
  144. // If so, don't connect. This will fix NT #406423 where the user turned
  145. // of FTP Folders because they have a firewall (CISCO filtering Router)
  146. // that will kill packets in such a way the caller (WinSock/Wininet) needs
  147. // to wait for a timeout. During this timeout, the browser will hang causing
  148. // the user to think it crashed.
  149. if (!SHRegGetBoolUSValue(SZ_REGKEY_FTPFOLDER, SZ_REGKEY_USE_OLD_UI, FALSE, FALSE))
  150. {
  151. HINTERNET hint;
  152. HINTPROCINFO hpi;
  153. ASSERTNONCRITICAL; // Cannot do psb (CStatusBar *) with the crst
  154. ASSERT(m_pfs);
  155. hpi.pfd = this;
  156. hpi.hwnd = hwnd;
  157. hpi.psb = psb;
  158. hr = GetHint(hwnd, psb, &hint, punkSite, pff);
  159. if (SUCCEEDED(hr)) // Ok if fails
  160. {
  161. BOOL fReleaseHint = TRUE;
  162. if (hp)
  163. hr = hp(hint, &hpi, (LPVOID)pv, &fReleaseHint);
  164. if (fReleaseHint)
  165. ReleaseHint(hint);
  166. }
  167. }
  168. return hr;
  169. }
  170. /*****************************************************************************\
  171. FUNCTION: _SetNameOfCB
  172. DESCRIPTION:
  173. If we were able to rename the file, return the output pidl.
  174. Also tell anybody who cares that this LPITEMIDLIST needs to be refreshed.
  175. The "A" emphasizes that the filename is received in ANSI.
  176. _UNDOCUMENTED_: The documentation on SetNameOf's treatment of
  177. the source pidl is random. It seems to suggest that the source
  178. pidl is ILFree'd by SetNameOf, but it isn't.
  179. \*****************************************************************************/
  180. HRESULT CFtpDir::_SetNameOfCB(HINTERNET hint, HINTPROCINFO * phpi, LPVOID pv, BOOL * pfReleaseHint)
  181. {
  182. LPSETNAMEOFINFO psnoi = (LPSETNAMEOFINFO) pv;
  183. if (phpi->psb)
  184. {
  185. TCHAR szStatus[MAX_PATH];
  186. FtpPidl_GetLastItemDisplayName(psnoi->pidlOld, szStatus, ARRAYSIZE(szStatus));
  187. phpi->psb->SetStatusMessage(IDS_RENAMING, szStatus);
  188. }
  189. // Remember, FTP filenames are always in the ANSI character set
  190. return FtpRenameFilePidlWrap(hint, TRUE, psnoi->pidlOld, psnoi->pidlNew);
  191. }
  192. BOOL CFtpDir::_DoesItemExist(HWND hwnd, CFtpFolder * pff, LPCITEMIDLIST pidl)
  193. {
  194. FTP_FIND_DATA wfd;
  195. HRESULT hr = GetFindData(hwnd, FtpPidl_GetLastItemWireName(pidl), &wfd, pff);
  196. return ((S_OK == hr) ? TRUE : FALSE);
  197. }
  198. BOOL CFtpDir::_ConfirmReplaceWithRename(HWND hwnd)
  199. {
  200. TCHAR szTitle[MAX_PATH];
  201. TCHAR szMessage[MAX_PATH];
  202. EVAL(LoadString(HINST_THISDLL, IDS_FTPERR_TITLE, szTitle, ARRAYSIZE(szTitle)));
  203. EVAL(LoadString(HINST_THISDLL, IDS_FTPERR_RENAME_REPLACE, szMessage, ARRAYSIZE(szMessage)));
  204. return ((IDYES == MessageBox(hwnd, szMessage, szTitle, (MB_ICONQUESTION | MB_YESNO))) ? TRUE : FALSE);
  205. }
  206. HRESULT CFtpDir::SetNameOf(CFtpFolder * pff, HWND hwndOwner, LPCITEMIDLIST pidl,
  207. LPCWSTR pwzName, DWORD dwReserved, LPITEMIDLIST *ppidlOut)
  208. {
  209. HRESULT hr = S_OK;
  210. SETNAMEOFINFO snoi;
  211. CWireEncoding cWireEncoding;
  212. ASSERT(pff);
  213. if (!pwzName)
  214. return E_FAIL;
  215. snoi.pidlOld = pidl;
  216. cWireEncoding.ChangeFtpItemIDName(NULL, pidl, pwzName, IsUTF8Supported(), (LPITEMIDLIST *) &snoi.pidlNew);
  217. if (snoi.pidlNew)
  218. {
  219. #ifdef FEATURE_REPLACE_IN_RENAME
  220. // FEATURE: Currently, if the user renames file <a> to file <b> and a file <b>
  221. // already exists, we fail with the error message, "file already exists...".
  222. // If we had an excess of time, I would turn on this feature. It would
  223. // ask the user if they want to replace the file. I would then need to add the
  224. // code that deletes that file and checks for delete errors.
  225. // Does it already exist? We don't care if we don't have an hwnd because
  226. // we can't ask the user to replace so we will just go ahead.
  227. if (hwndOwner && _DoesItemExist(hwndOwner, pff, snoi.pidlNew))
  228. {
  229. // Yes, so let's make sure it's OK with the user to replace it.
  230. hr = (_ConfirmReplaceWithRename(hwndOwner) ? S_OK : HRESULT_FROM_WIN32(ERROR_CANCELLED));
  231. todo; // Add the delete call here.
  232. }
  233. #endif FEATURE_REPLACE_IN_RENAME
  234. if (S_OK == hr)
  235. {
  236. hr = WithHint(NULL, hwndOwner, _SetNameOfCB, (LPVOID) &snoi, NULL, pff);
  237. if (SUCCEEDED(hr)) // Will fail if use didn't have permission to rename
  238. {
  239. // WARNING: The date/time stamp on the server may be different than what we give to SHChangeNotify()
  240. // but this is probably reasonable for perf reasons.
  241. FtpChangeNotify(hwndOwner, FtpPidl_DirChoose(pidl, SHCNE_RENAMEFOLDER, SHCNE_RENAMEITEM), pff, this, pidl, snoi.pidlNew, TRUE);
  242. if (ppidlOut)
  243. *ppidlOut = ILClone(snoi.pidlNew);
  244. }
  245. }
  246. ILFree((LPITEMIDLIST) snoi.pidlNew);
  247. }
  248. return hr;
  249. }
  250. LPCITEMIDLIST CFtpDir::GetPidlFromWireName(LPCWIRESTR pwWireName)
  251. {
  252. LPITEMIDLIST pidlToFind = NULL;
  253. LPITEMIDLIST pidlTemp;
  254. WCHAR wzDisplayName[MAX_PATH];
  255. // This isn't valid because the code page could be wrong, but we don't care
  256. // because it's not used in the search for the pidl, the pwWireName is.
  257. SHAnsiToUnicode(pwWireName, wzDisplayName, ARRAYSIZE(wzDisplayName));
  258. if (m_pflHfpl && SUCCEEDED(FtpItemID_CreateFake(wzDisplayName, pwWireName, FALSE, FALSE, FALSE, &pidlTemp)))
  259. {
  260. // PERF: log 2 (sizeof(m_pflHfpl))
  261. pidlToFind = m_pflHfpl->FindPidl(pidlTemp, FALSE);
  262. // We will try again and this time allow for the case to not match
  263. if (!pidlToFind)
  264. pidlToFind = m_pflHfpl->FindPidl(pidlTemp, TRUE);
  265. ILFree(pidlTemp);
  266. }
  267. return pidlToFind;
  268. }
  269. LPCITEMIDLIST CFtpDir::GetPidlFromDisplayName(LPCWSTR pwzDisplayName)
  270. {
  271. WIRECHAR wWireName[MAX_PATH];
  272. CWireEncoding * pwe = GetFtpSite()->GetCWireEncoding();
  273. EVAL(SUCCEEDED(pwe->UnicodeToWireBytes(NULL, pwzDisplayName, (IsUTF8Supported() ? WIREENC_USE_UTF8 : WIREENC_NONE), wWireName, ARRAYSIZE(wWireName))));
  274. return GetPidlFromWireName(wWireName);
  275. }
  276. /*****************************************************************************\
  277. FUNCTION: IsRoot
  278. DESCRIPTION:
  279. Returns FALSE if we are at the "FTP Folder" root level, not
  280. inside an actual FTP site.y
  281. \*****************************************************************************/
  282. BOOL CFtpDir::IsRoot(void)
  283. {
  284. return ILIsEmpty(m_pidl);
  285. }
  286. typedef struct tagGETFINDDATAINFO
  287. {
  288. LPCWIRESTR pwWireName;
  289. LPFTP_FIND_DATA pwfd;
  290. } GETFINDDATAINFO, * LPGETFINDDATAINFO;
  291. HRESULT CFtpDir::_GetFindData(HINTERNET hint, HINTPROCINFO * phpi, LPVOID pv, BOOL * pfReleaseHint)
  292. {
  293. LPGETFINDDATAINFO pgfdi = (LPGETFINDDATAINFO) pv;
  294. HRESULT hr = S_FALSE;
  295. // Remember, FTP filenames are always in the ANSI character set
  296. // PERF: Status
  297. hr = FtpDoesFileExist(hint, TRUE, pgfdi->pwWireName, pgfdi->pwfd, INTERNET_NO_CALLBACK);
  298. if (SUCCEEDED(hr))
  299. {
  300. if (!StrCmpIA(pgfdi->pwfd->cFileName, pgfdi->pwWireName))
  301. hr = S_OK; // The are equal.
  302. else if (!StrCmpA(pgfdi->pwfd->cFileName, SZ_DOTA))
  303. {
  304. // Coincidence of coincidences: If we found a ".",
  305. // then the wfd already contains the description of
  306. // the directory! In other words, the wfd contains
  307. // the correct information after all, save for the name.
  308. // Aren't we lucky.
  309. //
  310. // And if it isn't dot, then it's some directory with
  311. // unknown attributes (so we'll use whatever's lying around).
  312. // Just make sure it's a directory.
  313. pgfdi->pwfd->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
  314. StrCpyNA(pgfdi->pwfd->cFileName, pgfdi->pwWireName, ARRAYSIZE(pgfdi->pwfd->cFileName));
  315. hr = S_OK;
  316. }
  317. }
  318. else
  319. {
  320. #ifndef DEBUG
  321. // Don't display an error msg because some callers will call when they
  322. // know the file may not exist. This is the case for ConfirmCopy().
  323. hr = S_FALSE;
  324. #endif // DEBUG
  325. }
  326. return hr;
  327. }
  328. /*****************************************************************************\
  329. FUNCTION: GetFindData
  330. DESCRIPTION:
  331. Get the WIN32_FIND_DATA for a file, given by name.
  332. This is done as part of drag/drop to allow for an overwrite prompt.
  333. This is all a gross hack because the STAT command
  334. isn't supported by WinINet (as FtpGetFileAttributes).
  335. Not that it'd help, because ftp.microsoft.com is OUT OF SPEC
  336. with regard to the STAT command. (The first line of the output
  337. isn't terminated correctly, causing the client to hang.)
  338. Furthermore, UNIX ftp servers implement STAT incorrectly, too,
  339. rendering STAT no more useful than LIST.
  340. HACKHACK -- There is a bug in WinINet where doing a FindFirst
  341. on a name which happens to be a directory returns the contents
  342. of the directory instead of the attributes of the directory itself.
  343. (This is actually a failing of most FTP implementation, because
  344. they just use /bin/ls for directory listings.)
  345. So we compare the name that comes back against the name we ask
  346. for. If they are different, then it's a folder. We'll compare
  347. in a case-insensitive manner because we don't know whether the
  348. server is case-sensitive or not.
  349. Note that we can get faked out if a directory contains a file
  350. which has the same name as the directory. There is nothing we
  351. can do about that. Fortunately, UNIX servers always return "."
  352. as the first file in a subdirectory, so 99% of the time, we'll
  353. do the right thing.
  354. \*****************************************************************************/
  355. HRESULT CFtpDir::GetFindData(HWND hwnd, LPCWIRESTR pwWireName, LPFTP_FIND_DATA pwfd, CFtpFolder * pff)
  356. {
  357. GETFINDDATAINFO gfdi = {pwWireName, pwfd};
  358. HRESULT hr = WithHint(NULL, hwnd, _GetFindData, &gfdi, NULL, pff);
  359. return hr;
  360. }
  361. /*****************************************************************************\
  362. FUNCTION: GetFindDataForDisplayPath
  363. DESCRIPTION:
  364. \*****************************************************************************/
  365. HRESULT CFtpDir::GetFindDataForDisplayPath(HWND hwnd, LPCWSTR pwzDisplayPath, LPFTP_FIND_DATA pwfd, CFtpFolder * pff)
  366. {
  367. CWireEncoding * pwe = GetFtpSite()->GetCWireEncoding();
  368. WIRECHAR wWirePath[MAX_PATH];
  369. EVAL(SUCCEEDED(pwe->UnicodeToWireBytes(NULL, pwzDisplayPath, (IsUTF8Supported() ? WIREENC_USE_UTF8 : WIREENC_NONE), wWirePath, ARRAYSIZE(wWirePath))));
  370. return GetFindData(hwnd, wWirePath, pwfd, pff);
  371. }
  372. /*****************************************************************************\
  373. FUNCTION: GetNameOf
  374. DESCRIPTION:
  375. Common worker that handles SHGDN_FORPARSING style GetDisplayNameOf's.
  376. Note! that since we do not support junctions (duh), we can
  377. safely walk down the pidl generating goop as we go, secure
  378. in the knowledge that we are in charge of every subpidl.
  379. _CHARSET_: Since FTP filenames are always in the ANSI character
  380. set, by RFC 1738, we can return ANSI display names without loss
  381. of fidelity. In a general folder implementation, we should be
  382. using cStr to return display names, so that the UNICODE
  383. version of the shell extension can handle UNICODE names.
  384. \*****************************************************************************/
  385. HRESULT CFtpDir::GetNameOf(LPCITEMIDLIST pidl, DWORD shgno, LPSTRRET pstr)
  386. {
  387. LPITEMIDLIST pidlFull = ILCombine(m_pidl, pidl);
  388. HRESULT hr = E_FAIL;
  389. if (pidlFull)
  390. {
  391. hr = StrRetFromFtpPidl(pstr, shgno, pidlFull);
  392. ILFree(pidlFull);
  393. }
  394. return hr;
  395. }
  396. /*****************************************************************************\
  397. FUNCTION: ChangeFolderName
  398. DESCRIPTION:
  399. A rename happened on this folder so update the szDir and m_pidl
  400. PARAMETERS:
  401. pidlFtpPath
  402. \*****************************************************************************/
  403. HRESULT CFtpDir::ChangeFolderName(LPCITEMIDLIST pidlFtpPath)
  404. {
  405. HRESULT hr = E_FAIL;
  406. LPITEMIDLIST pidlNewFtpPath = NULL;
  407. EVAL(SUCCEEDED(m_pfs->FlushSubDirs(m_pidlFtpDir)));
  408. hr = FtpPidl_ReplacePath(m_pidl, pidlFtpPath, &pidlNewFtpPath);
  409. _SetFtpDir(m_pfs, this, pidlFtpPath);
  410. if (SUCCEEDED(hr))
  411. {
  412. Pidl_Set(&m_pidl, pidlNewFtpPath);
  413. ILFree(pidlNewFtpPath);
  414. }
  415. return hr;
  416. }
  417. /*****************************************************************************\
  418. FUNCTION: _CompareDirs
  419. DESCRIPTION:
  420. Check if the indicated pfd is already rooted at the indicated pidl.
  421. \*****************************************************************************/
  422. int CALLBACK _CompareDirs(LPVOID pvPidl, LPVOID pvFtpDir, LPARAM lParam)
  423. {
  424. LPCITEMIDLIST pidl = (LPCITEMIDLIST) pvPidl;
  425. CFtpDir * pfd = (CFtpDir *) pvFtpDir;
  426. return FtpItemID_CompareIDsInt(COL_NAME, pfd->m_pidl, pidl, FCMP_NORMAL);
  427. }
  428. HRESULT CFtpDir::_SetFtpDir(CFtpSite * pfs, CFtpDir * pfd, LPCITEMIDLIST pidl)
  429. {
  430. if (FtpID_IsServerItemID(pidl))
  431. pidl = _ILNext(pidl);
  432. // We don't want pfd->m_pidlFtpDir to include the virtual root.
  433. if (pfd->GetFtpSite()->HasVirtualRoot())
  434. {
  435. LPITEMIDLIST pidlIterate = (LPITEMIDLIST) pidl;
  436. LPITEMIDLIST pidlVRootIterate = (LPITEMIDLIST) pfd->GetFtpSite()->GetVirtualRootReference();
  437. ASSERT(!FtpID_IsServerItemID(pidl) && !FtpID_IsServerItemID(pidlVRootIterate));
  438. // Let's see if pidl starts off with
  439. while (!ILIsEmpty(pidlVRootIterate) && !ILIsEmpty(pidlIterate) &&
  440. FtpItemID_IsEqual(pidlVRootIterate, pidlIterate))
  441. {
  442. pidlVRootIterate = _ILNext(pidlVRootIterate);
  443. pidlIterate = _ILNext(pidlIterate);
  444. }
  445. if (ILIsEmpty(pidlVRootIterate))
  446. pidl = (LPCITEMIDLIST)pidlIterate;
  447. }
  448. Pidl_Set(&pfd->m_pidlFtpDir, pidl);
  449. return S_OK;
  450. }
  451. /*****************************************************************************\
  452. FUNCTION: CFtpDir_Create
  453. DESCRIPTION:
  454. Create a brand new FtpDir structure.
  455. \*****************************************************************************/
  456. HRESULT CFtpDir_Create(CFtpSite * pfs, LPCITEMIDLIST pidl, CFtpDir ** ppfd)
  457. {
  458. CFtpDir * pfd = new CFtpDir();
  459. HRESULT hr = E_OUTOFMEMORY;
  460. ASSERT(pfs);
  461. if (pfd)
  462. {
  463. // WARNING: No ref held because it's a back pointer.
  464. // This requires that the parent (CFtpSite) always
  465. // out live this object.
  466. pfd->m_pfs = pfs;
  467. Pidl_Set(&pfd->m_pidl, pidl);
  468. if (pfd->m_pidl)
  469. hr = pfd->_SetFtpDir(pfs, pfd, pidl);
  470. else
  471. IUnknown_Set(&pfd, NULL);
  472. }
  473. *ppfd = pfd;
  474. ASSERT(*ppfd ? SUCCEEDED(hr) : FAILED(hr));
  475. return hr;
  476. }
  477. /****************************************************\
  478. Constructor
  479. \****************************************************/
  480. CFtpDir::CFtpDir() : m_cRef(1)
  481. {
  482. DllAddRef();
  483. // This needs to be allocated in Zero Inited Memory.
  484. // Assert that all Member Variables are inited to Zero.
  485. ASSERT(!m_pfs);
  486. ASSERT(!m_pflHfpl);
  487. ASSERT(!m_pfgMotd);
  488. ASSERT(!m_pidl);
  489. LEAK_ADDREF(LEAK_CFtpDir);
  490. }
  491. /****************************************************\
  492. Destructor
  493. \****************************************************/
  494. CFtpDir::~CFtpDir()
  495. {
  496. // WARNING: m_pfs is a back pointer that doesn't have a ref.
  497. // m_pfs)
  498. IUnknown_Set(&m_pflHfpl, NULL);
  499. IUnknown_Set(&m_pfgMotd, NULL);
  500. if (m_pidl) // Win95's Shell32.dll crashes with ILFree(NULL)
  501. ILFree(m_pidl);
  502. DllRelease();
  503. LEAK_DELREF(LEAK_CFtpDir);
  504. }
  505. //===========================
  506. // *** IUnknown Interface ***
  507. //===========================
  508. ULONG CFtpDir::AddRef()
  509. {
  510. m_cRef++;
  511. return m_cRef;
  512. }
  513. ULONG CFtpDir::Release()
  514. {
  515. ASSERT(m_cRef > 0);
  516. m_cRef--;
  517. if (m_cRef > 0)
  518. return m_cRef;
  519. delete this;
  520. return 0;
  521. }
  522. HRESULT CFtpDir::QueryInterface(REFIID riid, void **ppvObj)
  523. {
  524. if (IsEqualIID(riid, IID_IUnknown))
  525. {
  526. *ppvObj = SAFECAST(this, IUnknown*);
  527. }
  528. else
  529. {
  530. TraceMsg(TF_FTPQI, "CFtpDir::QueryInterface() failed.");
  531. *ppvObj = NULL;
  532. return E_NOINTERFACE;
  533. }
  534. AddRef();
  535. return S_OK;
  536. }