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.

636 lines
21 KiB

  1. /*****************************************************************************
  2. *
  3. * ftpeidl.cpp - IEnumIDList interface
  4. *
  5. * FtpNameCache
  6. *
  7. * Enumerating an FTP site is an expensive operation, because
  8. * it can entail dialing the phone, connecting to an ISP, then
  9. * connecting to the site, logging in, cd'ing to the appropriate
  10. * location, pumping over an "ls" command, parsing the result,
  11. * then closing the connection.
  12. *
  13. * So we cache the results of an enumeration inside a pidl list.
  14. * If the user does a REFRESH, then we toss the list and create
  15. * a new one.
  16. *
  17. * NOTE! that the WinINet API does not allow a FindFirst to be
  18. * interrupted. In other words, once you do an FtpFindFirst,
  19. * you must read the directory to completion and close the
  20. * handle before you can do anything else to the site.
  21. *
  22. * As a result, we cannot use lazy evaluation on the enumerated
  23. * contents. (Not that it helps any, because WinINet will just
  24. * do an "ls", parse the output, and then hand the items back
  25. * one element at a time via FtpFindNext. You may as well retrieve
  26. * them all the moment they're ready.)
  27. *
  28. \*****************************************************************************/
  29. #include "priv.h"
  30. #include "ftpeidl.h"
  31. #include "view.h"
  32. #include "util.h"
  33. /*****************************************************************************
  34. *
  35. * We actually cache the result of the enumeration in the parent
  36. * FtpDir, because FTP enumeration is very expensive.
  37. *
  38. * Since DVM_REFRESH forces us to re-enumerate, but we might have
  39. * outstanding IEnumIDList's, we need to treat the object cache
  40. * as yet another object that needs to be refcounted.
  41. *
  42. *****************************************************************************/
  43. /*****************************************************************************
  44. * _fFilter
  45. *
  46. * Decides whether the file attributes agree with the filter criteria.
  47. *
  48. * If hiddens are excluded, then exclude hiddens. (Duh.)
  49. *
  50. * Else, include or exclude based on folder/nonfolder-ness.
  51. *
  52. * Let's look at that expression in slow motion.
  53. *
  54. * "The attributes pass the filter if both...
  55. * (1) it passes the INCLUDEHIDDEN criterion, and
  56. * (2) it passes the FOLDERS/NONFOLDERS criterion.
  57. *
  58. * The INCLUDEHIDDEN criterion is passed if FILE_ATTRIBUTE_HIDDEN
  59. * implies SHCONTF_INCLUDEHIDDEN.
  60. *
  61. * The FOLDERS/NONFOLDERS criterion is passed if the appropriate bit
  62. * is set in the shcontf, based on the actual type of the file."
  63. *****************************************************************************/
  64. BOOL CFtpEidl::_fFilter(DWORD shcontf, DWORD dwFAFLFlags)
  65. {
  66. BOOL fResult = FALSE;
  67. if (shcontf & SHCONTF_FOLDERS)
  68. fResult |= dwFAFLFlags & FILE_ATTRIBUTE_DIRECTORY;
  69. if (shcontf & SHCONTF_NONFOLDERS)
  70. fResult |= !(dwFAFLFlags & FILE_ATTRIBUTE_DIRECTORY);
  71. if ((dwFAFLFlags & FILE_ATTRIBUTE_HIDDEN) && !(shcontf & SHCONTF_INCLUDEHIDDEN))
  72. fResult = FALSE;
  73. return fResult;
  74. }
  75. /*****************************************************************************\
  76. * _AddFindDataToPidlList
  77. *
  78. * Add information in a WIN32_FIND_DATA to the cache.
  79. * Except that dot and dotdot don't go in.
  80. \*****************************************************************************/
  81. HRESULT CFtpEidl::_AddFindDataToPidlList(LPCITEMIDLIST pidl)
  82. {
  83. HRESULT hr = E_FAIL;
  84. if (EVAL(m_pflHfpl))
  85. {
  86. ASSERT(IsValidPIDL(pidl));
  87. hr = m_pflHfpl->InsertSorted(pidl);
  88. }
  89. return hr;
  90. }
  91. /*****************************************************************************\
  92. FUNCTION: _HandleSoftLinks
  93. DESCRIPTION:
  94. A softlink is a file on an UNIX server that reference another file or
  95. directory. We can detect these by the fact that (pwfd->dwFileAttribes == 0).
  96. If that is true, we have some work to do. First we find out if it's a file
  97. or a directory by trying to ChangeCurrentWorking directories into it. If we
  98. can we turn the dwFileAttributes from 0 to (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT).
  99. If it's just a softlink to a file, then we change it to
  100. (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_REPARSE_POINT). We later use the
  101. FILE_ATTRIBUTE_REPARSE_POINT attribute to put the shortcut overlay on it to
  102. que the user.
  103. RETURN VALUE:
  104. HRESULT - If FAILED() is returned, the item will not be added to the
  105. list view.
  106. \*****************************************************************************/
  107. HRESULT CFtpEidl::_HandleSoftLinks(HINTERNET hint, LPITEMIDLIST pidl, LPWIRESTR pwCurrentDir, DWORD cchSize)
  108. {
  109. HRESULT hr = S_OK;
  110. // Is it a softlink? It just came in off the wire and wininet returns 0 (zero)
  111. // for softlinks. This function will determine if it's a SoftLink to a file
  112. // or a directory and then set FILE_ATTRIBUTE_REPARSE_POINT or
  113. // (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT) respectively.
  114. if (0 == FtpPidl_GetAttributes(pidl))
  115. {
  116. LPCWIRESTR pwWireFileName = FtpPidl_GetFileWireName(pidl);
  117. // Yes, so I will need to attempt to CD into that directory to test if it's a directory.
  118. // I need to get back because ".." won't work. I will cache the return so I don't keep
  119. // getting it if there is a directory full of them.
  120. // Did we get the current directory yet? This is the bread crums so I can
  121. // find my way back.
  122. if (!pwCurrentDir[0])
  123. EVAL(SUCCEEDED(FtpGetCurrentDirectoryWrap(hint, TRUE, pwCurrentDir, cchSize)));
  124. // Yes, so is it a directory?
  125. if (SUCCEEDED(FtpSetCurrentDirectoryPidlWrap(hint, TRUE, pidl, FALSE, FALSE))) // Relative CD
  126. {
  127. // Does it have a virtual root?
  128. if (m_pfd->GetFtpSite()->HasVirtualRoot())
  129. {
  130. LPCITEMIDLIST pidlVirtualRoot = m_pfd->GetFtpSite()->GetVirtualRootReference();
  131. LPITEMIDLIST pidlSoftLinkDest = NULL;
  132. CWireEncoding * pwe = m_pfd->GetFtpSite()->GetCWireEncoding();
  133. // Yes, so we need to make sure this dir softlink doesn't point
  134. // outside of the virtual root, or it would cause invalid FTP URLs.
  135. // File SoftLinks are fine because the old FTP Code abuses FTP URLs.
  136. // I'm just not ready to drop my morals just yet.
  137. if (SUCCEEDED(FtpGetCurrentDirectoryPidlWrap(hint, TRUE, pwe, &pidlSoftLinkDest)))
  138. {
  139. if (!FtpItemID_IsParent(pidlVirtualRoot, pidlSoftLinkDest))
  140. {
  141. // This is a Softlink or HardLink to a directory outside of the virtual root.
  142. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Skip this one.
  143. }
  144. ILFree(pidlSoftLinkDest);
  145. }
  146. }
  147. // Return to where we came from.
  148. //TraceMsg(TF_WININET_DEBUG, "_HandleSoftLinks FtpSetCurrentDirectory(%hs) worked", pwWireFileName);
  149. EVAL(SUCCEEDED(FtpSetCurrentDirectoryWrap(hint, TRUE, pwCurrentDir))); // Absolute CD
  150. FtpPidl_SetAttributes(pidl, (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT));
  151. FtpPidl_SetFileItemType(pidl, TRUE);
  152. }
  153. else // No, it's one of those files w/o extensions.
  154. {
  155. TraceMsg(TF_WININET_DEBUG, "_HandleSoftLinks FtpSetCurrentDirectory(%s) failed", pwWireFileName);
  156. FtpPidl_SetAttributes(pidl, (FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_REPARSE_POINT));
  157. FtpPidl_SetFileItemType(pidl, FALSE);
  158. }
  159. }
  160. return hr;
  161. }
  162. /*****************************************************************************\
  163. * CFtpEidl::_PopulateItem
  164. *
  165. * Fill a cache with stuff.
  166. *
  167. * EEK! Some ftp servers (e.g., ftp.funet.fi) run with ls -F!
  168. * This means that things get "*" appended to them if they are executable.
  169. \*****************************************************************************/
  170. HRESULT CFtpEidl::_PopulateItem(HINTERNET hint0, HINTPROCINFO * phpi)
  171. {
  172. HRESULT hr = S_OK;
  173. HINTERNET hint;
  174. LPITEMIDLIST pidl;
  175. CMultiLanguageCache cmlc;
  176. CWireEncoding * pwe = m_pfd->GetFtpSite()->GetCWireEncoding();
  177. if (phpi->psb)
  178. {
  179. phpi->psb->SetStatusMessage(IDS_LS, NULL);
  180. EVAL(SUCCEEDED(_SetStatusBarZone(phpi->psb, phpi->pfd->GetFtpSite())));
  181. }
  182. hr = FtpFindFirstFilePidlWrap(hint0, TRUE, &cmlc, pwe, NULL, &pidl,
  183. (INTERNET_NO_CALLBACK | INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RESYNCHRONIZE | INTERNET_FLAG_RELOAD), NULL, &hint);
  184. if (hint)
  185. {
  186. WIRECHAR wCurrentDir[MAX_PATH]; // Used for _HandleSoftLinks().
  187. wCurrentDir[0] = 0;
  188. if (EVAL(m_pff))
  189. {
  190. m_pff->AddToUrlHistory(m_pfd->GetPidlReference());
  191. }
  192. //TraceMsg(TF_FTP_OTHER, "CFtpEidl::_PopulateItem() adding Name=%s", wCurrentDir);
  193. if (pidl && SUCCEEDED(_HandleSoftLinks(hint0, pidl, wCurrentDir, ARRAYSIZE(wCurrentDir))))
  194. hr = _AddFindDataToPidlList(pidl);
  195. ILFree(pidl);
  196. while (SUCCEEDED(hr))
  197. {
  198. hr = InternetFindNextFilePidlWrap(hint, TRUE, &cmlc, pwe, &pidl);
  199. if (SUCCEEDED(hr))
  200. {
  201. //TraceMsg(TF_FTP_OTHER, "CFtpEidl::_PopulateItem() adding Name=%hs", FtpPidl_GetLastItemWireName(pidl));
  202. // We may decide to not add it for some reasons.
  203. if (SUCCEEDED(_HandleSoftLinks(hint0, pidl, wCurrentDir, ARRAYSIZE(wCurrentDir))))
  204. hr = _AddFindDataToPidlList(pidl);
  205. ILFree(pidl);
  206. }
  207. else
  208. {
  209. // We failed to get the next file.
  210. if (HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES) != hr)
  211. {
  212. DisplayWininetError(phpi->hwnd, TRUE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_FOLDERENUM, IDS_FTPERR_WININET, MB_OK, NULL);
  213. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Clean error to indicate we already displayed the error and don't need to do it later.
  214. }
  215. else
  216. hr = S_OK; // That's fine if there aren't any more files to get
  217. break; // We are done here.
  218. }
  219. }
  220. EVAL(SUCCEEDED(pwe->ReSetCodePages(&cmlc, m_pflHfpl)));
  221. InternetCloseHandle(hint);
  222. }
  223. else
  224. {
  225. // This will happen in two cases.
  226. // 1. The folder is empty. (GetLastError() == ERROR_NO_MORE_FILES)
  227. // 2. The user doesn't have enough access to view the folder. (GetLastError() == ERROR_INTERNET_EXTENDED_ERROR)
  228. if (HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES) != hr)
  229. {
  230. DisplayWininetError(phpi->hwnd, TRUE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_OPENFOLDER, IDS_FTPERR_WININET, MB_OK, NULL);
  231. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Clean error to indicate we already displayed the error and don't need to do it later.
  232. WININET_ASSERT(SUCCEEDED(hr));
  233. }
  234. else
  235. hr = S_OK;
  236. TraceMsg(TF_FTP_IDENUM, "CFtpEnum_New() - Can't opendir. hres=%#08lx.", hr);
  237. }
  238. if (phpi->psb)
  239. phpi->psb->SetStatusMessage(IDS_EMPTY, NULL);
  240. return hr;
  241. }
  242. /*****************************************************************************\
  243. * CFtpEidl::_Init
  244. \*****************************************************************************/
  245. HRESULT CFtpEidl::_Init(void)
  246. {
  247. HRESULT hr = S_FALSE;
  248. ASSERT(m_pfd);
  249. IUnknown_Set(&m_pflHfpl, NULL);
  250. m_pflHfpl = m_pfd->GetHfpl(); // Use cached copy if it exists.
  251. if (m_pflHfpl)
  252. {
  253. // We will just use the previous copy because we already have the contents.
  254. // TODO: Maybe we want to purge the results if a certain amount of time as ellapsed.
  255. m_fInited = TRUE;
  256. hr = S_OK;
  257. }
  258. else if (!m_pfd->GetFtpSite()->IsSiteBlockedByRatings(m_hwndOwner))
  259. {
  260. CFtpPidlList_Create(0, NULL, &m_pflHfpl);
  261. if (m_pflHfpl)
  262. {
  263. CStatusBar * psb = GetCStatusBarFromDefViewSite(_punkSite);
  264. ASSERT(!m_pfd->IsRoot());
  265. //TraceMsg(TF_ALWAYS, "CFtpEidl::_Init() and enumerating");
  266. hr = m_pfd->WithHint(psb, m_hwndOwner, CFtpEidl::_PopulateItemCB, this, _punkSite, m_pff);
  267. if (SUCCEEDED(hr))
  268. {
  269. m_pfd->SetCache(m_pflHfpl);
  270. m_fInited = TRUE;
  271. hr = S_OK;
  272. }
  273. else
  274. IUnknown_Set(&m_pflHfpl, NULL);
  275. }
  276. }
  277. return hr;
  278. }
  279. /*****************************************************************************
  280. * CFtpEidl::_NextOne
  281. *****************************************************************************/
  282. LPITEMIDLIST CFtpEidl::_NextOne(DWORD * pdwIndex)
  283. {
  284. LPITEMIDLIST pidl = NULL;
  285. LPITEMIDLIST pidlResult = NULL;
  286. if (m_pflHfpl)
  287. {
  288. while ((*pdwIndex < (DWORD) m_pflHfpl->GetCount()) && (pidl = m_pflHfpl->GetPidl(*pdwIndex)))
  289. {
  290. ASSERT(IsValidPIDL(pidl));
  291. (*pdwIndex)++;
  292. if (_fFilter(m_shcontf, FtpPidl_GetAttributes(pidl)))
  293. {
  294. pidlResult = ILClone(pidl);
  295. break; // We don't need to search any more.
  296. }
  297. }
  298. }
  299. return pidlResult;
  300. }
  301. //===========================
  302. // *** IEnumIDList Interface ***
  303. //===========================
  304. /*****************************************************************************
  305. *
  306. * IEnumIDList::Next
  307. *
  308. * Creates a brand new enumerator based on an existing one.
  309. *
  310. *
  311. * OLE random documentation of the day: IEnumXXX::Next.
  312. *
  313. * rgelt - Receives an array of size celt (or larger).
  314. *
  315. * "Receives an array"? No, it doesn't receive an array.
  316. * It *is* an array. The array receives *elements*.
  317. *
  318. * "Or larger"? Does this mean I can return more than the caller
  319. * asked for? No, of course not, because the caller didn't allocate
  320. * enough memory to hold that many return values.
  321. *
  322. * No semantics are assigned to the possibility of celt = 0.
  323. * Since I am a mathematician, I treat it as vacuous success.
  324. *
  325. * pcelt is documented as an INOUT parameter, but no semantics
  326. * are assigned to its input value.
  327. *
  328. * The dox don't say that you are allowed to return *pcelt < celt
  329. * for reasons other than "no more elements", but the shell does
  330. * it everywhere, so maybe it's legal...
  331. *
  332. *****************************************************************************/
  333. HRESULT CFtpEidl::Next(ULONG celt, LPITEMIDLIST * rgelt, ULONG *pceltFetched)
  334. {
  335. HRESULT hr = S_OK;
  336. LPITEMIDLIST pidl = NULL;
  337. DWORD dwIndex;
  338. // The shell on pre-NT5 enums us w/o ole initialized which causes problems
  339. // when we call CoCreateInstance(). This happens in the thunking code
  340. // of encode.cpp when thunking strings.
  341. HRESULT hrOleInit = SHOleInitialize(0);
  342. if (pceltFetched) // In case of failure.
  343. {
  344. *pceltFetched = 0;
  345. }
  346. if (m_fDead)
  347. return E_FAIL;
  348. if (!m_fInited)
  349. {
  350. hr = _Init();
  351. if (FAILED(hr) && (HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr))
  352. {
  353. // Did we need to redirect because of a new password or username?
  354. if (HRESULT_FROM_WIN32(ERROR_NETWORK_ACCESS_DENIED) == hr)
  355. {
  356. m_fDead = TRUE;
  357. hr = E_FAIL;
  358. }
  359. else if (!m_fErrorDisplayed)
  360. {
  361. DisplayWininetError(m_hwndOwner, FALSE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_GETDIRLISTING, IDS_FTPERR_WININET, MB_OK, NULL);
  362. m_fErrorDisplayed = TRUE;
  363. }
  364. }
  365. }
  366. if (S_OK == hr)
  367. {
  368. // Do they want more and do we have more to give?
  369. for (dwIndex = 0; (dwIndex < celt) && (pidl = _NextOne(&m_nIndex)); dwIndex++)
  370. rgelt[dwIndex] = pidl; // Yes, so give away...
  371. if (pceltFetched)
  372. *pceltFetched = dwIndex;
  373. // Were we able to give any?
  374. if (0 == dwIndex)
  375. hr = S_FALSE;
  376. }
  377. SHOleUninitialize(hrOleInit);
  378. return hr;
  379. }
  380. /*****************************************************************************
  381. * IEnumIDList::Skip
  382. *****************************************************************************/
  383. HRESULT CFtpEidl::Skip(ULONG celt)
  384. {
  385. m_nIndex += celt;
  386. return S_OK;
  387. }
  388. /*****************************************************************************
  389. * IEnumIDList::Reset
  390. *****************************************************************************/
  391. HRESULT CFtpEidl::Reset(void)
  392. {
  393. m_fErrorDisplayed = FALSE;
  394. if (!m_fInited)
  395. _Init();
  396. m_nIndex = 0;
  397. return S_OK;
  398. }
  399. /*****************************************************************************\
  400. * IEnumIDList::Clone
  401. *
  402. * Creates a brand new enumerator based on an existing one.
  403. \*****************************************************************************/
  404. HRESULT CFtpEidl::Clone(IEnumIDList **ppenum)
  405. {
  406. return CFtpEidl_Create(m_pfd, m_pff, m_hwndOwner, m_shcontf, m_nIndex, ppenum);
  407. }
  408. /*****************************************************************************\
  409. * CFtpEidl_Create
  410. *
  411. * Creates a brand new enumerator based on an ftp site.
  412. \*****************************************************************************/
  413. HRESULT CFtpEidl_Create(CFtpDir * pfd, CFtpFolder * pff, HWND hwndOwner, DWORD shcontf, IEnumIDList ** ppenum)
  414. {
  415. CFtpEidl * pfe;
  416. HRESULT hres = CFtpEidl_Create(pfd, pff, hwndOwner, shcontf, &pfe);
  417. *ppenum = NULL;
  418. if (pfe)
  419. {
  420. hres = pfe->QueryInterface(IID_IEnumIDList, (LPVOID *) ppenum);
  421. pfe->Release();
  422. }
  423. return hres;
  424. }
  425. /*****************************************************************************
  426. *
  427. * CFtpEidl_Create
  428. *
  429. * Creates a brand new enumerator based on an ftp site.
  430. *
  431. *****************************************************************************/
  432. HRESULT CFtpEidl_Create(CFtpDir * pfd, CFtpFolder * pff, HWND hwndOwner, DWORD shcontf, CFtpEidl ** ppfe)
  433. {
  434. CFtpEidl * pfe = new CFtpEidl();
  435. HRESULT hr = E_OUTOFMEMORY;
  436. ASSERT(pfd && pff && ppfe);
  437. *ppfe = pfe;
  438. if (pfe)
  439. {
  440. ATOMICRELEASE(pfe->m_pm);
  441. pfe->m_pm = pff->GetIMalloc();
  442. IUnknown_Set(&pfe->m_pff, pff);
  443. IUnknown_Set(&pfe->m_pfd, pfd);
  444. pfe->m_pflHfpl = pfd->GetHfpl();
  445. pfe->m_shcontf = shcontf;
  446. pfe->m_hwndOwner = hwndOwner;
  447. }
  448. return hr;
  449. }
  450. /*****************************************************************************\
  451. * CFtpEidl_Create
  452. *
  453. * Creates a brand new enumerator based on an ftp site.
  454. \*****************************************************************************/
  455. HRESULT CFtpEidl_Create(CFtpDir * pfd, CFtpFolder * pff, HWND hwndOwner, DWORD shcontf, DWORD dwIndex, IEnumIDList ** ppenum)
  456. {
  457. CFtpEidl * pfe;
  458. HRESULT hres = CFtpEidl_Create(pfd, pff, hwndOwner, shcontf, &pfe);
  459. if (SUCCEEDED(hres))
  460. {
  461. pfe->m_nIndex = dwIndex;
  462. hres = pfe->QueryInterface(IID_IEnumIDList, (LPVOID *) ppenum);
  463. ASSERT(SUCCEEDED(hres));
  464. pfe->Release();
  465. }
  466. return hres;
  467. }
  468. /****************************************************\
  469. Constructor
  470. \****************************************************/
  471. CFtpEidl::CFtpEidl() : m_cRef(1)
  472. {
  473. DllAddRef();
  474. // This needs to be allocated in Zero Inited Memory.
  475. // Assert that all Member Variables are inited to Zero.
  476. ASSERT(!m_fInited);
  477. ASSERT(!m_nIndex);
  478. ASSERT(!m_shcontf);
  479. ASSERT(!m_pflHfpl);
  480. ASSERT(!m_pfd);
  481. ASSERT(!m_pm);
  482. ASSERT(!m_hwndOwner);
  483. ASSERT(!m_fInited);
  484. ASSERT(!m_fDead);
  485. LEAK_ADDREF(LEAK_CFtpEidl);
  486. }
  487. /****************************************************\
  488. Destructor
  489. \****************************************************/
  490. CFtpEidl::~CFtpEidl()
  491. {
  492. IUnknown_Set(&m_pflHfpl, NULL);
  493. IUnknown_Set(&m_pm, NULL);
  494. IUnknown_Set(&m_pfd, NULL);
  495. IUnknown_Set(&m_pff, NULL);
  496. DllRelease();
  497. LEAK_DELREF(LEAK_CFtpEidl);
  498. }
  499. //===========================
  500. // *** IUnknown Interface ***
  501. //===========================
  502. ULONG CFtpEidl::AddRef()
  503. {
  504. m_cRef++;
  505. return m_cRef;
  506. }
  507. ULONG CFtpEidl::Release()
  508. {
  509. ASSERT(m_cRef > 0);
  510. m_cRef--;
  511. if (m_cRef > 0)
  512. return m_cRef;
  513. delete this;
  514. return 0;
  515. }
  516. HRESULT CFtpEidl::QueryInterface(REFIID riid, void **ppvObj)
  517. {
  518. if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IEnumIDList))
  519. {
  520. *ppvObj = SAFECAST(this, IEnumIDList*);
  521. }
  522. else if (IsEqualIID(riid, IID_IObjectWithSite))
  523. {
  524. *ppvObj = SAFECAST(this, IObjectWithSite*);
  525. }
  526. else
  527. {
  528. TraceMsg(TF_FTPQI, "CFtpEidl::QueryInterface() failed.");
  529. *ppvObj = NULL;
  530. return E_NOINTERFACE;
  531. }
  532. AddRef();
  533. return S_OK;
  534. }