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.

2466 lines
87 KiB

  1. /*****************************************************************************\
  2. FILE: ftpfoldr.h
  3. DESCRIPTION:
  4. This class inherits from CBaseFolder for a base ShellFolder implementation
  5. of IShellFolder and overrides methods to give Ftp Specific features.
  6. _UNDOCUMENTED_: The shell violates Apartment model threading
  7. when doing background enumeration, so even though this DLL is
  8. marked as Apartment model, IShellFolder and IEnumIDList must
  9. be written with the free threading model with respect to anything
  10. that IEnumIDList can do in the background.
  11. This means that you'll see lots of ENTER_CRITICAL() and
  12. LEAVE_CRITICAL() calls when your brain would say, "I don't
  13. need to do that because I'm Apartment-model." I'll try to
  14. point them out as they occur; look for the marker _MT_.
  15. CAUTION! Internally, our property sheet handler also invokes
  16. methods on CFtpFolder on the wrong thread, so it's not just the
  17. shell that is weird.
  18. \*****************************************************************************/
  19. #include "priv.h"
  20. #include "ftpfoldr.h"
  21. #include "ftpurl.h"
  22. #include "ftppidl.h"
  23. #include "ftpicon.h"
  24. #include "view.h"
  25. #include "proxycache.h"
  26. #include <idhidden.h>
  27. #define FEATURE_SOFTLINK_SHORTCUT_ICONOVERLAY
  28. // {A11501B3-6EA4-11d2-B679-006097DF5BD4} Private to msieftp.dll
  29. const GUID IID_CFtpFolder = { 0xa11501b3, 0x6ea4, 0x11d2, { 0xb6, 0x79, 0x0, 0x60, 0x97, 0xdf, 0x5b, 0xd4 } };
  30. /*****************************************************************************
  31. *
  32. * More const statics.
  33. *
  34. *****************************************************************************/
  35. #pragma BEGIN_CONST_DATA
  36. WORD c_wZero = 0; /* As promised in ftpview.h */
  37. /*
  38. * String separator used when building relative names.
  39. */
  40. char c_szSlash[] = "/";
  41. #pragma END_CONST_DATA
  42. HRESULT CFtpFolder::_AddToUrlHistory(LPCWSTR pwzUrl)
  43. {
  44. HRESULT hr = S_OK;
  45. if (!m_puhs)
  46. {
  47. hr = CoCreateInstance(CLSID_CUrlHistory, NULL, CLSCTX_INPROC_SERVER, IID_IUrlHistoryStg, (void **)&m_puhs);
  48. }
  49. if (m_puhs)
  50. {
  51. hr = m_puhs->AddUrl(pwzUrl, pwzUrl, 0);
  52. }
  53. return hr;
  54. }
  55. /* Not yet needed
  56. HRESULT CFtpFolder::AddToUrlHistory(LPCTSTR pszUrl)
  57. {
  58. return _AddToUrlHistory(wzUrl);
  59. }
  60. */
  61. HRESULT CFtpFolder::AddToUrlHistory(LPCITEMIDLIST pidl)
  62. {
  63. WCHAR wzUrl[MAX_URL_STRING];
  64. HRESULT hr = UrlCreateFromPidlW(pidl, SHGDN_FORPARSING, wzUrl, ARRAYSIZE(wzUrl), (ICU_ESCAPE | ICU_USERNAME), TRUE);
  65. if (EVAL(SUCCEEDED(hr)))
  66. {
  67. hr = _AddToUrlHistory(wzUrl);
  68. }
  69. return hr;
  70. }
  71. CWireEncoding * CFtpFolder::GetCWireEncoding(void)
  72. {
  73. // GetFtpDir() may return NULL when we aren't rooted in an FTP server.
  74. CFtpDir * pfd = GetFtpDir();
  75. CWireEncoding * pwe = NULL;
  76. if (pfd)
  77. {
  78. pwe = pfd->GetFtpSite()->GetCWireEncoding();
  79. pfd->Release();
  80. }
  81. return pwe;
  82. }
  83. HRESULT CFtpFolder::_FixQuestionablePidl(LPCITEMIDLIST pidl)
  84. {
  85. HRESULT hr = S_OK;
  86. // NOTE: In the future, we may want to hit the server to
  87. // disambiguate this.
  88. /*
  89. BOOL fIsDir = TRUE;
  90. LPCSTR pszName = FtpPidl_GetLastItemName(pidl);
  91. // Can we get the name?
  92. if (EVAL(pszName))
  93. {
  94. // Is the file extension non-NULL? (Meaning it exists)
  95. if ('\0' != *PathFindExtensionA(pszName))
  96. fIsDir = FALSE; // Yes, so asume it's a file.
  97. }
  98. hr = FtpPidl_SetFileItemType((LPITEMIDLIST) pidl, fIsDir);
  99. */
  100. return hr;
  101. }
  102. BOOL CFtpFolder::_IsServerVMS(LPCITEMIDLIST pidl)
  103. {
  104. BOOL fIsServerVMS = FALSE; // Assume TRUE
  105. CFtpSite * pfs;
  106. // Some caller's don't pass the Server ID so let's assume
  107. // that they already made it past that point.
  108. if (FtpID_IsServerItemID(pidl) &&
  109. EVAL(SUCCEEDED(SiteCache_PidlLookup(pidl, FALSE, m_pm, &pfs))))
  110. {
  111. fIsServerVMS = pfs->IsServerVMS();
  112. pfs->Release();
  113. }
  114. return fIsServerVMS;
  115. }
  116. /****************************************************\
  117. FUNCTION: _IsProxyBlockingSite
  118. DESCRIPTION:
  119. We need to detect if we cannot connect to the
  120. site because the proxy is a CERN or CERN type proxy
  121. that blocks ALL ftp access. If this is true, we
  122. need to inform the user can fall all
  123. IShellFolder::BindToObject() calls.
  124. We will detect this case by doing the normal
  125. WININET FTP InternetConnect(). If that returns
  126. hr=0x80002EE7 (ERROR_INTERNET_NAME_NOT_RESOLVED)
  127. then it could either be that the name doesn't exist,
  128. or there is a CERN proxy blocking the call. We will
  129. then try connect the CERN method which will tell us
  130. if it's the proxy that is blocking us.
  131. \****************************************************/
  132. BOOL CFtpFolder::_IsProxyBlockingSite(LPCITEMIDLIST pidl)
  133. {
  134. BOOL fCacheResult;
  135. BOOL fResult = TRUE; // Assume TRUE
  136. CFtpDir * pfd;
  137. if (ProxyCache_IsProxyBlocking(pidl, &fCacheResult))
  138. return fCacheResult;
  139. if (EVAL(pfd = GetFtpDirFromPidl(pidl)))
  140. {
  141. HRESULT hr = pfd->WithHint(NULL, NULL, NULL, NULL, NULL, this);
  142. // WithHint() often fails if a CERN style proxy blocks REAL wininet
  143. // access to the server. If the server name is a DNS name, the error
  144. // returned will be ERROR_INTERNET_NAME_NOT_RESOLVED because that is
  145. // what is returned by the CERN proxy. If the server name is an IP
  146. // Address, wininet will skip the CERN proxy and try to find it on the
  147. // intranet. If not found (because it's past the firewall), then,
  148. // the attempt will timeout with ERROR_INTERNET_TIMEOUT. We need
  149. // to treat this as a proxy block if and ONLY if the server name is an
  150. // IP address because DNS names can timeout for other reasons. Us
  151. // treating IP server name timeouts as proxy blocks is going to have
  152. // to be tolerated because wininet won't handle this case. It happens
  153. // very infrequently so I don't care that much.
  154. //
  155. // Some authentication proxies fail with: ERROR_INTERNET_CANNOT_CONNECT
  156. // We would like to fall back in that case, however, that may include
  157. // other cases like the server refusing to allow us in.
  158. // (password or too many logged in users?)
  159. // It would be great if ERROR_INTERNET_INVALID_PROXY_REQUEST or
  160. // ERROR_INTERNET_CLIENT_AUTH_NOT_SETUP could be used.
  161. //
  162. if ((HRESULT_FROM_WIN32(ERROR_INTERNET_NAME_NOT_RESOLVED) == hr) ||
  163. (HRESULT_FROM_WIN32(ERROR_INTERNET_CANNOT_CONNECT) == hr) ||
  164. ((HRESULT_FROM_WIN32(ERROR_INTERNET_TIMEOUT) == hr) && !FtpPidl_IsDNSServerName(pidl)))
  165. {
  166. TCHAR szUrl[MAX_URL_STRING];
  167. if (EVAL(SUCCEEDED(UrlCreateFromPidl(pidl, SHGDN_FORPARSING, szUrl, ARRAYSIZE(szUrl), ICU_ESCAPE | ICU_USERNAME, FALSE))))
  168. {
  169. HINTERNET hintTemp;
  170. ASSERT(GetWininetSessionHandle());
  171. // For Web Proxies, InternetOpenUrl should work. The problem is that
  172. // some (Netscape's) don't work.
  173. if (SUCCEEDED(InternetOpenUrlWrap(GetWininetSessionHandle(), TRUE, szUrl, NULL, 0, INTERNET_FLAG_NO_UI, NULL, &hintTemp)))
  174. {
  175. InternetCloseHandle(hintTemp); // This did work, so we must have a CERN proxy.
  176. }
  177. else
  178. fResult = FALSE; // We aren't blocked by the proxy. (Wrong IP Addr or Name?)
  179. }
  180. }
  181. else
  182. fResult = FALSE; // We aren't blocked by the proxy.
  183. // Cache the result since finding out is so expensive.
  184. ProxyCache_SetProxyBlocking(pidl, fResult);
  185. pfd->Release();
  186. }
  187. return fResult;
  188. }
  189. /*****************************************************************************
  190. *
  191. * InvalidateCache
  192. *
  193. * Invalidate the pflHfpl cache in the corresponding FtpDir.
  194. *
  195. * _MT_: Note that the background enumerator calls this, so it must be
  196. * multithread-safe.
  197. *
  198. *****************************************************************************/
  199. void CFtpFolder::InvalidateCache(void)
  200. {
  201. CFtpDir * pfd = GetFtpDir();
  202. if (pfd)
  203. {
  204. // Should have created one on the GetHint()
  205. pfd->SetCache(0);
  206. pfd->Release();
  207. }
  208. }
  209. HRESULT CFtpFolder::_InitFtpSite(void)
  210. {
  211. HRESULT hr = S_OK;
  212. if (!m_pfs) // If we don't already got one...
  213. {
  214. ENTERCRITICAL;
  215. if (!m_pfs) // Did it get created while we were waiting
  216. {
  217. if (EVAL(GetPrivatePidlReference()))
  218. hr = SiteCache_PidlLookup(GetPrivatePidlReference(), TRUE, m_pm, &m_pfs);
  219. else
  220. {
  221. // Not initialized
  222. TraceMsg(TF_FTPISF, "CFtpFolder_GetFtpDir(%08x) NOT INITED", this);
  223. hr = E_FAIL;
  224. }
  225. }
  226. LEAVECRITICAL;
  227. }
  228. return hr;
  229. }
  230. /*****************************************************************************\
  231. FUNCTION: GetFtpDir
  232. DESCRIPTION:
  233. Say where our dir info is.
  234. We allocate the pfd only if somebody actually needs it, because
  235. Explorer does a lot of ILCompare's when you open a new folder,
  236. each of which creates a new IShellFolder for the sole purpose
  237. of calling CompareIDs. We don't want to go through all the
  238. hubbub of creating an FtpDir and FtpSite when we don't need one.
  239. _MT_: Note that the background enumerator calls this, so it must be
  240. multithread-safe. In such case, however, the IShellFolder is
  241. marked cBusy, so we don't have to worry about the this->pfd
  242. getting wiped out behind our back by a change of identity.
  243. \*****************************************************************************/
  244. CFtpDir * CFtpFolder::GetFtpDir(void)
  245. {
  246. HRESULT hres = S_OK;
  247. CFtpDir * pfd = NULL;
  248. _InitFtpSite(); // Okay if it fails.
  249. if (m_pfs)
  250. hres = m_pfs->GetFtpDir(GetPrivatePidlReference(), &pfd); // GetFtpDir can fail in out of memory
  251. return pfd;
  252. }
  253. CFtpDir * CFtpFolder::GetFtpDirFromPidl(LPCITEMIDLIST pidl)
  254. {
  255. HRESULT hres = S_OK;
  256. CFtpDir * pfd = NULL;
  257. CFtpSite * pfs = NULL;
  258. hres = SiteCache_PidlLookup(pidl, FALSE, m_pm, &pfs);
  259. if (pfs)
  260. {
  261. hres = pfs->GetFtpDir(pidl, &pfd);
  262. pfs->Release();
  263. }
  264. return pfd;
  265. }
  266. /*****************************************************************************\
  267. * GetItemAllocator
  268. *
  269. * Return today's pidl allocator.
  270. \*****************************************************************************/
  271. HRESULT CFtpFolder::GetItemAllocator(IMalloc **ppm)
  272. {
  273. HRESULT hr = E_FAIL;
  274. *ppm = NULL;
  275. if (EVAL(m_pm))
  276. {
  277. IUnknown_Set(ppm, m_pm);
  278. hr = S_OK;
  279. }
  280. else
  281. TraceMsg(TF_FTPISF, "CFtpFolder_GetItemAllocator(%08x) NOT INITED", this);
  282. return hr;
  283. }
  284. /*****************************************************************************\
  285. FUNCTION: GetUIObjectOfHfpl
  286. DESCRIPTION:
  287. _UNDOCUMENTED_: Nowhere is there a list of interfaces
  288. that "should be" supported. You just have to add lots of
  289. squirties and see what interfaces are asked for.
  290. _UNDOCUMENTED_: Nowhere is it mentioned that passing
  291. cidl = 0 (or the various other weird variants) means to
  292. get a UI object on the folder itself.
  293. _UNDOCUMENTED_: It is not mentioned whether the folder should
  294. be expected to handle cidl != 1 when asked for an IExtractIcon.
  295. I code defensively and handle the situation properly.
  296. IExtractIcon(0) extracts the icon for the folder itself.
  297. IExtractIcon(1) extracts the icon for the indicated pidl.
  298. IExtractIcon(n) extracts a generic "multi-document" icon.
  299. IContextMenu(0) produces a context menu for the folder itself.
  300. (Not used by the shell, but used by ourselves internally.)
  301. IContextMenu(n) produces a context menu for the multi-selection.
  302. IDataObject(0) ?? doesn't do anything
  303. IDataObject(n) produces a data object for the multi-selection.
  304. IDropTarget(0) produces a droptarget for the folder itself.
  305. (Not used by the shell, but used by ourselves internally.)
  306. IDropTarget(1) produces a droptarget for the single item.
  307. IShellView(0) ?? doesn't do anything
  308. IShellView(1) produces a shellview for the single item.
  309. (Nobody tries this yet, but I'm ready for it.)
  310. \*****************************************************************************/
  311. HRESULT CFtpFolder::GetUIObjectOfHfpl(HWND hwndOwner, CFtpPidlList * pflHfpl, REFIID riid, LPVOID * ppvObj, BOOL fFromCreateViewObject)
  312. {
  313. HRESULT hr = E_INVALIDARG;
  314. if (IsEqualIID(riid, IID_IExtractIconA) ||
  315. IsEqualIID(riid, IID_IExtractIconW) ||
  316. IsEqualIID(riid, IID_IQueryInfo))
  317. {
  318. hr = CFtpIcon_Create(this, pflHfpl, riid, ppvObj);
  319. //TraceMsg(TF_FTPISF, "CFtpFolder::GetUIObjectOfHfpl() CFtpIcon_Create() hr=%#08lx", hr);
  320. }
  321. else if (IsEqualIID(riid, IID_IContextMenu))
  322. {
  323. hr = CFtpMenu_Create(this, pflHfpl, hwndOwner, riid, ppvObj, fFromCreateViewObject);
  324. TraceMsg(TF_FTPISF, "CFtpFolder::GetUIObjectOfHfpl() CFtpMenu_Create() hr=%#08lx", hr);
  325. }
  326. else if (IsEqualIID(riid, IID_IDataObject))
  327. {
  328. hr = CFtpObj_Create(this, pflHfpl, riid, ppvObj); // Can fail in out of memory.
  329. TraceMsg(TF_FTPISF, "CFtpFolder::GetUIObjectOfHfpl() CFtpObj_Create() hr=%#08lx", hr);
  330. }
  331. else if (IsEqualIID(riid, IID_IDropTarget))
  332. {
  333. // This will fail when someone gets a property sheet on an FTP PIDL Shortcut
  334. // that has a file as the destination.
  335. hr = CreateSubViewObject(hwndOwner, pflHfpl, riid, ppvObj);
  336. TraceMsg(TF_FTPISF, "CFtpFolder::GetUIObjectOfHfpl() CreateSubViewObject() hr=%#08lx", hr);
  337. }
  338. else if (IsEqualIID(riid, IID_IShellView))
  339. {
  340. ASSERT(0); // Shouldn't happen
  341. }
  342. else if (IsEqualIID(riid, IID_IQueryAssociations))
  343. {
  344. IQueryAssociations * pqa;
  345. hr = AssocCreate(CLSID_QueryAssociations, IID_IQueryAssociations, (void **)&pqa);
  346. if (SUCCEEDED(hr))
  347. {
  348. hr = pqa->Init(0, L"Folder", NULL, NULL);
  349. if (SUCCEEDED(hr))
  350. *ppvObj = (void *)pqa;
  351. else
  352. pqa->Release();
  353. }
  354. }
  355. else
  356. {
  357. //TraceMsg(TF_FTPISF, "CFtpFolder::GetUIObjectOfHfpl() E_NOINTERFACE");
  358. hr = E_NOINTERFACE;
  359. }
  360. if (FAILED(hr))
  361. *ppvObj = NULL;
  362. ASSERT_POINTER_MATCHES_HRESULT(*ppvObj, hr);
  363. return hr;
  364. }
  365. static const LPCTSTR pszBadAppArray[] = {TEXT("aol.exe"), TEXT("waol.exe"), TEXT("msnviewr.exe"), TEXT("cs3.exe"), TEXT("msdev.exe")};
  366. /*****************************************************************************\
  367. FUNCTION: IsAppFTPCompatible
  368. DESCRIPTION:
  369. Some apps (WebOC hosts) fail to navigate to FTP directories.
  370. We check the app here and see if it's one of those incompatible apps.
  371. I don't worry about perf because we can do the work only once and cache
  372. the result because our globals will be re-inited for each process.
  373. GOOD:
  374. ========================================================================
  375. iexplore.exe: Good of course.
  376. explorer.exe: Good of course.
  377. msdev.exe (v6): The HTML help works but folder navigations happen in
  378. a new window. I don't care because the same happens in
  379. the shell (File System case).
  380. <Default Case>: These are apps built with VB's WebOC that work fine, but
  381. they also have the open in new folder behavior.
  382. BAD and UGLY:
  383. ========================================================================
  384. msdev.exe (v5): You can navigate their Moniker help to FTP which will
  385. cause a hang.
  386. [MSN] (msnviewr.exe): For some reason MSN calls IPersistFolder::Initialize with an invalid value.
  387. Navigating to the folder works but launching other folders cause them
  388. to appear in their own window and they immediately close. This was
  389. on browser only so it may be because internet delegate folders aren't
  390. supported.
  391. [aol]: (waol.exe) This doesn't work either.
  392. cs3.exe (CompuServ): ????
  393. [ATT WorldNet]: ????
  394. [Protigy]: ????
  395. [SNAP]: ????
  396. \*****************************************************************************/
  397. BOOL IsAppFTPCompatible(void)
  398. {
  399. static BOOL s_fIsAppCompatible;
  400. static BOOL s_fIsResultCached = FALSE;
  401. if (!s_fIsResultCached)
  402. {
  403. TCHAR szAppPath[MAX_PATH];
  404. s_fIsAppCompatible = TRUE; // Assume all Web OC Hosts are fine...
  405. if (EVAL(GetModuleFileName(NULL, szAppPath, ARRAYSIZE(szAppPath))))
  406. {
  407. int nIndex;
  408. LPTSTR pszAppFileName = PathFindFileName(szAppPath);
  409. // Default to TRUE because if it's not in the registry, then default to compatible.
  410. s_fIsAppCompatible = SHRegGetBoolUSValue(SZ_REGKEY_FTPFOLDER_COMPAT, pszAppFileName, FALSE, TRUE);
  411. for (nIndex = 0; nIndex < ARRAYSIZE(pszBadAppArray); nIndex++)
  412. {
  413. if (!StrCmpI(pszAppFileName, pszBadAppArray[nIndex]))
  414. {
  415. // Default to FALSE because if it's not in the registry, then it's incompatible because
  416. // it's in our list.
  417. s_fIsAppCompatible = SHRegGetBoolUSValue(SZ_REGKEY_FTPFOLDER_COMPAT, pszAppFileName, FALSE, FALSE);
  418. break;
  419. }
  420. }
  421. }
  422. s_fIsResultCached = TRUE;
  423. }
  424. return s_fIsAppCompatible;
  425. }
  426. /*****************************************************************************\
  427. FUNCTION: CreateSubViewObject
  428. DESCRIPTION:
  429. Somebody is asking for a UI object of a subobject, which is
  430. better handled by the subobject than by the parent.
  431. Bind to the subobject and get the requested UI object thence.
  432. If the pidl list is empty, then we are talking about ourselves again.
  433. \*****************************************************************************/
  434. HRESULT CFtpFolder::CreateSubViewObject(HWND hwndOwner, CFtpPidlList * pflHfpl, REFIID riid, LPVOID * ppvObj)
  435. {
  436. HRESULT hr = E_INVALIDARG;
  437. DWORD dwItemsSelected = pflHfpl->GetCount();
  438. IShellFolder * psf = NULL;
  439. if (EVAL(ppvObj)) // I wouldn't be surprised if
  440. *ppvObj = NULL; // somebody relied on this
  441. if (1 == dwItemsSelected)
  442. {
  443. LPITEMIDLIST pidl = pflHfpl->GetPidl(0); // This doesn't clone the pidl so we don't need to free it.
  444. if (pidl)
  445. hr = BindToObject(pidl, 0, IID_IShellFolder, (LPVOID *)&psf);
  446. }
  447. else if (EVAL(0 == dwItemsSelected))
  448. hr = this->QueryInterface(IID_IShellFolder, (void **) &psf);
  449. ASSERT_POINTER_MATCHES_HRESULT(psf, hr);
  450. if (SUCCEEDED(hr))
  451. {
  452. // CreateViewObject will AddRef the psfT if it wants it
  453. hr = psf->CreateViewObject(hwndOwner, riid, ppvObj);
  454. }
  455. ASSERT_POINTER_MATCHES_HRESULT(*ppvObj, hr);
  456. ATOMICRELEASE(psf);
  457. return hr;
  458. }
  459. /*****************************************************************************\
  460. GetSiteMotd
  461. \*****************************************************************************/
  462. CFtpGlob * CFtpFolder::GetSiteMotd(void)
  463. {
  464. CFtpGlob * pGlob = NULL;
  465. _InitFtpSite(); // Okay if it fails.
  466. if (m_pfs)
  467. pGlob = m_pfs->GetMotd();
  468. return pGlob;
  469. }
  470. HRESULT CFtpFolder::_Initialize(LPCITEMIDLIST pidlTarget, LPCITEMIDLIST pidlRoot, int nBytesToPrivate)
  471. {
  472. IUnknown_Set(&m_pfs, NULL);
  473. return CBaseFolder::_Initialize(pidlTarget, pidlRoot, nBytesToPrivate);
  474. }
  475. // Sometimes the user will enter incorrect information without knowing.
  476. // We would catch this if we verified everything that was entered, but
  477. // we don't, we just take it on faith until we do the IEnumIDList.
  478. // This is great for perf but is bad for catching these kinds of things.
  479. // An example of this is the user using the File.Open dialog and going to
  480. // "ftp://myserver/dir/". They then enter "ftp://myserver/dir/file.txt"
  481. // which will try to parse relative but it's an absolute path.
  482. HRESULT CFtpFolder::_FilterBadInput(LPCTSTR pszUrl, LPITEMIDLIST * ppidl)
  483. {
  484. HRESULT hr = S_OK;
  485. // If pidlPrivate isn't empty, then we aren't at the
  486. // root, so reject any urls that are absolute (i.e. have
  487. // ftp: scheme).
  488. if (!IsRoot() && (URL_SCHEME_FTP == GetUrlScheme(pszUrl)))
  489. hr = E_FAIL;
  490. // More may come here...
  491. if (FAILED(hr) && *ppidl)
  492. Pidl_Set(ppidl, NULL);
  493. return hr;
  494. }
  495. /*****************************************************************************\
  496. FUNCTION: _ForPopulateAndEnum
  497. DESCRIPTION:
  498. This function exists to detect the following case and if it's true,
  499. populate the cache (pfd) and return the pidl from that cache in ppidl.
  500. There is one last thing we need to try, we need to detect if:
  501. 1) the URL has an URL path, and
  502. 2) the last item in the path doesn't have an extension and doesn't
  503. end in a slash ('/') to indicate it's a directory.
  504. If this case is true, we then need to find out if it is a directory
  505. or file by hitting the server. This is needed because by the time
  506. we bind, it's too late to fall back to the other thing (IEnumIDList).
  507. The one thing we might need to be careful about is AutoComplete because
  508. they may call :: ParseDisplayName() for every character a user types.
  509. This won't be so bad because it's on a background thread, asynch, and
  510. the first enum within a segment will cause the cache to be populated
  511. within a that segment so subsequent enums will be fast. The problem
  512. it that it's not uncommon for users to enter between 2 and 5 segments,
  513. and there would be 1 enum per segment.
  514. \*****************************************************************************/
  515. HRESULT CFtpFolder::_ForPopulateAndEnum(CFtpDir * pfd, LPCITEMIDLIST pidlBaseDir, LPCTSTR pszUrl, LPCWIRESTR pwLastDir, LPITEMIDLIST * ppidl)
  516. {
  517. HRESULT hr = E_FAIL;
  518. *ppidl = NULL;
  519. // We only care if the URL Path isn't empty AND it doesn't end in a '/' AND
  520. // it doesn't have an extension.
  521. if (pfd && !ILIsEmpty(pfd->GetPathPidlReference()) && (!pwLastDir || (0 == *PathFindExtensionA(pwLastDir))))
  522. {
  523. IEnumIDList * penumIDList;
  524. // NULL hwnd needs to suppress all UI.
  525. hr = CFtpEidl_Create(pfd, this, NULL, (SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN), &penumIDList);
  526. if (SUCCEEDED(hr))
  527. {
  528. hr = penumIDList->Reset();
  529. ASSERT(SUCCEEDED(hr));
  530. // We are working off of the assumption that calling Reset will force it to hit the server and pull down all of the contents.
  531. LPITEMIDLIST pidlFromCache = (LPITEMIDLIST) pfd->GetPidlFromWireName(pwLastDir);
  532. if (pidlFromCache)
  533. {
  534. // It was found, this means that it exists now in the cache after we
  535. // forced it to be populated.
  536. *ppidl = ILCombine(pidlBaseDir, pidlFromCache);
  537. ILFree(pidlFromCache);
  538. }
  539. else
  540. hr = E_FAIL;
  541. penumIDList->Release();
  542. }
  543. }
  544. return hr;
  545. }
  546. HRESULT CFtpFolder::_GetCachedPidlFromDisplayName(LPCTSTR pszDisplayName, LPITEMIDLIST * ppidl)
  547. {
  548. HRESULT hr = E_FAIL;
  549. if (ppidl)
  550. {
  551. CFtpDir * pfd = GetFtpDir();
  552. if (pfd)
  553. {
  554. // We may have a pointer but the cache may still be empty, as in case NT #353324
  555. CFtpPidlList * pfl = pfd->GetHfpl();
  556. if (pfl)
  557. {
  558. // Yes, so we will continue to use the cache. Now let's get rid of that
  559. // temp pointer.
  560. pfl->Release();
  561. }
  562. else
  563. {
  564. // No we don't have it cashed, so pretend the pfd was returned NULL.
  565. pfd->Release();
  566. pfd = NULL;
  567. }
  568. }
  569. *ppidl = NULL;
  570. if (!pfd)
  571. {
  572. LPITEMIDLIST pidlBaseDir;
  573. hr = CreateFtpPidlFromUrl(pszDisplayName, GetCWireEncoding(), NULL, &pidlBaseDir, m_pm, FALSE);
  574. if (SUCCEEDED(hr)) // May fail because of AutoComplete.
  575. {
  576. // If it's not pointing to just a server, then we can enum the contents and
  577. // find out if it's is a file or directory.
  578. if (!ILIsEmpty(pidlBaseDir) && !FtpID_IsServerItemID(ILFindLastID(pidlBaseDir)))
  579. {
  580. CFtpSite * pfs;
  581. hr = SiteCache_PidlLookup(pidlBaseDir, TRUE, m_pm, &pfs);
  582. if (SUCCEEDED(hr))
  583. {
  584. LPCWIRESTR pwLastDirName;
  585. // If we are using a hidden password, then ::GetDisplayNameOf() hands out
  586. // these "ftp://user@server/dir/" URLs and the password is hidden. If
  587. // :: ParseDisplayName() is given one of these URLs and we are currently in
  588. // that server w/that user name, then :: ParseDisplayNameOf() needs to hand
  589. // out a pidl with the correct hidden password cookie.
  590. //
  591. // Is pidlNav the same as GetPublicRootPidlReference() except pidlNav doesn't
  592. // have a password. The same means that the servers match, and the user names
  593. // match.
  594. EVAL(SUCCEEDED(pfs->UpdateHiddenPassword(pidlBaseDir)));
  595. // This is sneaky because pwLastDirName will point into them itemID
  596. // that will be removed. The memory won't really be removed, it will
  597. // just have the size set to zero.
  598. pwLastDirName = FtpPidl_GetLastItemWireName(pidlBaseDir);
  599. ILRemoveLastID(pidlBaseDir);
  600. pfs->GetFtpDir(pidlBaseDir, &pfd);
  601. if (pfd)
  602. {
  603. LPITEMIDLIST pidlFromCache = (LPITEMIDLIST) pfd->GetPidlFromWireName(pwLastDirName);
  604. if (pidlFromCache)
  605. {
  606. // It was found, this means we were probably in ftp://serverX/Dir1/
  607. // and the user entered something from that directory or another directory
  608. // taht we have alread displayed to the user and it's in our cache.
  609. *ppidl = ILCombine(pidlBaseDir, pidlFromCache);
  610. ILFree(pidlFromCache);
  611. hr = S_OK;
  612. }
  613. else
  614. {
  615. // There is one last thing we need to try, we need to detect if:
  616. // 1) the URL has an URL path, and
  617. // 2) the last item in the path doesn't have an extension and doesn't
  618. // end in a slash ('/') to indicate it's a directory.
  619. // If this case is true, we then need to find out if it is a directory
  620. // or file by hitting the server. This is needed because by the time
  621. // we bind, it's too late to fall back to the other thing (IEnumIDList).
  622. // The one thing we might need to be careful about is AutoComplete because
  623. // they may call :: ParseDisplayName() for every character a user types.
  624. // This won't be so bad because it's on a background thread, asynch, and
  625. // the first enum within a segment will cause the cache to be populated
  626. // within a that segment so subsequent enums will be fast. The problem
  627. // it that it's not uncommon for users to enter between 2 and 5 segments,
  628. // and there would be 1 enum per segment.
  629. hr = _ForPopulateAndEnum(pfd, pidlBaseDir, pszDisplayName, pwLastDirName, ppidl);
  630. }
  631. pfd->Release();
  632. }
  633. else
  634. hr = E_FAIL;
  635. pfs->Release();
  636. }
  637. else
  638. hr = E_FAIL;
  639. }
  640. else
  641. hr = E_FAIL;
  642. ILFree(pidlBaseDir);
  643. }
  644. }
  645. else
  646. {
  647. // Create a new enumeration object for the caller.
  648. // PERF: log 2 (sizeof(m_pflHfpl))
  649. *ppidl = (LPITEMIDLIST) pfd->GetPidlFromDisplayName(pszDisplayName);
  650. if (*ppidl)
  651. {
  652. hr = S_OK;
  653. }
  654. else
  655. {
  656. // If we got here, the cache for this directory is populated.
  657. // So if the name doesn't match, then either:
  658. // 1) it doesn't exist,
  659. // 2) the cache is out of date, or
  660. // 3) it's multilevel, (like "dir1\dir2\dir3") or
  661. // 4) It's a weird parsing token that our parent parse should have remoted, like "..", ".", "\", etc.
  662. // We will assome our parent parse takes care of #4, and #2 isn't true.
  663. // Is this multilevel? (Case #3)
  664. if (!StrChr(pszDisplayName, TEXT('/')))
  665. {
  666. // No, so reject it and don't let our caller blindly accept it.
  667. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  668. }
  669. }
  670. pfd->Release();
  671. }
  672. }
  673. return hr;
  674. }
  675. HRESULT CFtpFolder::_GetBindCtx(IBindCtx ** ppbc)
  676. {
  677. HRESULT hr = CreateBindCtx(NULL, ppbc);
  678. if (SUCCEEDED(hr)) // Can fail with out of memory
  679. {
  680. hr = (*ppbc)->RegisterObjectParam(STR_SKIP_BINDING_CLSID, SAFECAST(this, IShellIcon *)); // We want IUnknown, not IShellIcon, but this is to disambigiuate.
  681. if (FAILED(hr))
  682. {
  683. ATOMICRELEASE(*ppbc);
  684. }
  685. }
  686. return hr;
  687. }
  688. HRESULT CFtpFolder::_GetLegacyURL(LPCITEMIDLIST pidl, IBindCtx * pbc, LPTSTR pszUrl, DWORD cchSize)
  689. {
  690. HRESULT hr = S_OK;
  691. LPITEMIDLIST pidlWithVRoot;
  692. // We now need to insert the virtual root path into the path section
  693. // of the URL because the old FTP support doesn't follow the correct
  694. // FTP URL spec that says that the virtual root needs to be left out
  695. // of the URL.
  696. hr = _ConvertPidlForRootedFix(pidl, &pidlWithVRoot);
  697. if (SUCCEEDED(hr))
  698. {
  699. WCHAR wzFrag[MAX_PATH];
  700. // SECURITY ISSUE: We need to get the URL w/password or it won't work, but
  701. // this will expose the password publicly. We need a way for
  702. // the real FTP URL Pidl to hide the password.
  703. hr = UrlCreateFromPidlW(pidlWithVRoot, SHGDN_FORPARSING, pszUrl, cchSize, (ICU_ESCAPE | ICU_USERNAME), FALSE);
  704. if (ILGetHiddenStringW(pidl, IDLHID_URLFRAGMENT, wzFrag, ARRAYSIZE(wzFrag))) // Add fragment if it exists.
  705. UrlCombineW(pszUrl, wzFrag, pszUrl, &cchSize, 0);
  706. ILFree(pidlWithVRoot);
  707. }
  708. return hr;
  709. }
  710. HRESULT CFtpFolder::_GetLegacyPidl(LPCITEMIDLIST pidl, LPITEMIDLIST * ppidlLegacy)
  711. {
  712. IBindCtx * pbc = NULL;
  713. HRESULT hr = _GetBindCtx(&pbc);
  714. *ppidlLegacy = NULL;
  715. if (SUCCEEDED(hr)) // Can fail with out of memory.
  716. {
  717. WCHAR wzUrl[MAX_URL_STRING];
  718. hr = _GetLegacyURL(pidl, pbc, wzUrl, ARRAYSIZE(wzUrl));
  719. if (EVAL(SUCCEEDED(hr)))
  720. {
  721. TraceMsg(TF_FTPISF, "_BindToObject_OriginalFtpSupport() navigating to=%ls", wzUrl);
  722. hr = IEParseDisplayNameWithBCW(CP_ACP, wzUrl, pbc, ppidlLegacy);
  723. }
  724. pbc->Release();
  725. }
  726. return hr;
  727. }
  728. HRESULT CFtpFolder::_InitLegacyShellFolder(IShellFolder * psfLegacy, LPCITEMIDLIST pidlInit)
  729. {
  730. IPersistFolder * ppf;
  731. HRESULT hr = psfLegacy->QueryInterface(IID_IPersistFolder, (void **) &ppf);
  732. if (SUCCEEDED(hr))
  733. {
  734. hr = ppf->Initialize(pidlInit);
  735. ppf->Release();
  736. }
  737. return hr;
  738. }
  739. HRESULT CFtpFolder::_INetBindToObject(LPCITEMIDLIST pidl, IBindCtx * pbc, REFIID riid, LPVOID * ppvObj)
  740. {
  741. HRESULT hr = E_OUTOFMEMORY;
  742. LPITEMIDLIST pidlFirst = GetPublicPidlRootIDClone();
  743. if (pidlFirst)
  744. {
  745. IShellFolder * psfInternetSF;
  746. hr = IEBindToObject(pidlFirst, &psfInternetSF);
  747. if (SUCCEEDED(hr))
  748. {
  749. hr = _InitLegacyShellFolder(psfInternetSF, pidlFirst);
  750. if (SUCCEEDED(hr))
  751. {
  752. // Note the I use ILNext() in order to skip past the Desktop ItemID,
  753. // which is internal knowledge I should not have.
  754. hr = psfInternetSF->BindToObject(_ILNext(pidl), pbc, riid, ppvObj);
  755. }
  756. psfInternetSF->Release();
  757. }
  758. ILFree(pidlFirst);
  759. }
  760. return hr;
  761. }
  762. HRESULT CFtpFolder::_BindToObject_OriginalFtpSupport(LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvObj)
  763. {
  764. LPBC pbc = NULL;
  765. HRESULT hr = CreateBindCtx(NULL, &pbc);
  766. if (SUCCEEDED(hr))
  767. {
  768. hr = pbc->RegisterObjectParam(STR_SKIP_BINDING_CLSID, SAFECAST(this, IShellIcon *)); // We want IUnknown, not IShellIcon, but this is to disambigiuate.
  769. if (SUCCEEDED(hr))
  770. {
  771. LPITEMIDLIST pidlLegacy;
  772. hr = _GetLegacyPidl(pidl, &pidlLegacy);
  773. if (SUCCEEDED(hr))
  774. {
  775. hr = _INetBindToObject(pidlLegacy, pbc, riid, ppvObj);
  776. ILFree(pidlLegacy);
  777. }
  778. }
  779. pbc->Release();
  780. }
  781. ASSERT_POINTER_MATCHES_HRESULT(*ppvObj, hr);
  782. return hr;
  783. }
  784. /*****************************************************************************\
  785. FUNCTION: _IsValidPidlParameter
  786. DESCRIPTION:
  787. If this IShellFolder is rooted within our name space, then the pidl needs
  788. to be a valid relative pidl. If we are rooted at the base of our name space,
  789. then it needs to be a full pidl.
  790. \*****************************************************************************/
  791. BOOL CFtpFolder::_IsValidPidlParameter(LPCITEMIDLIST pidl)
  792. {
  793. BOOL fResult = TRUE;
  794. if (IsRoot())
  795. fResult = FtpPidl_IsValidFull(pidl);
  796. else
  797. fResult = FtpPidl_IsValidRelative(pidl);
  798. return fResult;
  799. }
  800. /*****************************************************************************\
  801. FUNCTION: IShellFolder::_BindToObject
  802. DESCRIPTION:
  803. We are now sure that we want to handle the support, so check what they
  804. want.
  805. \*****************************************************************************/
  806. HRESULT CFtpFolder::_BindToObject(LPCITEMIDLIST pidl, LPCITEMIDLIST pidlFull, IBindCtx * pbc, REFIID riid, LPVOID * ppvObj)
  807. {
  808. HRESULT hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Indicate we want the old functionality to kick in.
  809. if (IsEqualIID(riid, IID_IShellFolder) ||
  810. IsEqualIID(riid, IID_IShellFolder2) ||
  811. IsEqualIID(riid, IID_IBrowserFrameOptions))
  812. {
  813. LPITEMIDLIST pidlTarget = ILCombine(GetPublicTargetPidlReference(), pidl);
  814. LPITEMIDLIST pidlRoot = (GetFolderPidl() ? ILCombine(GetFolderPidl(), pidl) : NULL);
  815. // There's no point trying to verify that it's folders all
  816. // the way down, because it's the caller's job not to combine
  817. // pidls randomly. Furthermore, they might not actually be marked
  818. // as folders if we got them via ParseDisplayName.
  819. // NOTE: Binding will succeed even if the pidl isn't valid on the
  820. // server. In the future we may want to verify now so we
  821. // don't hand out a IEnumIDList that won't work. Currently,
  822. // IEnumIDList will fail and cause a renavigation if it can
  823. // connect to the server in a different way (different username
  824. // password pair). It would be better to do a redirect because
  825. // the renavigation causes the bad entry in the navigation stack.
  826. // We can't verify the item exists on the server if we have a WebProxy
  827. // installed.
  828. hr = CFtpFolder_Create(pidlTarget, pidlRoot, GetPidlByteOffset(), riid, ppvObj);
  829. //TraceMsg(TF_FOLDER_SHRTCUTS, "CFtpFolder::_BindToObject() creating an FTP IShellFolder psf=%#08lx, pidlTarget=%#08lx, pidlRoot=%#08lx", *ppvObj, pidlTarget, pidlRoot);
  830. if (SUCCEEDED(hr))
  831. {
  832. IUnknown * punk = (IUnknown *) *ppvObj;
  833. IDelegateFolder * pdf;
  834. hr = punk->QueryInterface(IID_IDelegateFolder, (LPVOID *) &pdf);
  835. if (EVAL(SUCCEEDED(hr)))
  836. {
  837. hr = pdf->SetItemAlloc(m_pm);
  838. pdf->Release();
  839. }
  840. }
  841. ILFree(pidlTarget);
  842. ILFree(pidlRoot);
  843. //TraceMsg(TF_FTPISF, "CFtpFolder::BindToObject() IID_IShellFolder hr=%#08lx", hr);
  844. }
  845. else if (IsEqualIID(riid, IID_IMoniker))
  846. {
  847. hr = _PidlToMoniker(pidlFull, (IMoniker **) ppvObj);
  848. }
  849. else if (IsEqualIID(riid, IID_IStream))
  850. {
  851. hr = E_OUTOFMEMORY;
  852. CFtpDir * pfd = GetFtpDir();
  853. if (pfd)
  854. {
  855. DWORD dwAccess = (BindCtx_GetMode(pbc, STGM_READ) & STGM_WRITE) ? GENERIC_WRITE : GENERIC_READ;
  856. ULARGE_INTEGER uliTemp = {0};
  857. hr = CFtpStm_Create(pfd, pidlFull, dwAccess, (IStream **)ppvObj, uliTemp, uliTemp, NULL, FALSE);
  858. pfd->Release();
  859. }
  860. }
  861. else if (IsEqualIID(riid, IID_CFtpFolder))
  862. {
  863. IShellFolder * psf;
  864. // Nothing like a little recursion to keep the code clean.
  865. // The fact that we use IID_IShellFolder guarantees the breaking
  866. // of the recursion.
  867. hr = BindToObject(pidl, pbc, IID_IShellFolder, (void **) &psf);
  868. if (SUCCEEDED(hr))
  869. {
  870. hr = psf->QueryInterface(riid, ppvObj);
  871. psf->Release();
  872. }
  873. }
  874. else
  875. {
  876. TraceMsg(TF_FTPISF, "CFtpFolder::BindToObject() unsupported interface hr=E_NOINTERFACE");
  877. *ppvObj = NULL;
  878. hr = E_NOINTERFACE;
  879. }
  880. return hr;
  881. }
  882. /*****************************************************************************\
  883. FUNCTION: _ConvertPidlForRootedFix
  884. DESCRIPTION:
  885. If an FTP URL has a login name, that login may root the user under a directory other than "/".
  886. The FTP URL spec (RFC 1738) says that URL paths need to be relative to the rooted directory. For example:
  887. If UserA's rooted account is in \usr\GroupA\UserA and the url is:
  888. ftp://UserA:FooBar@server/test/file.txt, then the real path is \usr\GroupA\UserA\test\file.txt.
  889. The problem is that the old FTP code doesn't respect this and requires:
  890. ftp://UserA:FooBar@server/usr/GroupA/UserA/test/file.txt, so we fix that here.
  891. PARAMETERS:
  892. pidlBefore [IN]: This will be a public pidl to the item to navigate to.
  893. This means it will be: [TheINet][FtpServerID][...]
  894. *ppidlWithVRoot [OUT]: This will be the same public pidl that was passed in except
  895. any ItemIDs that come from pfs->GetVirtualRootReference() will be inserted
  896. between the ServerID and ItemIDs.
  897. \*****************************************************************************/
  898. HRESULT CFtpFolder::_ConvertPidlForRootedFix(LPCITEMIDLIST pidlBefore, LPITEMIDLIST * ppidlWithVRoot)
  899. {
  900. CFtpSite * pfs;
  901. HRESULT hr = SiteCache_PidlLookup(pidlBefore, FALSE, m_pm, &pfs);
  902. *ppidlWithVRoot = NULL;
  903. if (SUCCEEDED(hr) && pfs)
  904. {
  905. if (pfs->HasVirtualRoot())
  906. {
  907. LPCITEMIDLIST pidlVirtualRoot = pfs->GetVirtualRootReference();
  908. LPITEMIDLIST pidlUrlPath = (LPITEMIDLIST)pidlBefore;
  909. // Skip past non-FTP Server/ItemIDs. (TheInternet)
  910. while (pidlUrlPath && !ILIsEmpty(pidlUrlPath) && !FtpID_IsServerItemID(pidlUrlPath))
  911. pidlUrlPath = _ILNext(pidlUrlPath);
  912. if (FtpID_IsServerItemID(pidlUrlPath))
  913. pidlUrlPath = _ILNext(pidlUrlPath);
  914. if (EVAL(pidlUrlPath))
  915. {
  916. LPITEMIDLIST pidlFullWithVRoot;
  917. USHORT cb = pidlUrlPath->mkid.cb;
  918. pidlUrlPath->mkid.cb = 0;
  919. pidlFullWithVRoot = ILCombine(pidlBefore, pidlVirtualRoot);
  920. pidlUrlPath->mkid.cb = cb;
  921. if (pidlFullWithVRoot)
  922. {
  923. FtpPidl_InsertVirtualRoot(pidlFullWithVRoot, pidlUrlPath, ppidlWithVRoot);
  924. ILFree(pidlFullWithVRoot);
  925. }
  926. }
  927. else
  928. hr = E_OUTOFMEMORY;
  929. }
  930. else
  931. hr = E_FAIL;
  932. pfs->Release();
  933. }
  934. if (FAILED(hr))
  935. {
  936. *ppidlWithVRoot = ILClone(pidlBefore);
  937. if (*ppidlWithVRoot)
  938. hr = S_OK;
  939. }
  940. return hr;
  941. }
  942. BOOL CFtpFolder::IsUTF8Supported(void)
  943. {
  944. if (EVAL(m_pfs))
  945. return m_pfs->IsUTF8Supported();
  946. return FALSE;
  947. }
  948. /*****************************************************************************\
  949. FUNCTION: IShellFolder::_PidlToMoniker
  950. DESCRIPTION:
  951. \*****************************************************************************/
  952. HRESULT CFtpFolder::_PidlToMoniker(LPCITEMIDLIST pidl, IMoniker ** ppmk)
  953. {
  954. HRESULT hr = E_INVALIDARG;
  955. *ppmk = NULL;
  956. if (EVAL(pidl))
  957. {
  958. IBindCtx * pbc;
  959. hr = _GetBindCtx(&pbc);
  960. if (SUCCEEDED(hr))
  961. {
  962. WCHAR wzUrl[MAX_URL_STRING];
  963. // URLMON expects incorrectly formatted URLs (where the virtual
  964. // root is included in the url path). We need to fix that
  965. // here.
  966. hr = _GetLegacyURL(pidl, pbc, wzUrl, ARRAYSIZE(wzUrl));
  967. if (SUCCEEDED(hr))
  968. {
  969. hr = CreateURLMoniker(NULL, wzUrl, ppmk);
  970. }
  971. pbc->Release();
  972. }
  973. }
  974. ASSERT_POINTER_MATCHES_HRESULT(*ppmk, hr);
  975. return hr;
  976. }
  977. HRESULT CFtpFolder::_CreateShellView(HWND hwndOwner, void ** ppvObj)
  978. {
  979. IShellFolderViewCB * psfvCallBack;
  980. HRESULT hr = CFtpView_Create(this, hwndOwner, IID_IShellFolderViewCB, (LPVOID *) &psfvCallBack);
  981. if (SUCCEEDED(hr))
  982. {
  983. // GetPublicTargetPidlReference() is used because it's passed to SFVM_GETNOTIFY
  984. // to synch ChangeNotify messages.
  985. hr = CBaseFolder::_CreateShellView(hwndOwner, ppvObj, FTP_SHCNE_EVENTS,
  986. FVM_DETAILS, psfvCallBack, GetPublicTargetPidlReference(), CBaseFolderViewCB::_IShellFolderViewCallBack);
  987. psfvCallBack->Release();
  988. }
  989. ASSERT_POINTER_MATCHES_HRESULT(*ppvObj, hr);
  990. return hr;
  991. }
  992. HKEY ClassKeyFromExtension(LPCWIRESTR pszExt)
  993. {
  994. HKEY hkey = NULL;
  995. WIRECHAR szProgID[MAX_PATH];
  996. DWORD cbProgID = sizeof(szProgID);
  997. if (ERROR_SUCCESS == SHGetValueA(HKEY_CLASSES_ROOT, pszExt, NULL, NULL, (void *)szProgID, &cbProgID))
  998. {
  999. // the entension points to a ProgID, use that.
  1000. RegOpenKeyA(HKEY_CLASSES_ROOT, szProgID, &hkey);
  1001. }
  1002. else
  1003. {
  1004. // No ProgID, use the extension as the program ID.
  1005. RegOpenKeyA(HKEY_CLASSES_ROOT, pszExt, &hkey);
  1006. }
  1007. return hkey;
  1008. }
  1009. #define SZ_REGVALUE_DOCOBJECT TEXT("DocObject")
  1010. #define SZ_REGVALUE_BROWSEINPLACE TEXT("BrowseInPlace")
  1011. BOOL _IsDocObjViewerInstalled(LPCITEMIDLIST pidl)
  1012. {
  1013. BOOL fResult = FALSE;
  1014. // Return FALSE if it's just pointing to an FTP server.
  1015. if (!FtpID_IsServerItemID(ILFindLastID(pidl)))
  1016. {
  1017. LPCWIRESTR pwWireFileName = FtpPidl_GetLastItemWireName(pidl);
  1018. LPCWIRESTR pszExt = PathFindExtensionA(pwWireFileName);
  1019. if (pszExt)
  1020. {
  1021. HKEY hkey = ClassKeyFromExtension(pszExt);
  1022. if (hkey)
  1023. {
  1024. if ((ERROR_SUCCESS == RegQueryValue(hkey, SZ_REGVALUE_DOCOBJECT, 0, NULL)) ||
  1025. (ERROR_SUCCESS == RegQueryValue(hkey, SZ_REGVALUE_BROWSEINPLACE, 0, NULL)))
  1026. {
  1027. fResult = TRUE;
  1028. }
  1029. RegCloseKey(hkey);
  1030. }
  1031. }
  1032. }
  1033. return fResult;
  1034. }
  1035. ULONG FtpGetAttributesOf(LPCITEMIDLIST pidl)
  1036. {
  1037. ASSERT(IsValidPIDL(pidl));
  1038. DWORD dwAttributes = FtpPidl_GetAttributes(pidl); // Get File based attributes.
  1039. ULONG rgfInOut = Misc_SfgaoFromFileAttributes(dwAttributes); // Turn them into IShellFolder attributes.
  1040. return rgfInOut;
  1041. }
  1042. //===========================
  1043. // *** IShellFolder2 Interface ***
  1044. //===========================
  1045. STDAPI InitVariantFromBuffer(VARIANT *pvar, const void *pv, UINT cb)
  1046. {
  1047. HRESULT hres;
  1048. SAFEARRAY *psa = SafeArrayCreateVector(VT_UI1, 0, cb); // create a one-dimensional safe array
  1049. if (psa)
  1050. {
  1051. memcpy(psa->pvData, pv, cb);
  1052. memset(pvar, 0, sizeof(*pvar)); // VariantInit()
  1053. pvar->vt = VT_ARRAY | VT_UI1;
  1054. pvar->parray = psa;
  1055. hres = S_OK;
  1056. }
  1057. else
  1058. hres = E_OUTOFMEMORY;
  1059. return hres;
  1060. }
  1061. /*****************************************************************************\
  1062. FUNCTION: IShellFolder2::GetDetailsEx
  1063. DESCRIPTION:
  1064. This function will be called when the caller wants detailed info about
  1065. and item. SHGetDataFromIDList() is one such caller and that is commonly
  1066. called by the Shell Object model when using CSDFldrItem::get_Size(LONG *pul)
  1067. and other such APIs.
  1068. \*****************************************************************************/
  1069. HRESULT CFtpFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
  1070. {
  1071. HRESULT hr = E_OUTOFMEMORY;
  1072. if (IsEqualGUID(pscid->fmtid, FMTID_ShellDetails) && (PID_FINDDATA == pscid->pid))
  1073. {
  1074. WIN32_FIND_DATAW wfd;
  1075. // I can handle this.
  1076. LPITEMIDLIST pidlFull = CreateFullPrivatePidl(pidl);
  1077. if (pidlFull)
  1078. {
  1079. hr = Win32FindDataFromPidl(pidlFull, &wfd, TRUE, TRUE);
  1080. if (SUCCEEDED(hr))
  1081. {
  1082. hr = InitVariantFromBuffer(pv, (PVOID)&wfd, sizeof(wfd));
  1083. }
  1084. ILFree(pidlFull);
  1085. }
  1086. }
  1087. else
  1088. {
  1089. hr = CBaseFolder::GetDetailsEx(pidl, pscid, pv);
  1090. }
  1091. return hr;
  1092. }
  1093. //===========================
  1094. // *** IShellFolder Interface ***
  1095. //===========================
  1096. /*****************************************************************************\
  1097. FUNCTION: IShellFolder:: ParseDisplayName
  1098. DESCRIPTION:
  1099. The incoming name is %-encoded, but if we see an illegal %-sequence,
  1100. just leave the % alone.
  1101. For now, we disallow backslash, "*" and "?" from filenames.
  1102. Backslashes don't sit well with wininet, and wildcards
  1103. mess up the "quick FindFirst to see if the file exists".
  1104. We also disallow encoded slashes, because they mess up the way
  1105. we manage subpidls.
  1106. Annoying feature: You can't pass -1 as the output buffer size.
  1107. NLS returns ERROR_INVALID_PARAMETER if you try. So you have to pass
  1108. the actual size. Sigh.
  1109. \*****************************************************************************/
  1110. HRESULT CFtpFolder::ParseDisplayName(HWND hwnd, LPBC pbcReserved, LPOLESTR pwszDisplayName,
  1111. ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes)
  1112. {
  1113. HRESULT hr = S_OK;
  1114. *ppidl = NULL;
  1115. if (pchEaten)
  1116. *pchEaten = 0;
  1117. // PERF: log 2 (sizeof(m_pflHfpl))
  1118. hr = _GetCachedPidlFromDisplayName(pwszDisplayName, ppidl);
  1119. if (FAILED(hr) && (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) != hr))
  1120. {
  1121. // Are we are rooted within an FTP Server?
  1122. if (IsRoot())
  1123. {
  1124. // No, so parse the entire thing
  1125. // There is only one case where we want to hide the password,
  1126. // and that is when the user entered it into the "Login"
  1127. // dialog. Since we entering it into the dialog will cause a
  1128. // redirect to an URL with that password in it, we need to determie
  1129. // if we are being called during this redirect. If so,
  1130. // the password just came from the Login dialog and we need to hide it.
  1131. // This will work for fully qualified Ftp URLs
  1132. hr = CreateFtpPidlFromUrl(pwszDisplayName, GetCWireEncoding(), pchEaten, ppidl, m_pm, FALSE);
  1133. if (SUCCEEDED(hr))
  1134. {
  1135. CFtpSite * pfs;
  1136. hr = SiteCache_PidlLookup(*ppidl, TRUE, m_pm, &pfs);
  1137. if (SUCCEEDED(hr))
  1138. {
  1139. // If we are using a hidden password, then ::GetDisplayNameOf() hands out
  1140. // these "ftp://user@server/dir/" URLs and the password is hidden. If
  1141. // ::ParseDisplayName() is given one of these URLs and we are currently in
  1142. // that server w/that user name, then ::ParseDisplayNameOf() needs to hand
  1143. // out a pidl with the correct hidden password cookie.
  1144. //
  1145. // Is pidlNav the same as GetPublicPidlReference() except pidlNav doesn't
  1146. // have a password. The same means that the servers match, and the user names
  1147. // match.
  1148. EVAL(SUCCEEDED(pfs->UpdateHiddenPassword(*ppidl)));
  1149. pfs->Release();
  1150. }
  1151. }
  1152. }
  1153. else
  1154. {
  1155. // Yes, so do a relative parse
  1156. // Sometimes the user will enter incorrect information without knowing.
  1157. // We would catch this if we verified everything that was entered, but
  1158. // we don't, we just take it on faith until we do the IEnumIDList.
  1159. // This is great for perf but is bad for catching these kinds of things.
  1160. // An example of this is the user using the File.Open dialog and going to
  1161. // "ftp://myserver/dir/". They then enter "ftp://myserver/dir/file.txt"
  1162. // which will try to parse relative but it's an absolute path.
  1163. hr = _FilterBadInput(pwszDisplayName, ppidl);
  1164. if (SUCCEEDED(hr))
  1165. {
  1166. CFtpDir * pfd = GetFtpDir();
  1167. if (pfd)
  1168. hr = CreateFtpPidlFromDisplayPath(pwszDisplayName, pfd->GetFtpSite()->GetCWireEncoding(), pchEaten, ppidl, FALSE, FALSE);
  1169. else
  1170. hr = E_OUTOFMEMORY;
  1171. pfd->Release();
  1172. }
  1173. }
  1174. }
  1175. if (SUCCEEDED(hr) && pdwAttributes)
  1176. {
  1177. hr = GetAttributesOf(1, (LPCITEMIDLIST *) ppidl, pdwAttributes);
  1178. if (FAILED(hr))
  1179. ILFree(*ppidl);
  1180. }
  1181. #ifdef DEBUG
  1182. TCHAR szUrlDebug[MAX_URL_STRING];
  1183. szUrlDebug[0] = 0;
  1184. if (*ppidl)
  1185. UrlCreateFromPidl(*ppidl, SHGDN_FORPARSING, szUrlDebug, ARRAYSIZE(szUrlDebug), ICU_USERNAME, FALSE);
  1186. TraceMsg(TF_FTPISF, "CFtpFolder::ParseDisplayName(%ls) CreateFtpPidlFromUrl() returned hres=%#08lx %ls", pwszDisplayName, hr, szUrlDebug);
  1187. ASSERT(FAILED(hr) || IsValidPIDL(*ppidl));
  1188. #endif // DEBUG
  1189. ASSERT_POINTER_MATCHES_HRESULT(*ppidl, hr);
  1190. return hr;
  1191. }
  1192. IMalloc * CFtpFolder::GetIMalloc(void)
  1193. {
  1194. IMalloc * pm = NULL;
  1195. IUnknown_Set(&pm, m_pm);
  1196. ASSERT(pm);
  1197. return pm;
  1198. }
  1199. /*****************************************************************************\
  1200. FUNCTION: IShellFolder::EnumObjects
  1201. DESCRIPTION:
  1202. Design subtlety: If we couldn't create an enumeration on the server,
  1203. succeed, but return an enumerator that shows no objects.
  1204. This is necessary so that our IShellView callback can put
  1205. up error UI. If we failed the create, the shell would
  1206. destroy the view without giving us a chance to say what's
  1207. up.
  1208. It's also important for write-only directories like /incoming,
  1209. so that the user can drag files into the directory without
  1210. necessarily being able to drag files out.
  1211. \*****************************************************************************/
  1212. HRESULT CFtpFolder::EnumObjects(HWND hwndOwner, DWORD grfFlags, IEnumIDList ** ppenumIDList)
  1213. {
  1214. HRESULT hres = E_FAIL;
  1215. CFtpDir * pfd = GetFtpDir();
  1216. // This will happen if some TARD tries to just CoCreateInstance our
  1217. // Name Space extension and see what contents we have. TweakUI
  1218. // is an example of one such abuser. Since we can only populate
  1219. // our contents after we navigate to a FTP server, we are empty.
  1220. *ppenumIDList = NULL;
  1221. if (pfd)
  1222. {
  1223. // Create a new enumeration object for the caller.
  1224. ASSERT(m_pm);
  1225. hres = CFtpEidl_Create(pfd, this, hwndOwner, grfFlags, ppenumIDList);
  1226. TraceMsg(TF_FTPISF, "CFtpFolder::EnumObjects() CFtpEidl_Create() returned hres=%#08lx", hres);
  1227. pfd->Release();
  1228. }
  1229. ASSERT_POINTER_MATCHES_HRESULT(*ppenumIDList, hres);
  1230. return hres;
  1231. }
  1232. BOOL CFtpFolder::_NeedToFallBackRelative(LPCITEMIDLIST pidl, BOOL * pfDisplayProxyFallBackDlg)
  1233. {
  1234. LPITEMIDLIST pidlFull = CreateFullPrivatePidl(pidl);
  1235. BOOL fFallBack = FALSE;
  1236. if (pidlFull)
  1237. {
  1238. fFallBack = _NeedToFallBack(pidl, pfDisplayProxyFallBackDlg);
  1239. ILFree(pidlFull);
  1240. }
  1241. return fFallBack;
  1242. }
  1243. /*****************************************************************************\
  1244. FUNCTION: _NeedToFallBack
  1245. DESCRIPTION:
  1246. We need to fall back to the old URLMON support in these cases:
  1247. #1 It's a file, we let the old code use URLMON to do the download.
  1248. #2 The app (WebOC host) has bugs that cause us to fail.
  1249. #3 The user turned off the New FTP UI. (For whatever reason)
  1250. #4 The proxy is a web proxy and allows URLMON but not WININET access,
  1251. so fall back to the old support.
  1252. #5 WININET doesn't support VMS servers, so we need to fall back in that case.
  1253. NOTE: The order is important because we always need to calc
  1254. fIsProxyBlockingFTP so we only display the fallback dlg
  1255. in the correct case.
  1256. \*****************************************************************************/
  1257. BOOL CFtpFolder::_NeedToFallBack(LPCITEMIDLIST pidlFull, BOOL * pfDisplayProxyFallBackDlg)
  1258. {
  1259. BOOL fNeedToFallBack = TRUE;
  1260. *pfDisplayProxyFallBackDlg = FALSE;
  1261. // TweakUI sends us an Empty pidls so don't fault. NT #396234.
  1262. if (pidlFull && !ILIsEmpty(pidlFull))
  1263. {
  1264. BOOL fIsDirectory;
  1265. if (IsFtpPidlQuestionable(pidlFull))
  1266. _FixQuestionablePidl(pidlFull);
  1267. fIsDirectory = (!FtpPidl_HasPath(pidlFull) || FtpPidl_IsDirectory(pidlFull, FALSE));
  1268. if (fIsDirectory) // #1
  1269. {
  1270. if (IsAppFTPCompatible()) // #2
  1271. {
  1272. if (!SHRegGetBoolUSValue(SZ_REGKEY_FTPFOLDER, SZ_REGKEY_USE_OLD_UI, FALSE, FALSE)) // #3
  1273. {
  1274. // The binding code passes us a bind context and that would be a good
  1275. // key to determine if were are about to navigate to the site. The
  1276. // problem is that we can't skip the proxy checking because we will
  1277. // fail later.
  1278. //
  1279. // #224285 is an example where navigating from ftp://ftp.microsoft.com/ to
  1280. // "www.microsoft.com" will cause CShellUrl to call :: BindToObject and then
  1281. // our IEnumIDList::Next() which will give an error message.
  1282. //
  1283. // Are we unable to get access to the server because there is
  1284. // a CERN type proxy blocking us?
  1285. // PERF: Only check for the proxy if we have a bind context because
  1286. // the only place we are called from to navigate is
  1287. // CDocObjectFolder:: BindToObject() and we are guaranteed that they
  1288. // pass it to us.
  1289. if (!_IsProxyBlockingSite(pidlFull)) // #4
  1290. {
  1291. // Is this a VMS Server? If yes, fallback
  1292. // to URLMON support because wininet doesn't work with this kind of server.
  1293. if (!_IsServerVMS(pidlFull))
  1294. {
  1295. // Only continue if the user didn't turn the new UI Off.
  1296. fNeedToFallBack = FALSE;
  1297. }
  1298. }
  1299. else
  1300. *pfDisplayProxyFallBackDlg = TRUE;
  1301. }
  1302. }
  1303. }
  1304. }
  1305. return fNeedToFallBack;
  1306. }
  1307. /*****************************************************************************\
  1308. FUNCTION: IShellFolder:: BindToObject
  1309. DESCRIPTION:
  1310. First thing we need to do, is see if we want to over ride the default
  1311. IE FTP support. If we do, we call otherwise, we just fallback to the old
  1312. support. We want the new UI if: a) it's a directory, b) the web proxy doesn't
  1313. block us, and c) the user didn't turn us off.
  1314. PERF/TODO:
  1315. OrderItem_GetSystemImageListIndexFromCache (\shell\lib\dpastuff.cpp)
  1316. uses riid=IShellFolder when trying to find out the icon. We don't want
  1317. to hit the net in that case, so force them to pass a pbc to indicate skipping
  1318. the net in that case.
  1319. \*****************************************************************************/
  1320. HRESULT CFtpFolder::BindToObject(LPCITEMIDLIST pidl, IBindCtx * pbc, REFIID riid, LPVOID * ppvObj)
  1321. {
  1322. HRESULT hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Indicate we want the old functionality to kick in.
  1323. if (ppvObj)
  1324. *ppvObj = NULL;
  1325. if (!pidl || ILIsEmpty(pidl) || !_IsValidPidlParameter(pidl))
  1326. {
  1327. // Caller, are you smoking crack? What's the idea of passing
  1328. // an empty pidl. (Comdlg32 is known to do this)
  1329. hr = E_INVALIDARG;
  1330. }
  1331. else
  1332. {
  1333. BOOL fDisplayProxyFallBackDlg = FALSE;
  1334. LPITEMIDLIST pidlFull = CreateFullPrivatePidl(pidl);
  1335. if (pidlFull)
  1336. {
  1337. // We need to handle it.
  1338. hr = _BindToObject(pidl, pidlFull, pbc, riid, ppvObj);
  1339. // Maybe we still need to handle it if
  1340. ASSERT(HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr);
  1341. ILFree(pidlFull);
  1342. }
  1343. }
  1344. ASSERT_POINTER_MATCHES_HRESULT(*ppvObj, hr);
  1345. return hr;
  1346. }
  1347. /*****************************************************************************\
  1348. FUNCTION: IShellFolder::BindToStorage
  1349. DESCRIPTION:
  1350. We need to implement this so the user can Open and Save files in
  1351. the standard Open Dialog and Save Dialog.
  1352. \*****************************************************************************/
  1353. HRESULT CFtpFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, LPVOID * ppvObj)
  1354. {
  1355. HRESULT hr = E_INVALIDARG;
  1356. if (!EVAL(_IsValidPidlParameter(pidl)))
  1357. return E_INVALIDARG;
  1358. *ppvObj = 0;
  1359. if (EVAL(pidl))
  1360. {
  1361. IMoniker * pmk;
  1362. hr = _PidlToMoniker(pidl, &pmk);
  1363. if (SUCCEEDED(hr))
  1364. {
  1365. hr = pmk->BindToStorage(pbc, NULL, riid, ppvObj);
  1366. pmk->Release();
  1367. }
  1368. }
  1369. ASSERT_POINTER_MATCHES_HRESULT(*ppvObj, hr);
  1370. TraceMsg(TF_FTPISF, "CFtpFolder::BindToStorage() hr=%#08lx", hr);
  1371. return hr;
  1372. }
  1373. /*****************************************************************************\
  1374. FUNCTION: IShellFolder::CompareIDs
  1375. DESCRIPTION:
  1376. ici - column on which to sort. Note! that we rely on the fact that
  1377. IShellFolders are uniform; we do not need to bind to the shell folder in
  1378. order to compare its sub-itemids.
  1379. _UNDOCUMENTED_: The documentation does not say whether or not
  1380. complex pidls can be received. In fact, they can.
  1381. \*****************************************************************************/
  1382. HRESULT CFtpFolder::CompareIDs(LPARAM ici, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  1383. {
  1384. ASSERT(IsValidPIDL(pidl1));
  1385. ASSERT(IsValidPIDL(pidl2));
  1386. return FtpItemID_CompareIDs(ici, pidl1, pidl2, FCMP_GROUPDIRS);
  1387. }
  1388. HRESULT CFtpFolder::_CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID * ppvObj)
  1389. {
  1390. HRESULT hr = E_FAIL;
  1391. CFtpDir * pfd = GetFtpDir();
  1392. *ppvObj = NULL; // Explorer relies on this
  1393. //TraceMsg(TF_FTPISF, "CFtpObj::CreateViewObject() ");
  1394. if (pfd)
  1395. {
  1396. if (IsEqualIID(riid, IID_IDropTarget))
  1397. {
  1398. // Don't create a drop target for the root FTP folder.
  1399. if (IsRoot())
  1400. hr = E_NOINTERFACE;
  1401. else
  1402. {
  1403. CFtpDrop * pfm;
  1404. hr = CFtpDrop_Create(this, hwndOwner, &pfm);
  1405. if (SUCCEEDED(hr))
  1406. {
  1407. hr = pfm->QueryInterface(riid, ppvObj);
  1408. pfm->Release();
  1409. }
  1410. }
  1411. }
  1412. else
  1413. hr = E_NOINTERFACE;
  1414. // TODO: IID_IShellDetails
  1415. pfd->Release();
  1416. }
  1417. else
  1418. hr = E_FAIL; // Can't do that yet - Never _Initialize'd
  1419. ASSERT_POINTER_MATCHES_HRESULT(*ppvObj, hr);
  1420. if (FAILED(hr))
  1421. hr = CBaseFolder::CreateViewObject(hwndOwner, riid, ppvObj);
  1422. return hr;
  1423. }
  1424. IShellFolder * CFtpFolder::_GetLegacyShellFolder(void)
  1425. {
  1426. IShellFolder * psfLegacy = NULL;
  1427. // I assert that this succeeds because I need to make
  1428. // sure every install case has this CLSID publicly available.
  1429. if (SUCCEEDED(CoCreateInstance(CLSID_CDocObjectFolder, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder, (void **)&psfLegacy)))
  1430. {
  1431. LPITEMIDLIST pidl = GetPrivatePidlClone();
  1432. if (pidl && !ILIsEmpty(pidl))
  1433. {
  1434. LPITEMIDLIST pidlLast = (LPITEMIDLIST) ILGetLastID(pidl);
  1435. LPITEMIDLIST pidlLegacy;
  1436. if (!FtpID_IsServerItemID(pidlLast))
  1437. {
  1438. // NT #291513: We want to force the last item to always be marked as a file
  1439. // because then it will not have the trailing '/' in the URL when we
  1440. // pass it to URLMON. This way, we leave wether it's a file or dir
  1441. // ambigious for URLMON to figure out. This is because we can't
  1442. // disambiguate because the proxy blocks us but URLMON handles it
  1443. // correctly.
  1444. FtpPidl_SetFileItemType(pidlLast, FALSE);
  1445. FtpPidl_SetAttributes(pidl, FILE_ATTRIBUTE_NORMAL);
  1446. }
  1447. if (SUCCEEDED(_GetLegacyPidl(pidl, &pidlLegacy)))
  1448. {
  1449. if (FAILED(_InitLegacyShellFolder(psfLegacy, pidlLegacy)))
  1450. ATOMICRELEASE(psfLegacy);
  1451. ILFree(pidlLegacy);
  1452. }
  1453. ILFree(pidl);
  1454. }
  1455. }
  1456. return psfLegacy;
  1457. }
  1458. /*****************************************************************************\
  1459. FUNCTION: IShellFolder:: CreateViewObject
  1460. DESCRIPTION:
  1461. _UNDOCUMENTED_: This entire method is not documented.
  1462. _UNDOCUMENTED_: It is not documented that you need to
  1463. provide an IDropTarget object if you want the view to
  1464. act as a drop target.
  1465. IDropTarget produces a droptarget for the folder itself.
  1466. _UNDOCUMENTED_: The IShellView interface is not documented.
  1467. IShellView produces a shell view for the folder itself.
  1468. _UNOBVIOUS_: Not obvious that this is how the shell gets
  1469. a context menu for the folder itself. (You might think it
  1470. comes from GetUIObjectOf...)
  1471. IContextMenu produces a context menu for the folder itself.
  1472. This is important for supporting things like New and Paste.
  1473. IShellDetails (undocumented) is the direct interface to
  1474. GetDetailsOf and ColumnClick, which is now obsolete, replaced
  1475. by the DVM_GETDETAILSOF and DVM_COLUMNCLICK notifications.
  1476. _UNDOCUMENTED_: SHCreateShellFolderViewEx is not documented.
  1477. Yes, it's annoying how some things are handled by CreateViewObject
  1478. and some things are handled by GetUIObjectOf(cpidl = 0), so we
  1479. keep having to forward the requests back and forth. Particularly
  1480. annoying because the shell actually comes through both ways.
  1481. For example, if the user drags something onto a folder,
  1482. it does a CreateViewObject(IDropTarget), because it might not
  1483. be able to bind to the parent to get the IDropTarget (if the
  1484. folder is the root of a namespace).
  1485. But if you drag an object onto a subfolder of a folder, the shell
  1486. asks for a GetUIObjectOf(pidl, IDropTarget) so it can talk to
  1487. the drop target of the subobject. It does this to allow the
  1488. shell folder to create a quick IDropTarget without necessarily
  1489. binding to the subobject first.
  1490. We don't do any such optimization, so GetUIObjectOf() simply
  1491. binds to the subfolder and uses CreateViewObject().
  1492. If the IShellFolder doesn't have an FtpSite (typically because it
  1493. has not been IPersistFolder::Initialize'd), then fail any attempt
  1494. to create a view object.
  1495. \*****************************************************************************/
  1496. HRESULT CFtpFolder::CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID * ppvObj)
  1497. {
  1498. HRESULT hr = E_FAIL;
  1499. BOOL fDisplayProxyFallBackDlg = TRUE;
  1500. // We don't need to wory about falling back with a few interfaces,
  1501. // IResolveShellLink is one of them. There may be others, and we should
  1502. // add them if they are used often or in common scenarios because of the
  1503. // HUGE PERF IMPACT of _NeedToFallBack() which checks the net for a proxy
  1504. // blocking us. TODO: Investigate if IID_IDropTarget, IID_IContextMenu can be added.
  1505. if (!IsEqualIID(riid, IID_IResolveShellLink) && !IsEqualIID(riid, IID_IShellDetails) &&
  1506. _NeedToFallBack(GetPrivatePidlReference(), &fDisplayProxyFallBackDlg))
  1507. {
  1508. IShellFolder * psfLegacy = _GetLegacyShellFolder();
  1509. // We only want to display the proxy blocking dialog when we are creating the view.
  1510. if (fDisplayProxyFallBackDlg && IsEqualIID(riid, IID_IShellView))
  1511. {
  1512. DisplayBlockingProxyDialog(GetPrivatePidlReference(), hwndOwner);
  1513. }
  1514. if (psfLegacy)
  1515. {
  1516. hr = psfLegacy->CreateViewObject(hwndOwner, riid, ppvObj);
  1517. psfLegacy->Release();
  1518. }
  1519. }
  1520. else
  1521. {
  1522. hr = _CreateViewObject(hwndOwner, riid, ppvObj);
  1523. }
  1524. ASSERT_POINTER_MATCHES_HRESULT(*ppvObj, hr);
  1525. return hr;
  1526. }
  1527. /*****************************************************************************\
  1528. FUNCTION: IShellFolder::GetAttributesOf
  1529. DESCRIPTION:
  1530. If we are given cpidl = 0, then we are being asked for attributes
  1531. on the folder itself. But note that some people pass slightly
  1532. confused versions of cpidl = 0, as noted in the comment block below.
  1533. If the SFGAO_VALIDATE bit is set with cpidl = 0, then the view
  1534. object is warning us that it is about to refresh, so we should
  1535. throw away any cached information.
  1536. NOTE! ftpcm.cpp relies heavily on the fact that this routine will
  1537. fail when given complex pidls. (This allows ftpcm.cpp to assume
  1538. that all the pidls are directly in the affected folder.)
  1539. _UNDOCUMENTED_: The documentation does not say whether or not
  1540. complex pidls can be received. I don't know whether or not
  1541. they can, so I'll code defensively and watch out for them.
  1542. Does a server need to return SFGAO_HASSUBFOLDER? We don't currently
  1543. do that and it would be a lot of work and incure a huge perf hit
  1544. if we did.
  1545. \*****************************************************************************/
  1546. HRESULT CFtpFolder::GetAttributesOf(UINT cpidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut)
  1547. {
  1548. HRESULT hr;
  1549. DWORD dwMask = *rgfInOut;
  1550. // _UNDOCUMENTED_:
  1551. // Some people pass cpidl = 1 but ILIsEmpty(apidl[0]),
  1552. // intending to pass cpidl = 0. While we're being distracted
  1553. // by these sorts of people, may as well catch apidl[0] == 0 also...
  1554. // Oh, and defview sometimes passes cpidl = 1 but apidl == 0...
  1555. if (cpidl > 0 && apidl && apidl[0] && !ILIsEmpty(apidl[0]))
  1556. {
  1557. UINT ipidl;
  1558. // Can't multi-rename because there's nowhere to pass the new names.
  1559. // Can't multi-paste since you don't know where it really goes.
  1560. if (cpidl > 1)
  1561. {
  1562. *rgfInOut &= ~SFGAO_CANRENAME;
  1563. *rgfInOut &= ~SFGAO_DROPTARGET;
  1564. }
  1565. hr = S_OK;
  1566. for (ipidl = 0; ipidl < cpidl; ipidl++)
  1567. {
  1568. // This maybe a fully qualified pidl or relative pidl
  1569. LPITEMIDLIST pidlFull;
  1570. if (ILIsSimple(apidl[ipidl]))
  1571. pidlFull = CreateFullPrivatePidl(apidl[0]);
  1572. else
  1573. pidlFull = (LPITEMIDLIST) apidl[0];
  1574. *rgfInOut &= FtpGetAttributesOf(pidlFull);
  1575. // BUG NT #166783: shell32 v3 & v4 (Win95 & NT4 orig) won't allow you
  1576. // to allow SFGAO_CANLINK but disallow the link to be created in your
  1577. // own folder. So we need to disable this item in browser only.
  1578. if (SHELL_VERSION_NT5 != GetShellVersion())
  1579. *rgfInOut &= ~SFGAO_CANLINK;
  1580. if (apidl[0] != pidlFull)
  1581. ILFree(pidlFull); // We alloced it so we free it.
  1582. }
  1583. }
  1584. else
  1585. {
  1586. // At top-level, SFGAO_DROPTARGET is also disabled
  1587. if (IsRoot())
  1588. *rgfInOut &= ~SFGAO_DROPTARGET;
  1589. *rgfInOut &= ~(SFGAO_GHOSTED | SFGAO_LINK | SFGAO_READONLY |
  1590. SFGAO_SHARE | SFGAO_REMOVABLE);
  1591. if (*rgfInOut & SFGAO_VALIDATE)
  1592. InvalidateCache(); // About to refresh...
  1593. hr = S_OK;
  1594. }
  1595. // TraceMsg(TF_FTPISF, "CFtpFolder::GetAttributesOf() *rgfInOut=%#08lx, hr=%#08lx", *rgfInOut, hr);
  1596. return hr;
  1597. }
  1598. /*****************************************************************************\
  1599. DESCRIPTION:
  1600. Creates an pflHfpl and asks CFtpFolder_GetUIObjectOfHfpl (qv)
  1601. to do the real work.
  1602. Note that we always release the pflHfpl. If GetUIObjectOfHfpl
  1603. needs to keep the pflHfpl, it will do its own AddRef().
  1604. \*****************************************************************************/
  1605. HRESULT CFtpFolder::GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST rgpidl[],
  1606. REFIID riid, UINT * prgfInOut, LPVOID * ppvObj)
  1607. {
  1608. return _GetUIObjectOf(hwndOwner, cidl, rgpidl, riid, prgfInOut, ppvObj, FALSE);
  1609. }
  1610. /*****************************************************************************\
  1611. DESCRIPTION:
  1612. Creates an pflHfpl and asks CFtpFolder_GetUIObjectOfHfpl (qv)
  1613. to do the real work.
  1614. Note that we always release the pflHfpl. If GetUIObjectOfHfpl
  1615. needs to keep the pflHfpl, it will do its own AddRef().
  1616. \*****************************************************************************/
  1617. HRESULT CFtpFolder::_GetUIObjectOf(HWND hwndOwner, UINT cidl, LPCITEMIDLIST rgpidl[],
  1618. REFIID riid, UINT * prgfInOut, LPVOID * ppvObj, BOOL fFromCreateViewObject)
  1619. {
  1620. CFtpPidlList * pflHfpl = NULL;
  1621. HRESULT hr = E_FAIL;
  1622. LPITEMIDLIST pidlFull;
  1623. if (rgpidl)
  1624. pidlFull = CreateFullPrivatePidl(rgpidl[0]);
  1625. else
  1626. pidlFull = GetPrivatePidlClone();
  1627. if (ppvObj)
  1628. *ppvObj = NULL;
  1629. // Is the proxy blocking us? If yes, don't do anything
  1630. // because we don't want our Context Menu to appear for the
  1631. // original FTP UI.
  1632. // It's not blocking so go ahead.
  1633. hr = CFtpPidlList_Create(cidl, rgpidl, &pflHfpl);
  1634. if (SUCCEEDED(hr))
  1635. {
  1636. _InitFtpSite(); // GetUIObjectOfHfpl() will later need m_pfs.
  1637. hr = GetUIObjectOfHfpl(hwndOwner, pflHfpl, riid, ppvObj, fFromCreateViewObject);
  1638. pflHfpl->Release();
  1639. }
  1640. if (pidlFull)
  1641. ILFree(pidlFull);
  1642. // TraceMsg(TF_FTPISF, "CFtpFolder::GetUIObjectOf() hres=%#08lx", hr);
  1643. ASSERT_POINTER_MATCHES_HRESULT(*ppvObj, hr);
  1644. return hr;
  1645. }
  1646. /*****************************************************************************\
  1647. FUNCTION: IShellFolder::GetDisplayNameOf
  1648. DESCRIPTION:
  1649. Note! that since we do not support junctions (duh), we can
  1650. safely walk down the pidl generating goop as we go, secure
  1651. in the knowledge that we are in charge of every subpidl.
  1652. FTP UNICODE ISSUE:
  1653. The FTP spec (RFC 959 (?)) says that FTP uses 8-bit BYTEs as
  1654. names. If the 8th bit is zero, these are treated as ANSI.
  1655. But it's not specified what the 8th bit means when it's set?
  1656. Some FTP clients have been pushing DBCS/MBCS up using the 8th bit
  1657. but this incurs data loss because the code page is lost and cross
  1658. code page strings are not supported. For that reason, a combination
  1659. of UTF-8 (by default) should be used and fall back to DBCS with
  1660. code page guessing (maybe need UI to guess code page).
  1661. We need to use WININET BYTE APIs (BYTE means ANSI with an ambiguous 8th bit).
  1662. We then need to store those bytes in our cache (CFtpDir). When we display
  1663. these strings in UI, we need to convert them to unicode and guess weather
  1664. it's UTF-8 or DBCS encoded.
  1665. \*****************************************************************************/
  1666. HRESULT CFtpFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD shgno, LPSTRRET pStrRet)
  1667. {
  1668. HRESULT hr = E_FAIL;
  1669. // It's invalid for someone to pass us an empty pidl, but some do.
  1670. // (comdlg32\GetPathFromLocation) Reject it now.
  1671. if (ILIsEmpty(pidl) || !EVAL(_IsValidPidlParameter(pidl)))
  1672. return E_INVALIDARG;
  1673. LPITEMIDLIST pidlFull = CreateFullPrivatePidl(pidl);
  1674. if (pidlFull)
  1675. {
  1676. ASSERT(IsValidPIDL(pidlFull));
  1677. hr = StrRetFromFtpPidl(pStrRet, shgno, pidlFull);
  1678. ILFree(pidlFull);
  1679. }
  1680. // TraceMsg(TF_FTPISF, "CFtpFolder::GetDisplayNameOf() szName=%hs, hres=%#08lx", pStrRet->cStr, hr);
  1681. return hr;
  1682. }
  1683. /*****************************************************************************\
  1684. FUNCTION: IShellFolder::SetNameOf
  1685. DESCRIPTION:
  1686. The real work is done by SetNameOf.
  1687. \*****************************************************************************/
  1688. HRESULT CFtpFolder::SetNameOf(HWND hwndOwner, LPCITEMIDLIST pidl, LPCOLESTR pwszName,
  1689. DWORD dwReserved, LPITEMIDLIST *ppidlOut)
  1690. {
  1691. HRESULT hr = S_FALSE;
  1692. TCHAR szPath[MAX_PATH];
  1693. BOOL fContinueToRename = TRUE;
  1694. if (!EVAL(_IsValidPidlParameter(pidl)))
  1695. return E_INVALIDARG;
  1696. CFtpDir * pfd = GetFtpDir();
  1697. if (!pfd)
  1698. {
  1699. return E_OUTOFMEMORY;
  1700. }
  1701. ASSERT(IsValidPIDL(pidl));
  1702. SHUnicodeToTChar(pwszName, szPath, ARRAYSIZE(szPath));
  1703. // You can not rename a folder or file to have spaces at the beginning or end. This
  1704. // is because the path is sent over the wire as "RENAME foobar.txt foobar2.txt"
  1705. // so note that the spaces are ambiguis so the server will ignore spaces before or after the
  1706. // file. If the caller has spaces before or after the path, remove them. Spaces in
  1707. // the middle are acceptable.
  1708. PathRemoveBlanks(szPath);
  1709. // Does the new item not have an extension and this isn't a directory?
  1710. if ((0 == PathFindExtension(szPath)[0]) && !FtpPidl_IsDirectory(pidl, FALSE))
  1711. {
  1712. LPCWIRESTR pszFileName = FtpPidl_GetLastItemWireName(pidl);
  1713. // Yes, then we are scared that they may be loosing an extension.
  1714. // Did the original name have an extension?
  1715. if (pszFileName && PathFindExtensionA(pszFileName)[0])
  1716. {
  1717. // Yes, so now we are scared they may loose it and not be able
  1718. // to find the src app. Ask the user if they really want to do this
  1719. // rename if that will mean the file will no longer have an extension.
  1720. // Hey browser, can I display UI?
  1721. if (EVAL(hwndOwner))
  1722. {
  1723. // Hay browser, cover me, I'm going to do UI.
  1724. IUnknown_EnableModless(_punkSite, FALSE);
  1725. TCHAR szTitle[MAX_PATH];
  1726. TCHAR szReplaceMsg[MAX_PATH*4];
  1727. EVAL(LoadString(HINST_THISDLL, IDS_FTPERR_RENAME_TITLE, szTitle, ARRAYSIZE(szTitle)));
  1728. EVAL(LoadString(HINST_THISDLL, IDS_FTPERR_RENAME_EXT_WRN, szReplaceMsg, ARRAYSIZE(szReplaceMsg)));
  1729. if (IDNO == MessageBox(hwndOwner, szReplaceMsg, szTitle, (MB_YESNO | MB_ICONEXCLAMATION)))
  1730. fContinueToRename = FALSE; // Cancel the rename.
  1731. IUnknown_EnableModless(_punkSite, TRUE);
  1732. }
  1733. }
  1734. }
  1735. if (fContinueToRename)
  1736. {
  1737. if (pfd)
  1738. {
  1739. hr = pfd->SetNameOf(this, hwndOwner, pidl, szPath, dwReserved, ppidlOut);
  1740. if (FAILED(hr) && (HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr) && hwndOwner)
  1741. {
  1742. DisplayWininetError(hwndOwner, TRUE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_FILERENAME, IDS_FTPERR_WININET, MB_OK, NULL);
  1743. }
  1744. }
  1745. TraceMsg(TF_FTPISF, "CFtpFolder::SetNameOf(%ls) hres=%#08lx", pwszName, hr);
  1746. // shell32.dll in IE4 (maybe earlier also) will infinitely call
  1747. // CFtpFolder::SetNameOf() over and over if it returns FAILED(hr);
  1748. if (FAILED(hr))
  1749. hr = S_FALSE;
  1750. }
  1751. if (ppidlOut)
  1752. {
  1753. ASSERT_POINTER_MATCHES_HRESULT(*ppidlOut, hr);
  1754. }
  1755. if (pfd)
  1756. {
  1757. pfd->Release();
  1758. }
  1759. return hr;
  1760. }
  1761. //===========================
  1762. // *** IPersistFolder Interface ***
  1763. //===========================
  1764. /*****************************************************************************\
  1765. FUNCTION: IPersistFolder::Initialize
  1766. DESCRIPTION:
  1767. This is called when the shell creates a new "root".
  1768. Note that we pass a fake "null" ID list as the second
  1769. pidl to ::_Initialize, q.v., for explanation.
  1770. _UNDOCUMENTED_: Undocumented method in undocumented interface.
  1771. \*****************************************************************************/
  1772. HRESULT CFtpFolder::Initialize(LPCITEMIDLIST pidl)
  1773. {
  1774. ASSERT(IsValidPIDL(pidl));
  1775. HRESULT hr = _Initialize(pidl, NULL, ILGetSize(pidl) - sizeof(pidl->mkid.cb));
  1776. //TraceMsg(TF_FTPISF, "CFtpFolder::Initialize() hres=%#08lx", hr);
  1777. return hr;
  1778. }
  1779. //===========================
  1780. // *** IShellIcon Interface ***
  1781. //===========================
  1782. /*****************************************************************************\
  1783. FUNCTION: IShellIcon::GetIconOf
  1784. DESCRIPTION:
  1785. Get the system icon imagelist index for a pidl. Subtlety - If we are
  1786. enumerating children of the root, their icon is a computer.
  1787. _UNDOCUMENTED_: Undocumented method in undocumented interface.
  1788. \*****************************************************************************/
  1789. HRESULT CFtpFolder::GetIconOf(LPCITEMIDLIST pidl, UINT gil, LPINT pnIcon)
  1790. {
  1791. HRESULT hr = S_OK;
  1792. if (!EVAL(_IsValidPidlParameter(pidl)))
  1793. return E_INVALIDARG;
  1794. // If we want custom icons, make that change here. We could want to do
  1795. // that if we wanted folder icons from FTP sites to look different than
  1796. // folder icons from the file system. But we don't currently want that.
  1797. *pnIcon = GetFtpIcon(gil, IsRoot());
  1798. ASSERT(IsValidPIDL(pidl));
  1799. if (EVAL(!IsRoot())) // GetFtpIcon() is wrong so either fix it or verify we never try to use it.
  1800. {
  1801. SHFILEINFO sfi;
  1802. hr = FtpPidl_GetFileInfo(pidl, &sfi, SHGFI_SYSICONINDEX |
  1803. ((gil & GIL_OPENICON) ? SHGFI_OPENICON : 0));
  1804. if (SUCCEEDED(hr))
  1805. {
  1806. *pnIcon = sfi.iIcon;
  1807. if (sfi.hIcon)
  1808. DestroyIcon(sfi.hIcon);
  1809. }
  1810. }
  1811. // TraceMsg(TF_FTPISF, "CFtpFolder::GetIconOf() hres=%#08lx", hr);
  1812. return hr;
  1813. }
  1814. //===========================
  1815. // *** IShellIconOverlay Interface ***
  1816. //===========================
  1817. HRESULT CFtpFolder::GetOverlayIndexHelper(LPCITEMIDLIST pidl, int * pIndex, DWORD dwFlags)
  1818. {
  1819. HRESULT hr = E_FAIL;
  1820. *pIndex = 0;
  1821. // Is this a soft link? (Meaning it won't have a windows link
  1822. // extension (.lnk, .url, ...) but we still want the shortcut cue.
  1823. if (pidl && FtpPidl_IsSoftLink(pidl))
  1824. {
  1825. if (!m_psiom)
  1826. {
  1827. hr = CoCreateInstance(CLSID_CFSIconOverlayManager, NULL, CLSCTX_INPROC_SERVER, IID_IShellIconOverlayManager, (void **)&m_psiom);
  1828. }
  1829. if (m_psiom)
  1830. {
  1831. hr = m_psiom->GetReservedOverlayInfo(L"", FtpPidl_GetAttributes(pidl), pIndex, dwFlags, SIOM_RESERVED_LINK);
  1832. }
  1833. }
  1834. return hr;
  1835. }
  1836. //===========================
  1837. // *** IDelegateFolder Interface ***
  1838. //===========================
  1839. /*****************************************************************************\
  1840. FUNCTION: IDelegateFolder::SetItemAlloc
  1841. DESCRIPTION:
  1842. Gives us the pidl allocator.
  1843. \*****************************************************************************/
  1844. HRESULT CFtpFolder::SetItemAlloc(IMalloc *pm)
  1845. {
  1846. IUnknown_Set(&m_pm, pm);
  1847. // TraceMsg(TF_FTPISF, "CFtpFolder::SetItemAlloc(IMalloc *pm=%#08lx) hres=%#08lx", pm, S_OK);
  1848. return S_OK;
  1849. }
  1850. //===========================
  1851. // *** IBrowserFrameOptions Interface ***
  1852. //===========================
  1853. /*****************************************************************************\
  1854. DESCRIPTION:
  1855. Tell the browser/host what behaviors we want. This lets the caller
  1856. know when we want to act like the shell, the browser, or even unique.
  1857. \*****************************************************************************/
  1858. HRESULT CFtpFolder::GetFrameOptions(IN BROWSERFRAMEOPTIONS dwMask, OUT BROWSERFRAMEOPTIONS * pdwOptions)
  1859. {
  1860. // This function is called in the follow situations:
  1861. // ftp://bryanst/ (w/ & w/o folder shortcut)
  1862. // ftp://bryanst/default.htm (w/ & w/o folder shortcut)
  1863. // ftp://bryanst/notes.txt (w/ & w/o folder shortcut)
  1864. // ftp://bryanst/resume.doc (w/ & w/o folder shortcut)
  1865. // ftp://bryanst/ (w/ & w/o folder shortcut)
  1866. // ftp://bryanst/ (w/ & w/o folder shortcut)
  1867. // ftp://bryanst/ (w/ & w/o folder shortcut)
  1868. HRESULT hr = E_INVALIDARG;
  1869. if (pdwOptions)
  1870. {
  1871. // We want both "Internet Options" and "Folder Options".
  1872. *pdwOptions = dwMask & (BFO_BOTH_OPTIONS | BFO_BROWSE_NO_IN_NEW_PROCESS |
  1873. BFO_NO_REOPEN_NEXT_RESTART |
  1874. BFO_ENABLE_HYPERLINK_TRACKING | BFO_USE_IE_LOGOBANDING |
  1875. BFO_ADD_IE_TOCAPTIONBAR | BFO_USE_DIALUP_REF);
  1876. hr = S_OK;
  1877. }
  1878. return hr;
  1879. }
  1880. /*****************************************************************************\
  1881. FUNCTION: CFtpFolder_Create
  1882. DESCRIPTION:
  1883. Note that we release the pff that Common_New created, because we
  1884. are done with it. The real refcount is handled by the
  1885. CFtpFolder_QueryInterface.
  1886. \*****************************************************************************/
  1887. HRESULT CFtpFolder_Create(REFIID riid, LPVOID * ppvObj)
  1888. {
  1889. HRESULT hres = E_OUTOFMEMORY;
  1890. CFtpFolder * pff = new CFtpFolder();
  1891. *ppvObj = NULL;
  1892. if (pff)
  1893. {
  1894. hres = pff->QueryInterface(riid, ppvObj);
  1895. pff->Release();
  1896. }
  1897. ASSERT_POINTER_MATCHES_HRESULT(*ppvObj, hres);
  1898. return hres;
  1899. }
  1900. /*****************************************************************************\
  1901. DESCRIPTION:
  1902. \*****************************************************************************/
  1903. HRESULT CFtpFolder_Create(LPCITEMIDLIST pidlTarget, LPCITEMIDLIST pidlRoot, int ib, REFIID riid, LPVOID * ppvObj)
  1904. {
  1905. HRESULT hr = S_OK;
  1906. CFtpFolder * pff = new CFtpFolder();
  1907. ASSERT(IsValidPIDL(pidlTarget));
  1908. ASSERT(!pidlRoot || IsValidPIDL(pidlRoot));
  1909. *ppvObj = NULL;
  1910. if (!pff)
  1911. {
  1912. return E_OUTOFMEMORY;
  1913. }
  1914. else
  1915. {
  1916. hr = pff->_Initialize(pidlTarget, pidlRoot, ib); // Can fail in out of memory
  1917. if (SUCCEEDED(hr))
  1918. hr = pff->QueryInterface(riid, ppvObj);
  1919. pff->Release();
  1920. }
  1921. ASSERT_POINTER_MATCHES_HRESULT(*ppvObj, hr);
  1922. return hr;
  1923. }
  1924. /****************************************************\
  1925. Constructor
  1926. \****************************************************/
  1927. CFtpFolder::CFtpFolder() : CBaseFolder((LPCLSID) &CLSID_FtpFolder)
  1928. {
  1929. DllAddRef();
  1930. // This needs to be allocated in Zero Inited Memory.
  1931. // Assert that all Member Variables are inited to Zero.
  1932. ASSERT(!m_pfs);
  1933. ASSERT(!m_pm);
  1934. ASSERT(!m_puhs);
  1935. ASSERT(!m_psiom);
  1936. // Needed because we need to call CoCreateInstance() on Browser Only.
  1937. LEAK_ADDREF(LEAK_CFtpFolder);
  1938. }
  1939. /****************************************************\
  1940. Destructor
  1941. \****************************************************/
  1942. CFtpFolder::~CFtpFolder()
  1943. {
  1944. ATOMICRELEASE(m_pfs);
  1945. ATOMICRELEASE(m_pm);
  1946. ATOMICRELEASE(m_puhs);
  1947. ATOMICRELEASE(m_psiom);
  1948. if (m_hinstInetCpl)
  1949. FreeLibrary(m_hinstInetCpl);
  1950. DllRelease();
  1951. LEAK_DELREF(LEAK_CFtpFolder);
  1952. }
  1953. //===========================
  1954. // *** IUnknown Interface ***
  1955. //===========================
  1956. HRESULT CFtpFolder::QueryInterface(REFIID riid, void **ppvObj)
  1957. {
  1958. if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IShellIcon))
  1959. {
  1960. *ppvObj = SAFECAST(this, IShellIcon*);
  1961. }
  1962. // This causes all icons to use my custom ftp folder icon, so I will do this when that is fixed.
  1963. #ifdef FEATURE_SOFTLINK_SHORTCUT_ICONOVERLAY
  1964. else if (IsEqualIID(riid, IID_IShellIconOverlay))
  1965. {
  1966. *ppvObj = SAFECAST(this, IShellIconOverlay*);
  1967. }
  1968. #endif // FEATURE_SOFTLINK_SHORTCUT_ICONOVERLAY
  1969. else if (IsEqualIID(riid, IID_IPersistFolder))
  1970. {
  1971. *ppvObj = SAFECAST(this, IPersistFolder*);
  1972. }
  1973. else if (IsEqualIID(riid, IID_IDelegateFolder))
  1974. {
  1975. *ppvObj = SAFECAST(this, IDelegateFolder*);
  1976. }
  1977. else if (IsEqualIID(riid, IID_IObjectWithSite))
  1978. {
  1979. *ppvObj = SAFECAST(this, IObjectWithSite*);
  1980. }
  1981. else if (IsEqualIID(riid, IID_IPersistFolder2))
  1982. {
  1983. *ppvObj = SAFECAST(this, IPersistFolder2*);
  1984. }
  1985. else if (IsEqualIID(riid, IID_IShellPropSheetExt))
  1986. {
  1987. *ppvObj = SAFECAST(this, IShellPropSheetExt*);
  1988. }
  1989. else if (IsEqualIID(riid, IID_IBrowserFrameOptions))
  1990. {
  1991. *ppvObj = SAFECAST(this, IBrowserFrameOptions*);
  1992. }
  1993. else if (IsEqualIID(riid, IID_CFtpFolder))
  1994. {
  1995. // Only valid if caller lives in msieftp.dll
  1996. *ppvObj = (void *)this;
  1997. }
  1998. else
  1999. return CBaseFolder::QueryInterface(riid, ppvObj);
  2000. AddRef();
  2001. return S_OK;
  2002. }