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

2770 lines
84 KiB

  1. #include "priv.h"
  2. #include "sccls.h"
  3. #include "dochost.h"
  4. #include "resource.h"
  5. #include "stdenum.h"
  6. #include <idhidden.h>
  7. #include "shdocfl.h"
  8. #include <vdate.h>
  9. #include <mluisupp.h>
  10. #ifdef UNIX
  11. #include "unixstuff.h"
  12. #endif
  13. HRESULT CDocObjectView_Create(IShellView** ppv, IShellFolder* psf, LPCITEMIDLIST pidl);
  14. #define DM_STARTUP 0
  15. #define DM_CDOFPDN 0 // CDocObjectFolder::ParseDisplayName
  16. class CDocObjectFolder : public IShellFolder2,
  17. public IPersistFolder2,
  18. public IBrowserFrameOptions
  19. {
  20. public:
  21. CDocObjectFolder(LPCITEMIDLIST pidlRoot = NULL);
  22. // IUnknown
  23. STDMETHODIMP QueryInterface(REFIID, void **);
  24. STDMETHODIMP_(ULONG) AddRef(void);
  25. STDMETHODIMP_(ULONG) Release(void);
  26. // IShellFolder
  27. STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszDisplayName,
  28. ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes);
  29. STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList);
  30. STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut);
  31. STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj);
  32. STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  33. STDMETHODIMP CreateViewObject (HWND hwnd, REFIID riid, void **ppvOut);
  34. STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut);
  35. STDMETHODIMP GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST * apidl,
  36. REFIID riid, UINT * prgfInOut, void **ppvOut);
  37. STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName);
  38. STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName,
  39. DWORD uFlags, LPITEMIDLIST * ppidlOut);
  40. // IShellFolder2
  41. STDMETHODIMP GetDefaultSearchGUID(LPGUID pGuid);
  42. STDMETHODIMP EnumSearches(LPENUMEXTRASEARCH *ppenum);
  43. STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay) { return E_NOTIMPL; };
  44. STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pbState) { return E_NOTIMPL; };
  45. STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv) { return E_NOTIMPL; };
  46. STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails){ return E_NOTIMPL; };
  47. STDMETHODIMP MapColumnToSCID(UINT iCol, SHCOLUMNID *pscid) { return E_NOTIMPL; };
  48. // IPersistFolder
  49. STDMETHODIMP GetClassID(LPCLSID pClassID);
  50. STDMETHODIMP Initialize(LPCITEMIDLIST pidl);
  51. // IPersistFolder2
  52. STDMETHODIMP GetCurFolder(LPITEMIDLIST* ppidl);
  53. // IBrowserFrameOptions
  54. STDMETHODIMP GetFrameOptions(IN BROWSERFRAMEOPTIONS dwMask, OUT BROWSERFRAMEOPTIONS * pdwOptions);
  55. protected:
  56. ~CDocObjectFolder();
  57. LONG _cRef;
  58. LPITEMIDLIST _pidlRoot;
  59. };
  60. //========================================================================
  61. // CDocObjectFolder members
  62. //========================================================================
  63. CDocObjectFolder::CDocObjectFolder(LPCITEMIDLIST pidlRoot)
  64. : _cRef(1), _pidlRoot(NULL)
  65. {
  66. TraceMsg(TF_SHDLIFE, "ctor CDocObjectFolder %x", this);
  67. DllAddRef();
  68. if (pidlRoot)
  69. _pidlRoot = ILClone(pidlRoot);
  70. }
  71. CDocObjectFolder::~CDocObjectFolder()
  72. {
  73. TraceMsg(TF_SHDLIFE, "dtor CDocObjectFolder %x", this);
  74. if (_pidlRoot)
  75. ILFree(_pidlRoot);
  76. DllRelease();
  77. }
  78. HRESULT CDocObjectFolder::QueryInterface(REFIID riid, void **ppvObj)
  79. {
  80. static const QITAB qit[] = {
  81. QITABENTMULTI(CDocObjectFolder, IShellFolder, IShellFolder2),
  82. QITABENT(CDocObjectFolder, IShellFolder2),
  83. QITABENTMULTI(CDocObjectFolder, IPersistFolder, IPersistFolder2),
  84. QITABENT(CDocObjectFolder, IPersistFolder2),
  85. QITABENT(CDocObjectFolder, IBrowserFrameOptions),
  86. { 0 },
  87. };
  88. return QISearch(this, qit, riid, ppvObj);
  89. }
  90. ULONG CDocObjectFolder::AddRef()
  91. {
  92. return InterlockedIncrement(&_cRef);
  93. }
  94. ULONG CDocObjectFolder::Release()
  95. {
  96. if (InterlockedDecrement(&_cRef))
  97. return _cRef;
  98. delete this;
  99. return 0;
  100. }
  101. HRESULT CDocObjectFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pwszDisplayName,
  102. ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes)
  103. {
  104. AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL"));
  105. *ppidl = NULL;
  106. return E_UNEXPECTED;
  107. }
  108. HRESULT CDocObjectFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList)
  109. {
  110. *ppenumIDList = NULL;
  111. return E_UNEXPECTED;
  112. }
  113. HRESULT CDocObjectFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut)
  114. {
  115. AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL"));
  116. *ppvOut = NULL;
  117. return E_UNEXPECTED;
  118. }
  119. HRESULT CDocObjectFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj)
  120. {
  121. AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL"));
  122. *ppvObj = NULL;
  123. return E_UNEXPECTED;
  124. }
  125. HRESULT CDocObjectFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  126. {
  127. AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL"));
  128. return E_UNEXPECTED;
  129. }
  130. HRESULT CDocObjectFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppvOut)
  131. {
  132. HRESULT hres = E_FAIL;
  133. if (IsEqualIID(riid, IID_IShellView))
  134. {
  135. hres = CDocObjectView_Create((IShellView**)ppvOut, this, _pidlRoot);
  136. }
  137. else
  138. {
  139. hres = E_NOINTERFACE;
  140. *ppvOut = NULL;
  141. }
  142. return hres;
  143. }
  144. HRESULT CDocObjectFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
  145. REFIID riid, UINT *prgfInOut, void **ppvOut)
  146. {
  147. AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL"));
  148. *ppvOut = NULL;
  149. return E_UNEXPECTED;
  150. }
  151. HRESULT CDocObjectFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut)
  152. {
  153. // we should never have any children.
  154. ASSERT(cidl == 0);
  155. if (cidl != 0)
  156. return E_UNEXPECTED;
  157. if (*rgfInOut)
  158. {
  159. // they want to know about the document itself
  160. ASSERT(_pidlRoot);
  161. return SHGetAttributesOf(_pidlRoot, rgfInOut);
  162. }
  163. return S_OK;
  164. }
  165. HRESULT CDocObjectFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName)
  166. {
  167. AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL"));
  168. return E_UNEXPECTED;
  169. }
  170. HRESULT CDocObjectFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName,
  171. DWORD uFlags, LPITEMIDLIST *ppidlOut)
  172. {
  173. return E_UNEXPECTED;
  174. }
  175. HRESULT CDocObjectFolder::GetDefaultSearchGUID(GUID *pGuid)
  176. {
  177. *pGuid = SRCID_SWebSearch;
  178. return S_OK;
  179. }
  180. HRESULT CDocObjectFolder::EnumSearches(LPENUMEXTRASEARCH *ppenum)
  181. {
  182. *ppenum = NULL;
  183. return E_NOTIMPL;
  184. }
  185. HRESULT CDocObjectFolder::GetClassID(CLSID *pClassID)
  186. {
  187. *pClassID = CLSID_CDocObjectFolder;
  188. return S_OK;
  189. }
  190. HRESULT CDocObjectFolder::Initialize(LPCITEMIDLIST pidl)
  191. {
  192. if (_pidlRoot)
  193. {
  194. ILFree(_pidlRoot);
  195. _pidlRoot = NULL;
  196. }
  197. if (pidl)
  198. _pidlRoot = ILClone(pidl);
  199. return S_OK;
  200. }
  201. HRESULT CDocObjectFolder::GetCurFolder(LPITEMIDLIST* ppidl)
  202. {
  203. return SHILClone(_pidlRoot, ppidl);
  204. }
  205. // IBrowserFrameOptions
  206. #define BASE_OPTIONS \
  207. (BFO_BROWSER_PERSIST_SETTINGS | BFO_RENAME_FOLDER_OPTIONS_TOINTERNET | \
  208. BFO_PREFER_IEPROCESS | BFO_ENABLE_HYPERLINK_TRACKING | \
  209. BFO_USE_IE_LOGOBANDING | BFO_ADD_IE_TOCAPTIONBAR | BFO_GO_HOME_PAGE | \
  210. BFO_USE_IE_TOOLBAR | BFO_NO_PARENT_FOLDER_SUPPORT | BFO_NO_REOPEN_NEXT_RESTART | \
  211. BFO_SHOW_NAVIGATION_CANCELLED)
  212. // IBrowserFrameOptions
  213. HRESULT CDocObjectFolder::GetFrameOptions(IN BROWSERFRAMEOPTIONS dwMask, OUT BROWSERFRAMEOPTIONS * pdwOptions)
  214. {
  215. // We are hosting a DocObj?
  216. BOOL fIsFileURL = FALSE;
  217. // Is this under the Internet Name Space? Yes for
  218. // HTTP and FTP owned by the IE name space. MSIEFTP
  219. // pidls are passed straight to that folder.
  220. // This function will return FALSE for non-IE stuff
  221. // but we will then want to check if it's a file system
  222. // thing that wants to act like a web page because it's
  223. // MIME TYPE or other association is associated with the web.
  224. if (!IsURLChild(_pidlRoot, TRUE))
  225. {
  226. // Since IsURLChild() returned FALSE, this must be in the file system.
  227. // This case will happen with:
  228. // C:\foo.htm
  229. // http://www.yahoo.com/
  230. // http://bryanst/resume.doc
  231. // http://bryanst/notes.txt
  232. // <Start Page> [I couldn't find a case that hit CInternetFolder]
  233. // C:\foo.doc (use the addressbar to repro)
  234. fIsFileURL = TRUE;
  235. }
  236. *pdwOptions = dwMask & BASE_OPTIONS;
  237. if (!fIsFileURL)
  238. {
  239. // Add the Offline Support when we aren't in the file system.
  240. *pdwOptions |= dwMask & (BFO_USE_IE_OFFLINE_SUPPORT | BFO_USE_DIALUP_REF);
  241. }
  242. return S_OK;
  243. }
  244. STDAPI CDocObjectFolder_CreateInstance(IUnknown* pUnkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
  245. {
  246. CDocObjectFolder *psf = new CDocObjectFolder;
  247. if (psf)
  248. {
  249. *ppunk = SAFECAST(psf, IShellFolder *);
  250. return S_OK;
  251. }
  252. return E_OUTOFMEMORY;
  253. }
  254. class CInternetFolder : CDocObjectFolder
  255. {
  256. public:
  257. CInternetFolder(LPCITEMIDLIST pidlRoot = NULL) ;
  258. // IUnknown
  259. STDMETHODIMP QueryInterface(REFIID, void **);
  260. STDMETHODIMP_(ULONG) AddRef(void);
  261. STDMETHODIMP_(ULONG) Release(void);
  262. // IShellFolder
  263. STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszDisplayName,
  264. ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes);
  265. STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList);
  266. STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut);
  267. STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj);
  268. STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  269. STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut);
  270. STDMETHODIMP GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
  271. REFIID riid, UINT *prgfInOut, void **ppvOut);
  272. STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName);
  273. STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName,
  274. DWORD uFlags, LPITEMIDLIST * ppidlOut);
  275. // IPersistFolder
  276. STDMETHODIMP GetClassID(CLSID *pClassID);
  277. // IBrowserFrameOptions
  278. STDMETHODIMP GetFrameOptions(IN BROWSERFRAMEOPTIONS dwMask, OUT BROWSERFRAMEOPTIONS * pdwOptions);
  279. protected:
  280. ~CInternetFolder();
  281. HRESULT _CreateProtocolHandler(LPCSTR pszProtocol, IBindCtx * pbc, IShellFolder **ppsf);
  282. HRESULT _CreateProtocolHandlerFromPidl(LPCITEMIDLIST pidl, IBindCtx * pbc, IShellFolder **ppsf);
  283. HRESULT _GetAttributesOfProtocol(LPCSTR pszProtocol, LPCITEMIDLIST *apidl, UINT cpidl, ULONG *rgfInOut);
  284. HRESULT _FaultInUrlHandler(LPCSTR pszProtocol, LPCTSTR pszUrl, IUnknown * punkSite);
  285. HRESULT _ConditionallyFaultInUrlHandler(LPCSTR pszProtocol, LPCTSTR pszUrl, IBindCtx * pbc);
  286. HRESULT _AssocCreate(LPCITEMIDLIST pidl, REFIID riid, void * *ppv);
  287. HRESULT _GetScheme(LPCITEMIDLIST pidl, LPWSTR pszOut, DWORD cchOut);
  288. HRESULT _GetUIObjectFromShortcut(LPCITEMIDLIST pidl, REFIID riid, void **ppvOut);
  289. HRESULT _GetTitle(LPCWSTR pszUrl, STRRET *pstr);
  290. HRESULT _InitHistoryStg(IUrlHistoryStg **pphist);
  291. IUrlHistoryStg *_phist;
  292. };
  293. CInternetFolder::CInternetFolder(LPCITEMIDLIST pidlRoot)
  294. : CDocObjectFolder(pidlRoot)
  295. {
  296. TraceMsg(TF_URLNAMESPACE, "[%X] ctor CInternetFolder", this);
  297. ASSERT(NULL == _phist);
  298. }
  299. CInternetFolder::~CInternetFolder()
  300. {
  301. ATOMICRELEASE(_phist);
  302. TraceMsg(TF_URLNAMESPACE, "[%X] dtor CInternetFolder", this);
  303. }
  304. HRESULT CInternetFolder::QueryInterface(REFIID riid, void **ppvObj)
  305. {
  306. static const QITAB qit[] = {
  307. QITABENTMULTI(CInternetFolder, IShellFolder, IShellFolder2),
  308. QITABENT(CInternetFolder, IShellFolder2),
  309. QITABENTMULTI(CDocObjectFolder, IPersistFolder, IPersistFolder2),
  310. QITABENT(CDocObjectFolder, IPersistFolder2),
  311. QITABENT(CInternetFolder, IBrowserFrameOptions),
  312. { 0 },
  313. };
  314. return QISearch(this, qit, riid, ppvObj);
  315. }
  316. ULONG CInternetFolder::AddRef()
  317. {
  318. return InterlockedIncrement(&_cRef);
  319. }
  320. ULONG CInternetFolder::Release()
  321. {
  322. if (InterlockedDecrement(&_cRef))
  323. return _cRef;
  324. delete this;
  325. return 0;
  326. }
  327. typedef struct tagURLID
  328. {
  329. ITEMIDLIST idl; // cb and SHID
  330. BYTE bType; // URLID
  331. UINT uiCP; // Code Page
  332. WCHAR achUrl[1]; // variable size string
  333. } URLID;
  334. #define SHID_INTERNET 0x60
  335. #define SHID_INTERNET_SITE 0x61 // IE name space item
  336. #define URLID_URLBASEA 0x00
  337. /////// URLID_LOCATION 0x01 // LEGACY IE3/4 used for Frag IDs
  338. /////// URLID_FTPFOLDER 0x02 // LEGACY used by a pre-release FTP Folder dll
  339. #define URLID_PROTOCOL 0x03 // this is actually a delegated protocol
  340. #define URLID_URLBASEW 0x80 //
  341. // URLIDF_UNICODE 0x80 // URLID_ is actually of UNICODE type
  342. #ifdef UNICODE
  343. #define URLID_URLBASE URLID_URLBASEW
  344. #else
  345. #define URLID_URLBASE URLID_URLBASEA
  346. #endif
  347. typedef const UNALIGNED URLID *PCURLID;
  348. typedef UNALIGNED URLID *PURLID;
  349. #define PDID_SIG MAKEWORD(SHID_INTERNET_SITE, URLID_PROTOCOL)
  350. inline PCDELEGATEITEMID _IsValidDelegateID(LPCITEMIDLIST pidl)
  351. {
  352. PCDELEGATEITEMID pdi = (PCDELEGATEITEMID)pidl;
  353. ASSERT(pdi);
  354. if ((pdi->cbSize >= (SIZEOF(PDELEGATEITEMID)-1))
  355. && (pdi->wOuter == PDID_SIG))
  356. return pdi;
  357. return NULL;
  358. }
  359. LPCSTR _PidlToDelegateProtocol(LPCITEMIDLIST pidl)
  360. {
  361. PCDELEGATEITEMID pdi = _IsValidDelegateID(pidl);
  362. if (pdi)
  363. return (LPCSTR)&(pdi->rgb[pdi->cbInner]);
  364. return NULL;
  365. }
  366. inline PCURLID _IsValidUrlID(LPCITEMIDLIST pidl)
  367. {
  368. PCURLID purlid = (PCURLID)pidl;
  369. ASSERT(purlid);
  370. // 98/12/22 #263932 vtan: ANSI and Unicode URLs are both valid. Use function
  371. // _ExtractURL to extract the URL from the PIDL as a Unicode string.
  372. if (purlid->idl.mkid.cb >= SIZEOF(URLID)
  373. && (purlid->idl.mkid.abID[0] == SHID_INTERNET_SITE)
  374. && (purlid->bType == URLID_URLBASEA || purlid->bType == URLID_URLBASEW || _IsValidDelegateID(pidl)))
  375. return purlid;
  376. return NULL;
  377. }
  378. // 98/12/22 #263932 vtan: IE4 stores the PIDL in a stream as an ANSI
  379. // string. IE5 stores the PIDL in a stream as a Unicode string. This
  380. // functions reads the string (ANSI or Unicode) and converts it to
  381. // an internal Unicode string which is what will be written to the stream.
  382. void _ExtractURL (PCURLID pURLID, LPWSTR wszURL, int iCharCount)
  383. {
  384. if (pURLID->bType == URLID_URLBASEA)
  385. {
  386. char aszURL[MAX_URL_STRING];
  387. #ifdef UNIX
  388. ualstrcpynA(aszURL, (const char*)(pURLID->achUrl), sizeof(aszURL));
  389. #else
  390. ualstrcpynA(aszURL, reinterpret_cast<const char*>(pURLID->achUrl), sizeof(aszURL));
  391. #endif
  392. SHAnsiToUnicode(aszURL, wszURL, iCharCount);
  393. }
  394. else if (pURLID->bType == URLID_URLBASEW)
  395. {
  396. ualstrcpynW(wszURL, pURLID->achUrl, iCharCount);
  397. }
  398. }
  399. // 99/01/04 vtan: Added the following to help compare URLIDs which
  400. // can be AA/UU/AU/UA and perform the correct comparison.
  401. int _CompareURL (PCURLID pURLID1, PCURLID pURLID2)
  402. {
  403. int iResult;
  404. if ((pURLID1->bType == URLID_URLBASEA) && (pURLID2->bType == URLID_URLBASEA))
  405. {
  406. #ifdef UNIX
  407. iResult = ualstrcmpA((const char*)(pURLID1->achUrl), (const char*)(pURLID2->achUrl));
  408. #else
  409. iResult = ualstrcmpA(reinterpret_cast<const char*>(pURLID1->achUrl), reinterpret_cast<const char*>(pURLID2->achUrl));
  410. #endif
  411. }
  412. else if ((pURLID1->bType == URLID_URLBASEW) && (pURLID2->bType == URLID_URLBASEW))
  413. {
  414. iResult = ualstrcmpW(pURLID1->achUrl, pURLID2->achUrl);
  415. }
  416. else
  417. {
  418. PCURLID pCompareURLID;
  419. WCHAR wszURL[MAX_URL_STRING];
  420. // AU/UA comparison. To be efficient only convert the ANSI URLID
  421. // to Unicode and perform the comparison in Unicode.
  422. if (pURLID1->bType == URLID_URLBASEA)
  423. {
  424. pCompareURLID = pURLID2;
  425. _ExtractURL(pURLID1, wszURL, SIZECHARS(wszURL));
  426. }
  427. else
  428. {
  429. pCompareURLID = pURLID1;
  430. _ExtractURL(pURLID2, wszURL, SIZECHARS(wszURL));
  431. }
  432. iResult = ualstrcmpW(pCompareURLID->achUrl, wszURL);
  433. }
  434. return iResult;
  435. }
  436. IShellFolder* g_psfInternet = NULL;
  437. STDAPI CDelegateMalloc_Create(void *pv, UINT cbSize, WORD wOuter, IMalloc **ppmalloc);
  438. //
  439. // this might modify pszName if it's not a fully qualified url!
  440. BOOL _ValidateURL(LPTSTR pszName, DWORD dwFlags)
  441. {
  442. //
  443. // WARNING: In order to allow URL extensions, we assume all strings
  444. // which contains ":" in it is a valid string.
  445. // Assumptions are:
  446. //
  447. // (1) CDesktop::ParseDisplayName parse file system strings first.
  448. // (2) URL moniker will return an error correctly if URL is not valid.
  449. // (3) someone else (the desktop folder) handles shell: URLs
  450. // they should not be used directly by the browser
  451. //
  452. return SUCCEEDED(IURLQualify(pszName, dwFlags, pszName, NULL, NULL)) && (-1 != GetUrlScheme(pszName));
  453. HRESULT hr = IURLQualify(pszName, dwFlags, pszName, NULL, NULL);
  454. DWORD nScheme = GetUrlScheme(pszName);
  455. return SUCCEEDED(hr) && (-1 != nScheme) && (URL_SCHEME_SHELL != nScheme);
  456. }
  457. LPITEMIDLIST IEILAppendFragment(LPITEMIDLIST pidl, LPCWSTR pszFragment)
  458. {
  459. // WARNING: See IE5 bug #'s 86951 and 36497 for more details.
  460. // In a nutshell, we're rolling back the change for 36497 because
  461. // the change caused many more problems with customers than
  462. // the behavior we had in IE4.
  463. //
  464. // Because we're not ensuring that
  465. // the fragment is prefixed with a '#', there may be
  466. // cases where the URL in the address bar looks wrong,
  467. // as well as cases where a hyperlink to a different doc
  468. // or HTML page may fail if it contains a fragment.
  469. return ILAppendHiddenStringW(pidl, IDLHID_URLFRAGMENT, pszFragment);
  470. }
  471. // browser only uglyness... we need to construct a desktop relative "regitem" pidl for
  472. // the internet since browser only shell does not support psf->ParseDisplayName("::{guid}", &pidl)
  473. // this uses the same layout as REGITEMs so we have PIDL compatibility with integrated mode
  474. // this ensures that a shortcut to the IE icon made in browser only mode works in integrated
  475. #ifndef NOPRAGMAS
  476. #pragma pack(1)
  477. #endif
  478. typedef struct
  479. {
  480. WORD cb;
  481. BYTE bFlags;
  482. BYTE bReserved; // This is to get DWORD alignment
  483. CLSID clsid;
  484. } IDITEM; // IDREGITEM
  485. typedef struct
  486. {
  487. IDITEM idri;
  488. USHORT cbNext;
  489. } IDLITEM; // IDLREGITEM
  490. #ifndef NOPRAGMAS
  491. #pragma pack()
  492. #endif
  493. // stolen from shell32\shitemid.h
  494. #define SHID_ROOT_REGITEM 0x1f // MyDocuments, Internet, etc
  495. const IDLITEM c_idlInetRoot =
  496. {
  497. {SIZEOF(IDITEM), SHID_ROOT_REGITEM, 0,
  498. { 0x871C5380, 0x42A0, 0x1069, 0xA2,0xEA,0x08,0x00,0x2B,0x30,0x30,0x9D },/* CLSID_Internet */ }, 0,
  499. };
  500. LPCITEMIDLIST c_pidlURLRoot = (LPCITEMIDLIST)&c_idlInetRoot;
  501. // it must be an absolute pidl with a root regitem id at the front
  502. // if we're a rooted explorer, this is always false
  503. // this means we're definitely in nashville, so we shouldn't have a split
  504. // world
  505. PCURLID _FindUrlChild(LPCITEMIDLIST pidl, BOOL fIncludeHome = FALSE)
  506. {
  507. if ((pidl == NULL) ||
  508. (pidl->mkid.cb != sizeof(IDITEM)) ||
  509. (pidl->mkid.abID[0] != SHID_ROOT_REGITEM))
  510. {
  511. return NULL;
  512. }
  513. //
  514. // the clsid in the pidl must be our internet folder's
  515. //
  516. if (!IsEqualGUID(((IDITEM*)pidl)->clsid, CLSID_Internet))
  517. {
  518. ASSERT(!IsEqualGUID(((IDITEM*)pidl)->clsid, CLSID_CURLFolder));
  519. return NULL;
  520. }
  521. // go to the child...
  522. pidl = _ILNext(pidl);
  523. //
  524. // if it is a pidl to the internet root then it is the IE3 Home Page
  525. //
  526. if (fIncludeHome && ILIsEmpty(pidl))
  527. return (PCURLID)pidl;
  528. //
  529. // otherwise it is our child if it is a site object
  530. //
  531. return _IsValidUrlID(pidl);
  532. }
  533. STDAPI_(BOOL) IsURLChild(LPCITEMIDLIST pidl, BOOL fIncludeHome)
  534. {
  535. return (NULL != _FindUrlChild(pidl, fIncludeHome));
  536. }
  537. BOOL IEILGetFragment(LPCITEMIDLIST pidl, LPWSTR pszFragment, DWORD cchFragment)
  538. {
  539. return ILGetHiddenStringW(pidl, IDLHID_URLFRAGMENT, pszFragment, cchFragment);
  540. }
  541. UINT IEILGetCP(LPCITEMIDLIST pidl)
  542. {
  543. PCURLID purlid = _FindUrlChild((pidl));
  544. if (purlid)
  545. {
  546. if (!_IsValidDelegateID((LPCITEMIDLIST)purlid))
  547. return purlid->uiCP;
  548. }
  549. return CP_ACP;
  550. }
  551. LPITEMIDLIST _UrlIdCreate(UINT uiCP, LPCTSTR pszUrl)
  552. {
  553. //
  554. // the URLID has a variable sized string
  555. // member. but we put the arbitrary limit
  556. // of MAX_URL_STRING because that is what
  557. // we use everywhere else. we could just remove the
  558. // limit however.
  559. //
  560. USHORT cb = (USHORT)SIZEOF(URLID) - (USHORT)CbFromCch(1);
  561. USHORT cchUrl = lstrlen(pszUrl) + 1;
  562. cchUrl = (USHORT)min(cchUrl, MAX_URL_STRING);
  563. cb += CbFromCch(cchUrl);
  564. PURLID purlid = (PURLID)IEILCreate(cb + SIZEOF(USHORT));
  565. if (purlid)
  566. {
  567. // everything is actually aligned right now...
  568. purlid->idl.mkid.cb = cb;
  569. purlid->idl.mkid.abID[0] = SHID_INTERNET_SITE;
  570. purlid->bType = URLID_URLBASE;
  571. purlid->uiCP = uiCP;
  572. ualstrcpyn(purlid->achUrl, pszUrl, cchUrl);
  573. }
  574. return (LPITEMIDLIST) purlid;
  575. }
  576. LPITEMIDLIST UrlToPidl(UINT uiCP, LPCTSTR pszUrl, LPCWSTR pszFragment)
  577. {
  578. LPITEMIDLIST pidlRet;
  579. LPCTSTR pszAttachedFrag = UrlGetLocation(pszUrl);
  580. TCHAR szURLBuf[MAX_URL_STRING];
  581. // deal with URL's that still include the location (as in ParseDisplayName)
  582. if (pszAttachedFrag)
  583. {
  584. StrCpyN(szURLBuf, pszUrl, (int)(pszAttachedFrag-pszUrl+1));
  585. pszUrl = szURLBuf;
  586. // prefer the passed in fragment to the attached one
  587. if (!pszFragment)
  588. pszFragment = pszAttachedFrag;
  589. }
  590. ASSERT(pszUrl);
  591. pidlRet = _UrlIdCreate(uiCP, pszUrl);
  592. if (pidlRet && pszFragment && *pszFragment)
  593. pidlRet = IEILAppendFragment(pidlRet, pszFragment);
  594. return pidlRet;
  595. }
  596. typedef struct
  597. {
  598. LPCSTR pszProtocol;
  599. const CLSID * pCLSID;
  600. } FAULTIN_URLHANDERS;
  601. // TODO: If there are other URL Handlers, add them here.
  602. const FAULTIN_URLHANDERS c_FaultInUrlHandlers[] =
  603. {
  604. {"ftp", &CLSID_FTPShellExtension}
  605. };
  606. HRESULT CInternetFolder::_FaultInUrlHandler(LPCSTR pszProtocol, LPCTSTR pszUrl, IUnknown * punkSite)
  607. {
  608. HRESULT hr = S_OK;
  609. if (pszProtocol)
  610. {
  611. for (int nIndex = 0; nIndex < ARRAYSIZE(c_FaultInUrlHandlers); nIndex++)
  612. {
  613. if (!StrCmpIA(pszProtocol, c_FaultInUrlHandlers[nIndex].pszProtocol))
  614. {
  615. // Only fault in the feature if we are navigating to an FTP directory.
  616. if ((0 == nIndex) && !UrlIs(pszUrl, URLIS_DIRECTORY))
  617. {
  618. // It's not an ftp directory, so skip it.
  619. continue;
  620. }
  621. // FTP has a URL Shell Extension handler that is optionally
  622. // installed. Fault it in now if it's needed.
  623. uCLSSPEC ucs;
  624. QUERYCONTEXT qc = { 0 };
  625. HWND hwnd = NULL;
  626. ucs.tyspec = TYSPEC_CLSID;
  627. ucs.tagged_union.clsid = *c_FaultInUrlHandlers[nIndex].pCLSID;
  628. IUnknown_GetWindow(punkSite, &hwnd);
  629. if (EVAL(hwnd))
  630. {
  631. // Make it modal while the dialog is being displayed.
  632. IUnknown_EnableModless(punkSite, FALSE);
  633. FaultInIEFeature(hwnd, &ucs, &qc, 0);
  634. IUnknown_EnableModless(punkSite, TRUE);
  635. }
  636. break; // pidl can only have 1 procotol, so we don't need to check the other protocol.
  637. }
  638. }
  639. }
  640. return hr; // We don't care if it didn't make it.
  641. }
  642. HRESULT CInternetFolder::_ConditionallyFaultInUrlHandler(LPCSTR pszProtocol, LPCTSTR pszUrl, IBindCtx * pbc)
  643. {
  644. HRESULT hr = S_OK;
  645. // Faulting in the feature will probably require UI, so we need to assure that the caller
  646. // will allow this.
  647. if (pbc)
  648. {
  649. IUnknown * punkSite = NULL;
  650. pbc->GetObjectParam(STR_DISPLAY_UI_DURING_BINDING, &punkSite);
  651. if (punkSite)
  652. {
  653. hr = _FaultInUrlHandler(pszProtocol, pszUrl, punkSite);
  654. punkSite->Release();
  655. }
  656. }
  657. ASSERT(SUCCEEDED(hr));
  658. return S_OK; // We don't care if it didn't make it.
  659. }
  660. // returns:
  661. // success S_OK
  662. // failure FAILED(hres)
  663. HRESULT CInternetFolder::_CreateProtocolHandler(LPCSTR pszProtocol, IBindCtx * pbc, IShellFolder **ppsf)
  664. {
  665. HRESULT hres;
  666. CHAR szCLSID[GUIDSTR_MAX];
  667. DWORD cbSize = SIZEOF(szCLSID);
  668. *ppsf = NULL;
  669. if (pszProtocol &&
  670. SHGetValueA(HKEY_CLASSES_ROOT, pszProtocol, "ShellFolder", NULL, &szCLSID, &cbSize) == ERROR_SUCCESS)
  671. {
  672. CLSID clsid;
  673. IShellFolder *psf;
  674. GUIDFromStringA(szCLSID, &clsid);
  675. if (!SHSkipJunction(pbc, &clsid))
  676. {
  677. hres = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellFolder, &psf));
  678. if (SUCCEEDED(hres))
  679. {
  680. // IPersistFolder is optional
  681. IPersistFolder *ppf;
  682. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf))))
  683. {
  684. ppf->Initialize(_pidlRoot);
  685. ppf->Release();
  686. }
  687. IDelegateFolder *pdf;
  688. hres = psf->QueryInterface(IID_PPV_ARG(IDelegateFolder, &pdf));
  689. if (SUCCEEDED(hres))
  690. {
  691. // REVIEW: we could cache the malloc on a per protocol basis
  692. // to avoid creating these over and over
  693. IMalloc *pmalloc;
  694. hres = CDelegateMalloc_Create((void*)pszProtocol, (lstrlenA(pszProtocol) + 1), PDID_SIG, &pmalloc);
  695. if (SUCCEEDED(hres))
  696. {
  697. hres = pdf->SetItemAlloc(pmalloc);
  698. pmalloc->Release();
  699. }
  700. pdf->Release();
  701. }
  702. if (SUCCEEDED(hres))
  703. {
  704. hres = S_OK; // force all success codes to S_OK
  705. *ppsf = psf;
  706. }
  707. else
  708. psf->Release();
  709. }
  710. }
  711. else
  712. hres = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  713. }
  714. else
  715. hres = E_FAIL;
  716. return hres;
  717. }
  718. // returns:
  719. // S_FALSE if it is not a delegate protocol PIDL
  720. // hres of the bind opteration to the delegate protocol handler
  721. HRESULT CInternetFolder::_CreateProtocolHandlerFromPidl(LPCITEMIDLIST pidl, IBindCtx * pbc, IShellFolder **ppsf)
  722. {
  723. LPCSTR pszProtocol = _PidlToDelegateProtocol(pidl);
  724. if (pszProtocol)
  725. {
  726. HRESULT hres = _CreateProtocolHandler(pszProtocol, pbc, ppsf);
  727. ASSERT(hres != S_FALSE); // enforce the return value comment
  728. return hres;
  729. }
  730. *ppsf = NULL;
  731. return S_FALSE; // not a protocal PIDL
  732. }
  733. BOOL _GetUrlProtocol(LPCTSTR pszUrl, LPSTR pszProtocol, DWORD cchProtocol)
  734. {
  735. TCHAR sz[MAX_PATH];
  736. DWORD cch = SIZECHARS(sz);
  737. if (SUCCEEDED(UrlGetPart(pszUrl, sz, &cch, URL_PART_SCHEME, 0)))
  738. {
  739. SHTCharToAnsi(sz, pszProtocol, cchProtocol);
  740. return TRUE;
  741. }
  742. return FALSE;
  743. }
  744. UINT CodePageFromBindCtx(LPBC pbc)
  745. {
  746. UINT uiCP = CP_ACP;
  747. IDwnCodePage *pDwnCP;
  748. if (pbc && SUCCEEDED(pbc->QueryInterface(IID_PPV_ARG(IDwnCodePage, &pDwnCP))))
  749. {
  750. uiCP = pDwnCP->GetCodePage();
  751. pDwnCP->Release();
  752. }
  753. return uiCP;
  754. }
  755. #define STR_PARSE_INTERNET_DONT_ESCAPE_SPACES L"Parse Internet Dont Escape Spaces"
  756. HRESULT CInternetFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pwszDisplayName,
  757. ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes)
  758. {
  759. HRESULT hres = E_FAIL;
  760. TCHAR szName[MAX_URL_STRING];
  761. UINT uiCP = CodePageFromBindCtx(pbc);
  762. StrCpyN(szName, pwszDisplayName, ARRAYSIZE(szName));
  763. if (!PathIsFilePath(szName))
  764. {
  765. if (_ValidateURL(szName, 0) || ShouldShellExecURL(szName))
  766. {
  767. CHAR szProtocol[MAX_PATH];
  768. DWORD cchName = ARRAYSIZE(szName);
  769. IShellFolder *psfHandler;
  770. BOOL fProtocolExists;
  771. // if we're down here, then the szName was really a url so try to encode it.
  772. // turn spaces to %20, unless we are being called from shellexec
  773. // in which case we allow spaces in the URL
  774. if (!BindCtx_ContainsObject(pbc, STR_PARSE_INTERNET_DONT_ESCAPE_SPACES))
  775. UrlEscape(szName, szName, &cchName, URL_ESCAPE_SPACES_ONLY);
  776. fProtocolExists = _GetUrlProtocol(szName, szProtocol, ARRAYSIZE(szProtocol));
  777. _ConditionallyFaultInUrlHandler(szProtocol, szName, pbc);
  778. if (fProtocolExists &&
  779. _CreateProtocolHandler(szProtocol, pbc, &psfHandler) == S_OK)
  780. {
  781. TraceMsg(TF_PIDLWRAP, "Asking \"%s\" handler to parse %s (%08X) into a pidl", szProtocol, szName, szName);
  782. hres = psfHandler->ParseDisplayName(hwnd, pbc,
  783. pwszDisplayName, pchEaten,
  784. ppidl, pdwAttributes);
  785. TraceMsg(TF_PIDLWRAP, "the result is %08X, the pidl is %08X", hres, *ppidl);
  786. psfHandler->Release();
  787. TraceMsg(TF_URLNAMESPACE, "CODF::PDN(%s) called psfHandler and returning %x",
  788. szName, hres);
  789. }
  790. else
  791. {
  792. *ppidl = UrlToPidl(uiCP, szName, NULL);
  793. if (*ppidl)
  794. {
  795. if (pdwAttributes)
  796. hres = _GetAttributesOfProtocol(NULL, (LPCITEMIDLIST *)ppidl, 1, pdwAttributes);
  797. else
  798. hres = S_OK;
  799. }
  800. else
  801. hres = E_OUTOFMEMORY;
  802. TraceMsg(TF_URLNAMESPACE, "CODF::PDN(%s) called UrlToPidl and returning %x", szName, hres);
  803. }
  804. }
  805. else
  806. {
  807. TraceMsg(DM_CDOFPDN, "CDOF::PDN(%s) returning E_FAIL because of (%s) is FALSE", szName, TEXT("(_ValidateURL(szName) || ShouldShellExecURL( szName ))"));
  808. }
  809. }
  810. return hres;
  811. }
  812. class CInternetFolderDummyEnum : public IEnumIDList
  813. {
  814. public:
  815. CInternetFolderDummyEnum();
  816. // *** IUnknown methods ***
  817. STDMETHODIMP QueryInterface(REFIID,void **);
  818. STDMETHODIMP_(ULONG) AddRef(void);
  819. STDMETHODIMP_(ULONG) Release(void);
  820. // *** IEnumIDList methods ***
  821. STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
  822. STDMETHODIMP Skip(ULONG celt) {return E_NOTIMPL;}
  823. STDMETHODIMP Reset(void){return E_NOTIMPL;}
  824. STDMETHODIMP Clone(LPENUMIDLIST *ppenum){return E_NOTIMPL;}
  825. protected:
  826. ~CInternetFolderDummyEnum() {;}
  827. long _cRef;
  828. };
  829. CInternetFolderDummyEnum::CInternetFolderDummyEnum() : _cRef(1)
  830. {
  831. }
  832. HRESULT CInternetFolderDummyEnum::QueryInterface(REFIID riid, void **ppvObj)
  833. {
  834. static const QITAB qit[] = {
  835. QITABENT(CInternetFolderDummyEnum, IEnumIDList),
  836. { 0 },
  837. };
  838. return QISearch(this, qit, riid, ppvObj);
  839. }
  840. ULONG CInternetFolderDummyEnum::AddRef()
  841. {
  842. return InterlockedIncrement(&_cRef);
  843. }
  844. ULONG CInternetFolderDummyEnum::Release()
  845. {
  846. if (InterlockedDecrement(&_cRef))
  847. return _cRef;
  848. delete this;
  849. return 0;
  850. }
  851. HRESULT CInternetFolderDummyEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  852. {
  853. pceltFetched = 0;
  854. return S_FALSE;
  855. }
  856. HRESULT CInternetFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList)
  857. {
  858. CInternetFolderDummyEnum *pdummy = new CInternetFolderDummyEnum();
  859. if (pdummy)
  860. {
  861. *ppenumIDList = (IEnumIDList *)pdummy;
  862. return S_OK;
  863. }
  864. return E_OUTOFMEMORY;
  865. }
  866. HRESULT CInternetFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut)
  867. {
  868. IShellFolder *psfHandler = NULL;
  869. BOOL fUseDefault = TRUE;
  870. *ppvOut = NULL;
  871. if (!_IsValidUrlID(pidl))
  872. return E_INVALIDARG;
  873. HRESULT hres = _CreateProtocolHandlerFromPidl(pidl, pbc, &psfHandler);
  874. if (hres == S_OK)
  875. {
  876. // NOTE: We allow Shell Extensions to take over URL handling on a per
  877. // URL basis. We entered the _CreateProtocolHandlerFromPidl()
  878. // block of code above because
  879. // a shell extension is registered to take over handling this
  880. // URL. The above call to IShellFolder::BindToObject() just failed,
  881. // so we need to fall back and handle it in the traditional way.
  882. // This can be used by Shell Extensions, like the Ftp ShellExt, to
  883. // let the browser (us) handle URLs that are either inaccessible because of
  884. // the proxy or allow the browser to handle it so the traditional code
  885. // will: 1) download the item(s), 2) sniff the data for the type, 3)
  886. // use the suggested MIME type from the server or in the web page, 4)
  887. // check the file for type extension mappings, 5)
  888. // check any downloaded file for security certificates, and 6) display
  889. // Open/Save dialogs.
  890. hres = psfHandler->BindToObject(pidl, pbc, riid, ppvOut);
  891. // the handler will return ERROR_CANCELLED if it wants default behavior
  892. if (HRESULT_FROM_WIN32(ERROR_CANCELLED) != hres)
  893. fUseDefault = FALSE;
  894. }
  895. if (fUseDefault)
  896. {
  897. STRRET strRet;
  898. if (psfHandler)
  899. {
  900. // we had a delegated folder that failed, need a normal pidl
  901. hres = psfHandler->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strRet);
  902. }
  903. else
  904. hres = GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strRet);
  905. TCHAR szUrl[MAX_URL_STRING];
  906. if (SUCCEEDED(hres) &&
  907. SUCCEEDED(hres = StrRetToBuf(&strRet, pidl, szUrl, ARRAYSIZE(szUrl))))
  908. {
  909. if (IsEqualIID(IID_IMoniker, riid))
  910. {
  911. hres = MonikerFromURL(szUrl, (IMoniker **)ppvOut);
  912. }
  913. else // DEFAULT
  914. {
  915. // create a ShellFolder for the caller
  916. hres = E_OUTOFMEMORY;
  917. LPITEMIDLIST pidlT = NULL;
  918. // if we are using a handler but it returned cancelled,
  919. // then we need to recreate the pidl for ourselves to use
  920. // otherwise we just use the one that was passed in,
  921. // which we assume was the one we created.
  922. if (psfHandler)
  923. {
  924. pidlT = UrlToPidl(CP_ACP, szUrl, NULL);
  925. pidl = pidlT;
  926. }
  927. if (pidl)
  928. {
  929. LPITEMIDLIST pidlFull = ILCombine(_pidlRoot, pidl);
  930. if (pidlFull)
  931. {
  932. CDocObjectFolder *psf = new CDocObjectFolder(pidlFull);
  933. if (psf)
  934. {
  935. hres = psf->QueryInterface(riid, ppvOut);
  936. psf->Release();
  937. }
  938. ILFree(pidlFull);
  939. }
  940. ILFree(pidlT);
  941. }
  942. }
  943. }
  944. }
  945. if (psfHandler)
  946. psfHandler->Release();
  947. return hres;
  948. }
  949. HRESULT CInternetFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj)
  950. {
  951. IShellFolder *psfHandler;
  952. *ppvObj = NULL;
  953. if (!_IsValidUrlID(pidl))
  954. return E_INVALIDARG;
  955. HRESULT hres = _CreateProtocolHandlerFromPidl(pidl, pbc, &psfHandler);
  956. if (hres != S_FALSE)
  957. {
  958. if (SUCCEEDED(hres))
  959. {
  960. hres = psfHandler->BindToStorage(pidl, pbc, riid, ppvObj);
  961. psfHandler->Release();
  962. }
  963. return hres;
  964. }
  965. *ppvObj = NULL;
  966. return E_NOINTERFACE;
  967. }
  968. int CALLBACK CompareDelegateProtocols(void *pv1, void *pv2, LPARAM lParam)
  969. {
  970. LPCSTR psz1 = _PidlToDelegateProtocol((LPCITEMIDLIST)pv1);
  971. LPCSTR psz2 = _PidlToDelegateProtocol((LPCITEMIDLIST)pv2);
  972. if (psz1 && psz2)
  973. {
  974. int iRet = StrCmpA(psz1, psz2);
  975. if (0 == iRet && lParam)
  976. *((LPCSTR *)lParam) = psz1;
  977. }
  978. else if (psz1)
  979. {
  980. return 1;
  981. }
  982. else if (psz2)
  983. {
  984. return -1;
  985. }
  986. return 0;
  987. }
  988. HRESULT CInternetFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  989. {
  990. int iRet;
  991. ASSERT(!ILIsEmpty(pidl1) && !ILIsEmpty(pidl2));
  992. // Check for protocol pidls.
  993. LPCSTR psz = NULL;
  994. iRet = CompareDelegateProtocols((void *)pidl1, (void *)pidl2, (LPARAM)&psz);
  995. if (iRet)
  996. return ResultFromShort(iRet);
  997. if (psz)
  998. {
  999. IShellFolder *psfHandler;
  1000. if (_CreateProtocolHandler(psz, NULL, &psfHandler) == S_OK)
  1001. {
  1002. iRet = psfHandler->CompareIDs(lParam, pidl1, pidl2);
  1003. psfHandler->Release();
  1004. return ResultFromShort(iRet);
  1005. }
  1006. }
  1007. // we only have one layer of children
  1008. ASSERT(ILIsEmpty(_ILNext(pidl1)));
  1009. ASSERT(ILIsEmpty(_ILNext(pidl2)));
  1010. PCURLID purlid1 = _IsValidUrlID(pidl1);
  1011. if (purlid1)
  1012. {
  1013. PCURLID purlid2 = _IsValidUrlID(pidl2);
  1014. if (purlid2)
  1015. {
  1016. iRet = _CompareURL(purlid1, purlid2);
  1017. }
  1018. else
  1019. {
  1020. iRet = -1;
  1021. }
  1022. }
  1023. else
  1024. {
  1025. iRet = 1;
  1026. }
  1027. return ResultFromShort(iRet);
  1028. }
  1029. HRESULT CInternetFolder::_GetAttributesOfProtocol(LPCSTR pszProtocol,
  1030. LPCITEMIDLIST *apidl,
  1031. UINT cpidl, ULONG *rgfInOut)
  1032. {
  1033. HRESULT hres;
  1034. ASSERT(cpidl);
  1035. if (pszProtocol)
  1036. {
  1037. //
  1038. // We have a protocol. Find the protocol handler
  1039. // and pass it the bundle of pidls.
  1040. //
  1041. IShellFolder *psfHandler;
  1042. hres = _CreateProtocolHandler(pszProtocol, NULL, &psfHandler);
  1043. if (hres == S_OK)
  1044. {
  1045. hres = psfHandler->GetAttributesOf(cpidl, apidl, rgfInOut);
  1046. psfHandler->Release();
  1047. }
  1048. }
  1049. else if (_IsValidUrlID(apidl[0]))
  1050. {
  1051. ULONG uOut = SFGAO_CANLINK | SFGAO_BROWSABLE | SFGAO_STREAM;
  1052. *rgfInOut &= uOut;
  1053. hres = S_OK;
  1054. }
  1055. else
  1056. hres = E_INVALIDARG;
  1057. return hres;
  1058. }
  1059. HRESULT CInternetFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut)
  1060. {
  1061. if (*rgfInOut)
  1062. {
  1063. //
  1064. // Internet folder case.
  1065. //
  1066. LPCSTR pszProtocol;
  1067. if (cidl == 0)
  1068. {
  1069. //
  1070. // They are asking about the Internet Folder itself.
  1071. //
  1072. *rgfInOut &= SFGAO_FOLDER | SFGAO_CANLINK | SFGAO_STREAM;
  1073. }
  1074. else if (cidl == 1)
  1075. {
  1076. //
  1077. // Often we are asked about only one child,
  1078. // so we optimize that case.
  1079. //
  1080. pszProtocol = _PidlToDelegateProtocol(apidl[0]);
  1081. _GetAttributesOfProtocol(pszProtocol, apidl, cidl, rgfInOut);
  1082. }
  1083. else
  1084. {
  1085. //
  1086. // They are asking about multiple internet children.
  1087. // These children may have different protocols,
  1088. // so we have to find the GetAttributesOf handler for
  1089. // each group of protocols in the list.
  1090. //
  1091. LPCITEMIDLIST pidlBase;
  1092. UINT i, cpidlGroup;
  1093. // Create a list of pidls sorted by protocol.
  1094. HDPA hdpa = DPA_Create(100);
  1095. if (!hdpa)
  1096. return E_OUTOFMEMORY;
  1097. for (i = 0; i < cidl; i++)
  1098. {
  1099. DPA_AppendPtr(hdpa, (void *)apidl[i]);
  1100. }
  1101. DPA_Sort(hdpa, CompareDelegateProtocols, NULL);
  1102. //
  1103. // Call GetAttributesOf on each protocol group.
  1104. // A group
  1105. // starts at pidlBase
  1106. // contains cpidlGroup pidls
  1107. // has a protocol of pszProtocol
  1108. //
  1109. pidlBase = (LPCITEMIDLIST)DPA_FastGetPtr(hdpa, 0);
  1110. pszProtocol = NULL;
  1111. cpidlGroup = 0;
  1112. for (i = 0; *rgfInOut && (i < cidl); i++)
  1113. {
  1114. LPCITEMIDLIST pidlNew = (LPCITEMIDLIST)DPA_FastGetPtr(hdpa, i);
  1115. LPCSTR pszProtocolNew = _PidlToDelegateProtocol(pidlNew);
  1116. if (pszProtocolNew)
  1117. {
  1118. // See if we have a new protocol.
  1119. if (!pszProtocol || StrCmpA(pszProtocol, pszProtocolNew))
  1120. {
  1121. // We have a new protocol, time to process
  1122. // the last batch pidls.
  1123. _GetAttributesOfProtocol(pszProtocol, &pidlBase, cpidlGroup, rgfInOut);
  1124. pidlBase = pidlNew;
  1125. pszProtocol = pszProtocolNew;
  1126. cpidlGroup = 0;
  1127. }
  1128. }
  1129. cpidlGroup++;
  1130. }
  1131. if (*rgfInOut)
  1132. {
  1133. ASSERT(cpidlGroup);
  1134. _GetAttributesOfProtocol(pszProtocol, &pidlBase, cpidlGroup, rgfInOut);
  1135. }
  1136. DPA_Destroy(hdpa);
  1137. hdpa = NULL;
  1138. }
  1139. }
  1140. return S_OK;
  1141. }
  1142. BOOL GetCommonProtocol(LPCITEMIDLIST *apidl, UINT cpidl, LPCSTR *ppszProtocol)
  1143. {
  1144. UINT ipidl;
  1145. LPCSTR pszProtocol;
  1146. LPCSTR pszProtocolNext;
  1147. *ppszProtocol = NULL;
  1148. if (cpidl == 0)
  1149. {
  1150. return TRUE; // No pidls - no protocols, but they do all match!
  1151. }
  1152. //
  1153. // Grab the protocol of the first pidl, and use it to compare
  1154. // against the rest of the pidls.
  1155. //
  1156. pszProtocol = _PidlToDelegateProtocol(apidl[0]);
  1157. for (ipidl=1; ipidl<cpidl; ipidl++)
  1158. {
  1159. pszProtocolNext = _PidlToDelegateProtocol(apidl[ipidl]);
  1160. //
  1161. // Check if the protocols are different.
  1162. //
  1163. if ((pszProtocol != pszProtocolNext) &&
  1164. ((pszProtocol == NULL) ||
  1165. (pszProtocolNext == NULL) ||
  1166. (StrCmpA(pszProtocol, pszProtocolNext) != 0)))
  1167. {
  1168. return FALSE;
  1169. }
  1170. }
  1171. *ppszProtocol = pszProtocol;
  1172. return TRUE;
  1173. }
  1174. HRESULT _CombineHidden(LPCITEMIDLIST pidl, DWORD dwIEFlags, LPWSTR pszName, DWORD cchName)
  1175. {
  1176. HRESULT hres = S_OK;
  1177. //
  1178. // need to correctly append the fragment and query to the base
  1179. // if pszName is a DOSPATH, it will be converted to a
  1180. // file: URL so that it can accomadate the location
  1181. //
  1182. WCHAR sz[MAX_URL_STRING];
  1183. DWORD cch = cchName;
  1184. if (ILGetHiddenStringW(pidl, IDLHID_URLQUERY, sz, SIZECHARS(sz)))
  1185. hres = UrlCombineW(pszName, sz, pszName, &cch, 0);
  1186. if (!(dwIEFlags & IEGDN_NOFRAGMENT) && IEILGetFragment(pidl, sz, SIZECHARS(sz)))
  1187. {
  1188. hres = UrlCombineW(pszName, sz, pszName, &cchName, 0);
  1189. }
  1190. // else
  1191. // BUBBUG - should we return just the fragment in some case?
  1192. return hres;
  1193. }
  1194. HRESULT CInternetFolder::_GetUIObjectFromShortcut(LPCITEMIDLIST pidl, REFIID riid, void **ppvOut)
  1195. {
  1196. HRESULT hres = E_NOINTERFACE;
  1197. STRRET str;
  1198. TCHAR sz[MAX_URL_STRING];
  1199. if (SUCCEEDED(GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str))
  1200. && SUCCEEDED(StrRetToBuf(&str, pidl, sz, ARRAYSIZE(sz)))
  1201. && SUCCEEDED(_CombineHidden(pidl, 0, sz, ARRAYSIZE(sz))))
  1202. {
  1203. IUniformResourceLocator *purl;
  1204. hres = CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER,
  1205. IID_PPV_ARG(IUniformResourceLocator, &purl));
  1206. if (SUCCEEDED(hres))
  1207. {
  1208. hres = purl->SetURL(sz, 0);
  1209. if (SUCCEEDED(hres))
  1210. {
  1211. IShellLink * psl;
  1212. if (SUCCEEDED(purl->QueryInterface(IID_PPV_ARG(IShellLink, &psl))))
  1213. {
  1214. if (SUCCEEDED(GetDisplayNameOf(pidl, SHGDN_INFOLDER, &str)) &&
  1215. SUCCEEDED(StrRetToBuf(&str, pidl, sz, ARRAYSIZE(sz))))
  1216. {
  1217. PathRenameExtension(sz, TEXT(".url"));
  1218. psl->SetDescription(sz);
  1219. }
  1220. psl->Release();
  1221. }
  1222. hres = purl->QueryInterface(riid, ppvOut);
  1223. }
  1224. purl->Release();
  1225. }
  1226. }
  1227. return hres;
  1228. }
  1229. HRESULT CInternetFolder::_GetScheme(LPCITEMIDLIST pidl, LPWSTR pszOut, DWORD cchOut)
  1230. {
  1231. STRRET str;
  1232. LPCSTR pszProtocol = _PidlToDelegateProtocol(pidl);
  1233. if (pszProtocol)
  1234. {
  1235. SHAnsiToUnicode(pszProtocol, pszOut, cchOut);
  1236. return S_OK;
  1237. }
  1238. else if (SUCCEEDED(GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str)))
  1239. {
  1240. WCHAR sz[MAX_URL_STRING];
  1241. if (SUCCEEDED(StrRetToBufW(&str, pidl, sz, ARRAYSIZE(sz))))
  1242. {
  1243. return UrlGetPartW(sz, pszOut, &cchOut, URL_PART_SCHEME, 0);
  1244. }
  1245. }
  1246. return E_FAIL;
  1247. }
  1248. HRESULT CInternetFolder::_AssocCreate(LPCITEMIDLIST pidl, REFIID riid, void * *ppv)
  1249. {
  1250. *ppv = NULL;
  1251. IQueryAssociations *pqa;
  1252. HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
  1253. if (SUCCEEDED(hr))
  1254. {
  1255. WCHAR szScheme[MAX_PATH];
  1256. _GetScheme(pidl, szScheme, SIZECHARS(szScheme));
  1257. hr = pqa->Init(0, szScheme, NULL, NULL);
  1258. if (SUCCEEDED(hr))
  1259. hr = pqa->QueryInterface(riid, ppv);
  1260. pqa->Release();
  1261. }
  1262. return hr;
  1263. }
  1264. HRESULT CInternetFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
  1265. REFIID riid, UINT *prgfInOut, void **ppvOut)
  1266. {
  1267. HRESULT hres = E_NOINTERFACE;
  1268. LPCSTR pszProtocol;
  1269. *ppvOut = NULL;
  1270. if (apidl[0] && GetCommonProtocol(apidl, cidl, &pszProtocol) && pszProtocol)
  1271. {
  1272. IShellFolder *psfHandler;
  1273. hres = _CreateProtocolHandlerFromPidl(apidl[0], NULL, &psfHandler);
  1274. if (hres != S_FALSE)
  1275. {
  1276. if (SUCCEEDED(hres))
  1277. {
  1278. hres = psfHandler->GetUIObjectOf(hwnd, 1, apidl, riid, prgfInOut, ppvOut);
  1279. psfHandler->Release();
  1280. }
  1281. return hres;
  1282. }
  1283. }
  1284. else if (IsEqualIID(riid, IID_IExtractIconA)
  1285. || IsEqualIID(riid, IID_IExtractIconW)
  1286. || IsEqualIID(riid, IID_IContextMenu)
  1287. || IsEqualIID(riid, IID_IQueryInfo)
  1288. || IsEqualIID(riid, IID_IDataObject))
  1289. {
  1290. // WARNING - we only support this for one at a time.
  1291. if (cidl == 1)
  1292. {
  1293. hres = _GetUIObjectFromShortcut(apidl[0], riid, ppvOut);
  1294. }
  1295. }
  1296. else if (IsEqualIID(riid, IID_IQueryAssociations))
  1297. {
  1298. // WARNING - we only support this for one at a time.
  1299. if (cidl == 1)
  1300. {
  1301. hres = _AssocCreate(apidl[0], riid, ppvOut);
  1302. }
  1303. }
  1304. return hres;
  1305. }
  1306. HRESULT CInternetFolder::_InitHistoryStg(IUrlHistoryStg **pphist)
  1307. {
  1308. HRESULT hr;
  1309. if (!_phist)
  1310. {
  1311. hr = CoCreateInstance(CLSID_CUrlHistory, NULL, CLSCTX_INPROC_SERVER,
  1312. IID_PPV_ARG(IUrlHistoryStg, &_phist));
  1313. }
  1314. if (_phist)
  1315. {
  1316. *pphist = _phist;
  1317. _phist->AddRef();
  1318. return S_OK;
  1319. }
  1320. return hr;
  1321. }
  1322. HRESULT CInternetFolder::_GetTitle(LPCWSTR pszUrl, STRRET *pstr)
  1323. {
  1324. ASSERT(pszUrl);
  1325. IUrlHistoryStg *phist;
  1326. HRESULT hr = _InitHistoryStg(&phist);
  1327. if (SUCCEEDED(hr))
  1328. {
  1329. ASSERT(phist);
  1330. STATURL stat = {0};
  1331. hr = phist->QueryUrl(pszUrl, STATURL_QUERYFLAG_NOURL, &stat);
  1332. if (SUCCEEDED(hr) && stat.pwcsTitle)
  1333. {
  1334. hr = StringToStrRet(stat.pwcsTitle, pstr);
  1335. CoTaskMemFree(stat.pwcsTitle);
  1336. }
  1337. else
  1338. hr = E_FAIL;
  1339. phist->Release();
  1340. }
  1341. return hr;
  1342. }
  1343. HRESULT CInternetFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pstr)
  1344. {
  1345. IShellFolder *psfHandler;
  1346. HRESULT hr = _CreateProtocolHandlerFromPidl(pidl, NULL, &psfHandler);
  1347. if (hr != S_FALSE)
  1348. {
  1349. if (SUCCEEDED(hr))
  1350. {
  1351. hr = psfHandler->GetDisplayNameOf(pidl, uFlags, pstr);
  1352. psfHandler->Release();
  1353. }
  1354. return hr;
  1355. }
  1356. // FEATURE ZEKEL - should i handle more SHGDN flags here?? - Zekel - 24-NOV-98
  1357. PCURLID purlid = _IsValidUrlID(pidl);
  1358. if (purlid)
  1359. {
  1360. WCHAR sz[MAX_URL_STRING];
  1361. _ExtractURL(purlid, sz, SIZECHARS(sz));
  1362. if (SHGDN_NORMAL != uFlags)
  1363. hr = StringToStrRet(sz, pstr);
  1364. else
  1365. {
  1366. hr = _GetTitle(sz, pstr);
  1367. // fallback to the URL if necessary
  1368. if (FAILED(hr))
  1369. hr = StringToStrRet(sz, pstr);
  1370. }
  1371. }
  1372. else
  1373. hr = E_INVALIDARG;
  1374. return hr;
  1375. }
  1376. HRESULT CInternetFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl,
  1377. LPCOLESTR pszName, DWORD uFlags,
  1378. LPITEMIDLIST * ppidlOut)
  1379. {
  1380. IShellFolder *psfHandler;
  1381. HRESULT hres = _CreateProtocolHandlerFromPidl(pidl, NULL, &psfHandler);
  1382. if (hres != S_FALSE)
  1383. {
  1384. if (SUCCEEDED(hres))
  1385. {
  1386. hres = psfHandler->SetNameOf(hwnd, pidl, pszName, uFlags, ppidlOut);
  1387. psfHandler->Release();
  1388. }
  1389. return hres;
  1390. }
  1391. return E_FAIL;
  1392. }
  1393. HRESULT CInternetFolder::GetClassID(CLSID *pClassID)
  1394. {
  1395. *pClassID = CLSID_Internet;
  1396. return S_OK;
  1397. }
  1398. // IBrowserFrameOptions
  1399. HRESULT CInternetFolder::GetFrameOptions(IN BROWSERFRAMEOPTIONS dwMask, OUT BROWSERFRAMEOPTIONS * pdwOptions)
  1400. {
  1401. // The only case I know of that we hit this code is when you select "Internet Explorer" in the
  1402. // Folder Browser Band.
  1403. HRESULT hr = E_INVALIDARG;
  1404. if (pdwOptions)
  1405. {
  1406. // CInternetFolder should only be used for the "Internet Explorer" pidl that
  1407. // points to the Start Page, so find the start page and substitute it during
  1408. // navigation.
  1409. *pdwOptions |= dwMask & (BFO_SUBSTITUE_INTERNET_START_PAGE | BASE_OPTIONS);
  1410. hr = S_OK;
  1411. }
  1412. return hr;
  1413. }
  1414. #ifdef DEBUG
  1415. extern void remove_from_memlist(void *pv);
  1416. #endif
  1417. STDAPI CInternetFolder_CreateInstance(IUnknown* pUnkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
  1418. {
  1419. CInternetFolder *psf = new CInternetFolder;
  1420. if (psf)
  1421. {
  1422. //
  1423. // HACK:
  1424. //
  1425. // SHELL32 caches c_sfInetRoot in a static DATA section
  1426. // and never release it. It caches an instance of CInternetFolder
  1427. // and never release it. Therefore, we are removing this object
  1428. // from the to-be-memleak-detected list to avoid a false alarm
  1429. // assuming that we don't realy leak this object.
  1430. // Please don't copy it to another place unless you are really
  1431. // sure that it's OK not to detect leaks in that scenario.
  1432. // (SatoNa)
  1433. //
  1434. HRESULT hr = psf->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
  1435. psf->Release();
  1436. return hr;
  1437. }
  1438. return E_OUTOFMEMORY;
  1439. }
  1440. STDAPI MonikerFromURL(LPCWSTR wszPath, IMoniker** ppmk)
  1441. {
  1442. HRESULT hres = CreateURLMoniker(NULL, wszPath, ppmk);
  1443. #ifndef UNIX
  1444. if (FAILED(hres))
  1445. #else
  1446. // BUG BUG :
  1447. // IEUNIX : We use to crash on UNIX if we give a very long invalid url
  1448. // in address bar or as home page in inetcpl. We used to crash inside
  1449. // MkParseDisplayName.
  1450. if (FAILED(hres) && lstrlenW(wszPath) < MAX_PATH)
  1451. #endif
  1452. {
  1453. IBindCtx* pbc;
  1454. hres = CreateBindCtx(0, &pbc);
  1455. if (SUCCEEDED(hres))
  1456. {
  1457. // Fall back to a system (file) moniker
  1458. ULONG cchEaten = 0;
  1459. hres = MkParseDisplayName(pbc, wszPath, &cchEaten, ppmk);
  1460. pbc->Release();
  1461. }
  1462. }
  1463. return hres;
  1464. }
  1465. STDAPI MonikerFromString(LPCTSTR szPath, IMoniker** ppmk)
  1466. {
  1467. return MonikerFromURL(szPath, ppmk);
  1468. }
  1469. #if 0
  1470. // alternate way to do the below in a more "pure" way on integrated mode
  1471. HRESULT InitPSFInternet()
  1472. {
  1473. if (g_psfInternet)
  1474. return S_OK;
  1475. IShellFolder *psfTemp;
  1476. IShellFolder *psfDesktop;
  1477. HRESULT hres = SHGetDesktopFolder(&psfDesktop);
  1478. if (SUCCEEDED(hres))
  1479. {
  1480. ULONG cchEaten;
  1481. LPITEMIDLIST pidl;
  1482. WCHAR wszInternet[] = L"::{871C5380-42A0-1069-A2EA-08002B30309D}"; // CLSID_Internet
  1483. hres = psfDesktop->ParseDisplayName(NULL, NULL, wszInternet, &cchEaten, &pidl, NULL);
  1484. if (SUCCEEDED(hres))
  1485. {
  1486. hres = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfTemp));
  1487. SHFree(pidl);
  1488. }
  1489. psfDesktop->Release();
  1490. }
  1491. if (SHInterlockedCompareExchange((void **)&g_psfInternet, psfTemp, 0))
  1492. psfTemp->Release(); // race to the exchange, free dup copy
  1493. return hres;
  1494. }
  1495. #endif
  1496. HRESULT InitPSFInternet()
  1497. {
  1498. if (g_psfInternet)
  1499. return S_OK;
  1500. IShellFolder *psfTemp;
  1501. HRESULT hres = CoCreateInstance(CLSID_CURLFolder, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellFolder, &psfTemp));
  1502. if (SUCCEEDED(hres))
  1503. {
  1504. IPersistFolder* ppsf;
  1505. hres = psfTemp->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppsf));
  1506. if (SUCCEEDED(hres))
  1507. {
  1508. hres = ppsf->Initialize(c_pidlURLRoot);
  1509. if (SUCCEEDED(hres))
  1510. {
  1511. if (SHInterlockedCompareExchange((void **)&g_psfInternet, psfTemp, 0) == 0)
  1512. psfTemp->AddRef(); // global now holds ref
  1513. }
  1514. ppsf->Release();
  1515. }
  1516. psfTemp->Release();
  1517. }
  1518. return hres;
  1519. }
  1520. HRESULT _GetInternetRoot(IShellFolder **ppsfRoot)
  1521. {
  1522. HRESULT hr = InitPSFInternet();
  1523. *ppsfRoot = NULL;
  1524. if (SUCCEEDED(hr))
  1525. {
  1526. g_psfInternet->AddRef();
  1527. *ppsfRoot = g_psfInternet;
  1528. }
  1529. return hr;
  1530. }
  1531. HRESULT _GetRoot(LPCITEMIDLIST pidl, BOOL fIsUrl, IShellFolder **ppsfRoot)
  1532. {
  1533. HRESULT hr;
  1534. *ppsfRoot = NULL;
  1535. if (fIsUrl)
  1536. {
  1537. ASSERT(IsURLChild(pidl, TRUE));
  1538. TraceMsg(TF_URLNAMESPACE, "IEBTO(%x) using the Internet", pidl);
  1539. hr = _GetInternetRoot(ppsfRoot);
  1540. }
  1541. else
  1542. {
  1543. ASSERT(ILIsRooted(pidl));
  1544. TraceMsg(TF_URLNAMESPACE, "IEBTO(%x) using Rooted", pidl);
  1545. CLSID clsid;
  1546. ILRootedGetClsid(pidl, &clsid);
  1547. if (IsEqualGUID(clsid, CLSID_ShellDesktop))
  1548. {
  1549. hr = SHBindToObject(NULL, IID_IShellFolder, ILRootedFindIDList(pidl), (void **)ppsfRoot);
  1550. }
  1551. else
  1552. {
  1553. IShellFolder *psf;
  1554. hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IShellFolder, &psf));
  1555. if (SUCCEEDED(hr))
  1556. {
  1557. LPCITEMIDLIST pidlRoot = ILRootedFindIDList(pidl);
  1558. if (!pidlRoot)
  1559. pidlRoot = &s_idlNULL;
  1560. IPersistFolder* ppf;
  1561. hr = psf->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf));
  1562. if (SUCCEEDED(hr))
  1563. {
  1564. hr = ppf->Initialize(pidlRoot);
  1565. ppf->Release();
  1566. }
  1567. // hand over the reference
  1568. *ppsfRoot = psf;
  1569. }
  1570. }
  1571. }
  1572. return hr;
  1573. }
  1574. STDAPI_(BOOL) IEILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, BOOL fIgnoreHidden)
  1575. {
  1576. UINT cb = ILGetSize(pidl1);
  1577. if (cb != ILGetSize(pidl2) || 0 != memcmp(pidl1, pidl2, cb))
  1578. {
  1579. // THEY are binarily different
  1580. BOOL fRet = FALSE;
  1581. BOOL fWebOnly = FALSE;
  1582. if (IsURLChild(pidl1, TRUE) || IsURLChild(pidl2, TRUE))
  1583. fWebOnly = TRUE;
  1584. if ((IsURLChild(pidl1, FALSE) && IsURLChild(pidl2, FALSE))
  1585. || (ILIsRooted(pidl1) && ILIsEqualRoot(pidl1, pidl2)))
  1586. {
  1587. IShellFolder *psf;
  1588. if (SUCCEEDED(_GetRoot(pidl1, fWebOnly, &psf)))
  1589. {
  1590. if (0 == psf->CompareIDs(0, _ILNext(pidl1), _ILNext(pidl2)))
  1591. fRet = TRUE;
  1592. psf->Release();
  1593. }
  1594. }
  1595. if (!fRet && !fWebOnly)
  1596. {
  1597. #undef ILIsEqual
  1598. fRet = ILIsEqual(pidl1, pidl2);
  1599. }
  1600. if (fRet && !fIgnoreHidden)
  1601. {
  1602. fRet = (0 == ILCompareHiddenString(pidl1, pidl2, IDLHID_URLFRAGMENT));
  1603. if (fRet)
  1604. fRet = (0 == ILCompareHiddenString(pidl1, pidl2, IDLHID_URLQUERY));
  1605. if (fRet)
  1606. fRet = (0 == ILCompareHiddenString(pidl1, pidl2, IDLHID_NAVIGATEMARKER));
  1607. }
  1608. return fRet;
  1609. }
  1610. return TRUE;
  1611. }
  1612. // pszName must be MAX_URL_STRING
  1613. STDAPI IEGetDisplayName(LPCITEMIDLIST pidl, LPWSTR pszName, UINT uFlags)
  1614. {
  1615. return IEGetNameAndFlagsEx(pidl, uFlags, 0, pszName, MAX_URL_STRING, NULL);
  1616. }
  1617. #ifdef UNIX
  1618. //IEUNIX : No support for internal and external names in hp and solaris linker.
  1619. extern "C" STDAPI IEGetDisplayNameW(LPCITEMIDLIST pidl, LPWSTR pszName, UINT uFlags)
  1620. {
  1621. return IEGetDisplayName(pidl, pszName, uFlags);
  1622. }
  1623. #endif
  1624. HRESULT _GetInternetFolderName(LPWSTR pszName, DWORD cchName)
  1625. {
  1626. LPCTSTR pszKey;
  1627. DWORD cbSize = CbFromCch(cchName);
  1628. if (4 > GetUIVersion())
  1629. pszKey = TEXT("CLSID\\{FBF23B42-E3F0-101B-8488-00AA003E56F8}");
  1630. else
  1631. pszKey = TEXT("CLSID\\{871C5380-42A0-1069-A2EA-08002B30309D}");
  1632. if (NOERROR == SHGetValue(HKEY_CLASSES_ROOT, pszKey, NULL, NULL, pszName, &cbSize)
  1633. && *pszName)
  1634. return S_OK;
  1635. if (MLLoadString(IDS_REG_THEINTERNET, pszName, cchName)
  1636. && *pszName)
  1637. return S_OK;
  1638. return E_UNEXPECTED;
  1639. }
  1640. STDAPI IEGetNameAndFlagsEx(LPCITEMIDLIST pidl, UINT uSHFlags, DWORD dwIEFlags, LPWSTR pszName, DWORD cchName, DWORD *prgfInOutAttrs)
  1641. {
  1642. HRESULT hres = E_FAIL;
  1643. if (pszName)
  1644. {
  1645. VDATEINPUTBUF(pszName, TCHAR, cchName);
  1646. *pszName = 0;
  1647. }
  1648. // for support of NON-integrated builds, and
  1649. // to expedite handling of URLs while browsing
  1650. if (IsURLChild(pidl, FALSE))
  1651. {
  1652. hres = InitPSFInternet();
  1653. if (SUCCEEDED(hres))
  1654. {
  1655. if (pszName)
  1656. {
  1657. STRRET str;
  1658. hres = g_psfInternet->GetDisplayNameOf(_ILNext(pidl), uSHFlags, &str);
  1659. if (SUCCEEDED(hres))
  1660. {
  1661. StrRetToBufW(&str, pidl, pszName, cchName);
  1662. }
  1663. }
  1664. if (prgfInOutAttrs)
  1665. hres = IEGetAttributesOf(pidl, prgfInOutAttrs);
  1666. }
  1667. }
  1668. else if (GetUIVersion() <= 4 && IsURLChild(pidl, TRUE))
  1669. {
  1670. //
  1671. // we need to support requests for the Internet SFs
  1672. // Friendly name. on NT5 we will always have something
  1673. // even when the SF is hidden. but on older versions
  1674. // of the shell, it was possible to delete the folder
  1675. // by just removing the icon from the desktop
  1676. //
  1677. if (pszName)
  1678. hres = _GetInternetFolderName(pszName, cchName);
  1679. if (prgfInOutAttrs)
  1680. hres = IEGetAttributesOf(pidl, prgfInOutAttrs);
  1681. }
  1682. else if (ILIsRooted(pidl))
  1683. {
  1684. IShellFolder *psf;
  1685. LPCITEMIDLIST pidlChild;
  1686. hres = IEBindToParentFolder(pidl, &psf, &pidlChild);
  1687. if (SUCCEEDED(hres))
  1688. {
  1689. if (pszName)
  1690. {
  1691. STRRET str;
  1692. hres = IShellFolder_GetDisplayNameOf(psf, pidlChild, uSHFlags, &str, 0);
  1693. if (SUCCEEDED(hres))
  1694. {
  1695. hres = StrRetToBufW(&str, pidlChild, pszName, cchName);
  1696. }
  1697. }
  1698. if (prgfInOutAttrs)
  1699. hres = psf->GetAttributesOf(ILIsEmpty(pidlChild) ? 0 : 1, &pidlChild, prgfInOutAttrs);
  1700. psf->Release();
  1701. }
  1702. }
  1703. else
  1704. hres = SHGetNameAndFlags(pidl, uSHFlags, pszName, cchName, prgfInOutAttrs);
  1705. if (SUCCEEDED(hres) && pszName && (uSHFlags & SHGDN_FORPARSING))
  1706. {
  1707. hres = _CombineHidden(pidl, dwIEFlags, pszName, cchName);
  1708. }
  1709. TraceMsg(TF_URLNAMESPACE, "IEGDN(%s) returning %x", pszName, hres);
  1710. return hres;
  1711. }
  1712. STDAPI IEGetNameAndFlags(LPCITEMIDLIST pidl, UINT uFlags, LPWSTR pszName, DWORD cchName, DWORD *prgfInOutAttrs)
  1713. {
  1714. return IEGetNameAndFlagsEx(pidl, uFlags, 0, pszName, cchName, prgfInOutAttrs);
  1715. }
  1716. BOOL _ClassIsBrowsable(LPCTSTR pszClass)
  1717. {
  1718. BOOL fRet = FALSE;
  1719. HKEY hk;
  1720. if (SUCCEEDED(AssocQueryKey(0, ASSOCKEY_CLASS, pszClass, NULL, &hk)))
  1721. {
  1722. fRet = (NOERROR == RegQueryValueEx(hk, TEXT("DocObject"), NULL, NULL, NULL, NULL)
  1723. || NOERROR == RegQueryValueEx(hk, TEXT("BrowseInPlace"), NULL, NULL, NULL, NULL));
  1724. RegCloseKey(hk);
  1725. }
  1726. return fRet;
  1727. }
  1728. BOOL _MimeIsBrowsable(LPCTSTR pszExt)
  1729. {
  1730. BOOL fRet = FALSE;
  1731. TCHAR sz[MAX_PATH];
  1732. DWORD dwSize = ARRAYSIZE(sz);
  1733. if (SUCCEEDED(AssocQueryString(0, ASSOCSTR_CONTENTTYPE, pszExt, NULL, sz, &dwSize)))
  1734. {
  1735. TCHAR szKey[MAX_PATH];
  1736. dwSize = SIZEOF(sz);
  1737. // Get the CLSID for the handler of this content type.
  1738. wnsprintf(szKey, ARRAYSIZE(szKey), TEXT("MIME\\Database\\Content Type\\%s"), sz);
  1739. // reuse sz for the clsid
  1740. if (NOERROR == SHGetValue(HKEY_CLASSES_ROOT, szKey, TEXT("CLSID"), NULL, (void *) sz, &dwSize))
  1741. {
  1742. fRet = _ClassIsBrowsable(sz);
  1743. }
  1744. }
  1745. return fRet;
  1746. }
  1747. BOOL _StorageIsBrowsable(LPCTSTR pszPath)
  1748. {
  1749. BOOL fRet = FALSE;
  1750. //
  1751. // If the file is STILL not browsable, try to open it as a structured storage
  1752. // and check its CLSID.
  1753. //
  1754. IStorage *pStg = NULL;
  1755. if (StgOpenStorage(pszPath, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &pStg ) == S_OK && pStg)
  1756. {
  1757. STATSTG statstg;
  1758. if (pStg->Stat( &statstg, STATFLAG_NONAME ) == S_OK)
  1759. {
  1760. TCHAR szClsid[GUIDSTR_MAX];
  1761. SHStringFromGUIDW(statstg.clsid, szClsid, SIZECHARS(szClsid));
  1762. fRet = _ClassIsBrowsable(szClsid);
  1763. }
  1764. pStg->Release();
  1765. }
  1766. return fRet;
  1767. }
  1768. BOOL _IEIsBrowsable(LPCITEMIDLIST pidl)
  1769. {
  1770. TCHAR szPath[MAX_PATH];
  1771. BOOL fRet = FALSE;
  1772. if (SUCCEEDED(SHGetPathFromIDList(pidl, szPath)))
  1773. {
  1774. // Don't change the order of the following OR'd conditions because
  1775. // we want the HTML test to go first. Also, the NT5 shell will
  1776. // do the ClassIsBrowsable check for us, so we should avoid repeating
  1777. // that check.
  1778. if (PathIsHTMLFile(szPath)
  1779. || _ClassIsBrowsable(szPath)
  1780. || _MimeIsBrowsable(PathFindExtension(szPath))
  1781. || _StorageIsBrowsable(szPath))
  1782. fRet = TRUE;
  1783. }
  1784. return fRet;
  1785. }
  1786. HRESULT _IEGetAttributesOf(LPCITEMIDLIST pidl, DWORD* pdwAttribs, BOOL fAllowExtraChecks)
  1787. {
  1788. HRESULT hres = E_FAIL;
  1789. DWORD dwAttribs = *pdwAttribs;
  1790. BOOL fExtraCheckForBrowsable = FALSE;
  1791. //
  1792. // REARCHITECT - Check if we need to execute an additional logic - ZekeL - 7-JAN-99
  1793. // to see if it's browsable or not. this is necessary on shell32s from NT4/win95/IE4
  1794. // both NT4/win95 have no notion of SFGAO_BROWSABLE, and even though
  1795. // IE4 does, it doesnt handle it correctly for UNICODE file names.
  1796. // We are just as thorough (more) in our private check, so just defer to it.
  1797. //
  1798. // 78777: Even if we are on NT5, IE can browse things that Shell thinks is not
  1799. // browsable, for example, .htm files when Netscape is the default browser.
  1800. // So we should do the extra check on every platform.
  1801. if (fAllowExtraChecks && (dwAttribs & SFGAO_BROWSABLE))
  1802. {
  1803. dwAttribs |= SFGAO_FILESYSTEM | SFGAO_FOLDER;
  1804. fExtraCheckForBrowsable = TRUE;
  1805. }
  1806. IShellFolder* psfParent;
  1807. LPCITEMIDLIST pidlChild;
  1808. if (ILIsEmpty(pidl))
  1809. {
  1810. hres = SHGetDesktopFolder(&psfParent);
  1811. pidlChild = pidl;
  1812. }
  1813. else if (ILIsRooted(pidl) && ILIsEmpty(_ILNext(pidl)))
  1814. {
  1815. //
  1816. // when getting the attributes of the root itself, we
  1817. // decide its better to just limit the attribs to
  1818. // some easily supported subset. we used to always
  1819. // fail, but that is a little extreme.
  1820. //
  1821. // we could also try to get the attributes from HKCR\CLSID\{clsid}\shellfolder\attributes
  1822. //
  1823. *pdwAttribs &= (SFGAO_FOLDER);
  1824. return S_OK;
  1825. }
  1826. else
  1827. {
  1828. if (GetUIVersion() < 4 && IsURLChild(pidl, TRUE))
  1829. {
  1830. IShellFolder *psfRoot;
  1831. //
  1832. // if we are Browser Only, and this is the
  1833. // internet folder itself that we are interested
  1834. // in, then we need to bind to it by hand
  1835. // and query it with cidl = 0
  1836. //
  1837. hres = _GetInternetRoot(&psfRoot);
  1838. if (SUCCEEDED(hres))
  1839. {
  1840. hres = SHBindToFolderIDListParent(psfRoot, _ILNext(pidl), IID_PPV_ARG(IShellFolder, &psfParent), &pidlChild);
  1841. psfRoot->Release();
  1842. }
  1843. }
  1844. else if (ILIsRooted(pidl))
  1845. hres = IEBindToParentFolder(pidl, &psfParent, &pidlChild);
  1846. else
  1847. hres = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psfParent), &pidlChild);
  1848. }
  1849. if (SUCCEEDED(hres))
  1850. {
  1851. ASSERT(psfParent);
  1852. hres = psfParent->GetAttributesOf(ILIsEmpty(pidlChild) ? 0 : 1, &pidlChild, &dwAttribs);
  1853. if (FAILED(hres))
  1854. TraceMsg(TF_WARNING, "IEGetAttribs psfParent->GetAttr failed %x", hres);
  1855. psfParent->Release();
  1856. }
  1857. else
  1858. TraceMsg(TF_WARNING, "IEGetAttribs BindTOParent failed %x", hres);
  1859. //
  1860. // This is the extra logic we need to execute if this is a browser
  1861. // only mode to get the right "browsable" attribute flag to DocObjects.
  1862. //
  1863. if (fExtraCheckForBrowsable && !(dwAttribs & SFGAO_BROWSABLE))
  1864. {
  1865. if ((dwAttribs & (SFGAO_FILESYSTEM | SFGAO_FOLDER)) == SFGAO_FILESYSTEM)
  1866. {
  1867. if (_IEIsBrowsable(pidl))
  1868. dwAttribs |= SFGAO_BROWSABLE;
  1869. }
  1870. }
  1871. *pdwAttribs &= dwAttribs;
  1872. return hres;
  1873. }
  1874. HRESULT IEGetAttributesOf(LPCITEMIDLIST pidl, DWORD* pdwAttribs)
  1875. {
  1876. return _IEGetAttributesOf(pidl, pdwAttribs, TRUE);
  1877. }
  1878. // BRYANST: 7/22/97 - NT Bug #188099
  1879. // shell32.dll in IE4 SI and only in that version had a bug if pbc was passed
  1880. // to IShellFolder::BindToObject() (fstreex.c!FSBindToFSFolder), it would fail
  1881. // to bind to Shell Extensions that extended file system folders, such as:
  1882. // the history folder, the occache, etc. We work around this by passing a NULL pbc
  1883. // if the destination is an IE4 shell32.dll and it will go thru FSBindToFSFolder().
  1884. BOOL ShouldWorkAroundBCBug(LPCITEMIDLIST pidl)
  1885. {
  1886. BOOL fWillBCCauseBug = FALSE;
  1887. if (4 == GetUIVersion())
  1888. {
  1889. LPITEMIDLIST pidlCopy = ILClone(pidl);
  1890. LPITEMIDLIST pidlIterate = pidlCopy;
  1891. // Skip the first two ItemIDs. (#1 could be My Computer)
  1892. if (!ILIsEmpty(pidlIterate))
  1893. {
  1894. IShellFolder * psf;
  1895. // (#2 could be CFSFolder::BindToObject())
  1896. pidlIterate = _ILNext(pidlIterate);
  1897. if (!ILIsEmpty(pidlIterate))
  1898. {
  1899. pidlIterate = _ILNext(pidlIterate);
  1900. // Remove everything else so we bind directly to CFSFolder::BindToObject()
  1901. pidlIterate->mkid.cb = 0;
  1902. if (SUCCEEDED(IEBindToObject(pidlCopy, &psf)))
  1903. {
  1904. IPersist * pp;
  1905. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IPersist, &pp))))
  1906. {
  1907. CLSID clsid;
  1908. if (SUCCEEDED(pp->GetClassID(&clsid)) &&
  1909. IsEqualCLSID(clsid, CLSID_ShellFSFolder))
  1910. {
  1911. fWillBCCauseBug = TRUE;
  1912. }
  1913. pp->Release();
  1914. }
  1915. psf->Release();
  1916. }
  1917. }
  1918. }
  1919. ILFree(pidlCopy);
  1920. }
  1921. return fWillBCCauseBug;
  1922. }
  1923. typedef enum
  1924. {
  1925. SHOULDBIND_DOCOBJ,
  1926. SHOULDBIND_DESKTOP,
  1927. SHOULDBIND_NONE,
  1928. } SHOULDBIND;
  1929. //
  1930. // _ShouldDocObjBind()
  1931. // returns
  1932. // SHOULDBIND_DOCOBJ - Should just use DocObjectFolder directly
  1933. // SHOULDBIND_DESKTOP - bind through the desktop
  1934. // SHOULDBIND_NONE - FAIL the bind...
  1935. //
  1936. SHOULDBIND _ShouldDocObjBind(DWORD dwAttribs, BOOL fStrictBind)
  1937. {
  1938. if (fStrictBind)
  1939. {
  1940. if ((dwAttribs & (SFGAO_FOLDER | SFGAO_BROWSABLE | SFGAO_FILESYSTEM)) == (SFGAO_BROWSABLE | SFGAO_FILESYSTEM))
  1941. return SHOULDBIND_DOCOBJ;
  1942. else
  1943. return SHOULDBIND_DESKTOP;
  1944. }
  1945. else
  1946. {
  1947. if (dwAttribs & (SFGAO_FOLDER | SFGAO_BROWSABLE))
  1948. return SHOULDBIND_DESKTOP;
  1949. // manually bind using our CDocObjectFolder for
  1950. // files which are not DocObject. Without this code, file:
  1951. // to non-Docobject files (such as multi-media files)
  1952. // won't do anything.
  1953. //
  1954. // is is needed for non integraded browser mode
  1955. //
  1956. if (dwAttribs & SFGAO_FILESYSTEM)
  1957. return SHOULDBIND_DOCOBJ;
  1958. else
  1959. return SHOULDBIND_NONE;
  1960. }
  1961. }
  1962. STDAPI _IEBindToObjectInternal(BOOL fStrictBind, LPCITEMIDLIST pidl, IBindCtx * pbc, REFIID riid, void **ppvOut)
  1963. {
  1964. IShellFolder *psfTemp;
  1965. HRESULT hr;
  1966. *ppvOut = NULL;
  1967. // Special case: If we have the pidl for the "Desktop" then just use the Desktop folder itself
  1968. if (ILIsEmpty(pidl))
  1969. {
  1970. hr = SHGetDesktopFolder(&psfTemp);
  1971. if (SUCCEEDED(hr))
  1972. {
  1973. hr = psfTemp->QueryInterface(riid, ppvOut);
  1974. psfTemp->Release();
  1975. }
  1976. }
  1977. else
  1978. {
  1979. BOOL fIsUrlChild = IsURLChild(pidl, TRUE);
  1980. if (fIsUrlChild || ILIsRooted(pidl))
  1981. {
  1982. hr = _GetRoot(pidl, fIsUrlChild, &psfTemp);
  1983. if (SUCCEEDED(hr))
  1984. {
  1985. pidl = _ILNext(pidl);
  1986. if (!ILIsEmpty(pidl))
  1987. hr = psfTemp->BindToObject(pidl, pbc, riid, ppvOut);
  1988. else
  1989. hr = psfTemp->QueryInterface(riid, ppvOut);
  1990. psfTemp->Release();
  1991. }
  1992. }
  1993. else
  1994. {
  1995. // non integrated browser mode will succeed on
  1996. // BindToObject(IID_IShellFolder) even for things that should
  1997. // fail (files). to avoid the down stream problems caused by this we
  1998. // filter out things that are not "browseable" up front,
  1999. //
  2000. // NOTE: this does not work on simple PIDLs
  2001. DWORD dwAttribs = SFGAO_FOLDER | SFGAO_BROWSABLE | SFGAO_FILESYSTEM;
  2002. hr = _IEGetAttributesOf(pidl, &dwAttribs, fStrictBind);
  2003. if (SUCCEEDED(hr))
  2004. {
  2005. switch (_ShouldDocObjBind(dwAttribs, fStrictBind))
  2006. {
  2007. case SHOULDBIND_DOCOBJ:
  2008. {
  2009. //
  2010. // shortcircuit and bind using our CDocObjectFolder for
  2011. // files which are BROWSABLE. Without this code, file:
  2012. // to non-Docobject files (such as multi-media files)
  2013. // won't do anything.
  2014. //
  2015. // is is needed for non integraded browser mode
  2016. //
  2017. CDocObjectFolder *pdof = new CDocObjectFolder();
  2018. TraceMsg(TF_URLNAMESPACE, "IEBTO(%x) using DocObjectFolder", pidl);
  2019. if (pdof)
  2020. {
  2021. hr = pdof->Initialize(pidl);
  2022. if (SUCCEEDED(hr))
  2023. hr = pdof->QueryInterface(riid, ppvOut);
  2024. pdof->Release();
  2025. }
  2026. else
  2027. hr = E_OUTOFMEMORY;
  2028. }
  2029. break;
  2030. case SHOULDBIND_DESKTOP:
  2031. {
  2032. //
  2033. // This is the normal case. We just bind down through the desktop...
  2034. //
  2035. TraceMsg(TF_URLNAMESPACE, "IEBTO(%x) using Desktop", pidl);
  2036. hr = SHGetDesktopFolder(&psfTemp);
  2037. if (SUCCEEDED(hr))
  2038. {
  2039. // BRYANST: 7/22/97 - NT Bug #188099
  2040. // shell32.dll in IE4 SI and only in that version had a bug if pbc was passed
  2041. // to IShellFolder::BindToObject() (fstreex.c!FSBindToFSFolder), it would fail
  2042. // to bind to Shell Extensions that extended file system folders, such as:
  2043. // the history folder, the occache, etc. We work around this by passing a NULL pbc
  2044. // if the destination is an IE4 shell32.dll and it will go thru FSBindToFSFolder().
  2045. if (pbc && ShouldWorkAroundBCBug(pidl))
  2046. {
  2047. pbc = NULL;
  2048. }
  2049. hr = psfTemp->BindToObject(pidl, pbc, riid, ppvOut);
  2050. psfTemp->Release();
  2051. }
  2052. }
  2053. break;
  2054. default:
  2055. hr = E_FAIL;
  2056. }
  2057. }
  2058. }
  2059. }
  2060. if (SUCCEEDED(hr) && !*ppvOut)
  2061. {
  2062. // Some NSEs have bugs where they will fail to fill in the
  2063. // out pointer but return SUCCEEDED(hr). WS_FTP is one example
  2064. // in NT #413950.
  2065. TraceMsg(TF_URLNAMESPACE, "IEBTO() BUG!!! An NSE succeeded but returned a NULL interface pointer.");
  2066. hr = E_FAIL;
  2067. }
  2068. TraceMsg(TF_URLNAMESPACE, "IEBTO(%x) returning %x", pidl, hr);
  2069. return hr;
  2070. }
  2071. STDAPI IEBindToObjectEx(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void **ppvOut)
  2072. {
  2073. return _IEBindToObjectInternal(TRUE, pidl, pbc, riid, ppvOut);
  2074. }
  2075. STDAPI IEBindToObject(LPCITEMIDLIST pidl, IShellFolder **ppsfOut)
  2076. {
  2077. return _IEBindToObjectInternal(TRUE, pidl, NULL, IID_PPV_ARG(IShellFolder, ppsfOut));
  2078. }
  2079. STDAPI IEBindToObjectWithBC(LPCITEMIDLIST pidl, IBindCtx * pbc, IShellFolder **ppsfOut)
  2080. {
  2081. return _IEBindToObjectInternal(TRUE, pidl, pbc, IID_PPV_ARG(IShellFolder, ppsfOut));
  2082. }
  2083. // CLASSIC BIND here
  2084. HRESULT IEBindToObjectForNavigate(LPCITEMIDLIST pidl, IBindCtx * pbc, IShellFolder **ppsfOut)
  2085. {
  2086. return _IEBindToObjectInternal(FALSE, pidl, pbc, IID_PPV_ARG(IShellFolder, ppsfOut));
  2087. }
  2088. //
  2089. // CDwnCodePage: Dummy supports IBindCtx interface object only for casting
  2090. // It holds codepage info to pass via LPBC parameter
  2091. //
  2092. class CDwnCodePage : public IBindCtx
  2093. , public IDwnCodePage
  2094. {
  2095. public:
  2096. // IUnknown methods
  2097. STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
  2098. STDMETHODIMP_(ULONG) AddRef(void);
  2099. STDMETHODIMP_(ULONG) Release(void);
  2100. // IBindCtx methods
  2101. STDMETHODIMP RegisterObjectBound(IUnknown *punk) { return (_pbc ? _pbc->RegisterObjectBound(punk) : E_NOTIMPL); };
  2102. STDMETHODIMP RevokeObjectBound(IUnknown *punk) { return (_pbc ? _pbc->RevokeObjectBound(punk) : E_NOTIMPL); };
  2103. STDMETHODIMP ReleaseBoundObjects(void) { return (_pbc ? _pbc->ReleaseBoundObjects() : E_NOTIMPL); };
  2104. STDMETHODIMP SetBindOptions(BIND_OPTS *pbindopts) { return (_pbc ? _pbc->SetBindOptions(pbindopts) : E_NOTIMPL); };
  2105. STDMETHODIMP GetBindOptions(BIND_OPTS *pbindopts) { return (_pbc ? _pbc->GetBindOptions(pbindopts) : E_NOTIMPL); };
  2106. STDMETHODIMP GetRunningObjectTable(IRunningObjectTable **pprot) { *pprot = NULL; return (_pbc ? _pbc->GetRunningObjectTable(pprot) : E_NOTIMPL); };
  2107. STDMETHODIMP RegisterObjectParam(LPOLESTR pszKey, IUnknown *punk) { return (_pbc ? _pbc->RegisterObjectParam(pszKey, punk) : E_NOTIMPL); };
  2108. STDMETHODIMP GetObjectParam(LPOLESTR pszKey, IUnknown **ppunk) { *ppunk = NULL; return (_pbc ? _pbc->GetObjectParam(pszKey, ppunk) : E_NOTIMPL); };
  2109. STDMETHODIMP EnumObjectParam(IEnumString **ppenum) { *ppenum = NULL; return (_pbc ? _pbc->EnumObjectParam(ppenum) : E_NOTIMPL); };
  2110. STDMETHODIMP RevokeObjectParam(LPOLESTR pszKey) { return (_pbc ? _pbc->RevokeObjectParam(pszKey) : E_NOTIMPL); };
  2111. STDMETHODIMP RemoteSetBindOptions(BIND_OPTS2 *pbindopts) { return E_NOTIMPL; };
  2112. STDMETHODIMP RemoteGetBindOptions(BIND_OPTS2 *pbindopts) { return E_NOTIMPL; };
  2113. // IDwnCodePage methods
  2114. STDMETHODIMP_(UINT) GetCodePage(void) { return _uiCodePage; };
  2115. STDMETHODIMP SetCodePage(UINT uiCodePage) { _uiCodePage = uiCodePage; return S_OK; };
  2116. // Constructor
  2117. CDwnCodePage(IBindCtx * pbc, UINT uiCodePage) : _cRef(1) { _uiCodePage = uiCodePage; _pbc = NULL; IUnknown_Set((IUnknown **)&_pbc, (IUnknown *)pbc); };
  2118. ~CDwnCodePage() { ATOMICRELEASE(_pbc); };
  2119. private:
  2120. int _cRef;
  2121. UINT _uiCodePage;
  2122. IBindCtx * _pbc;
  2123. };
  2124. STDAPI CDwnCodePage::QueryInterface(REFIID riid, void **ppvObj)
  2125. {
  2126. static const QITAB qit[] = {
  2127. QITABENT(CDwnCodePage, IBindCtx),
  2128. QITABENT(CDwnCodePage, IDwnCodePage),
  2129. { 0 },
  2130. };
  2131. return QISearch(this, qit, riid, ppvObj);
  2132. }
  2133. STDAPI_(ULONG) CDwnCodePage::AddRef()
  2134. {
  2135. _cRef++;
  2136. return _cRef;
  2137. }
  2138. STDAPI_(ULONG) CDwnCodePage::Release()
  2139. {
  2140. _cRef--;
  2141. if (0 < _cRef)
  2142. return _cRef;
  2143. delete this;
  2144. return 0;
  2145. }
  2146. // IEParseDisplayName() will do all of the below functionality in IECreateFromPathCPWithBC()
  2147. // plus the following two things:
  2148. // 1. It will call ParseURLFromOutsideSource(), so this is more friendly to
  2149. // strings from outside sources.
  2150. // 2. If the URL has a fragment, this function will pass out a PIDL with the last
  2151. // ID being the location.
  2152. HRESULT IECreateFromPathCPWithBCW(UINT uiCP, LPCWSTR pszPath, IBindCtx * pbc, LPITEMIDLIST *ppidlOut)
  2153. {
  2154. TraceMsg(TF_URLNAMESPACE, "IECFP(%s) called", pszPath);
  2155. HRESULT hr = S_OK;
  2156. WCHAR szPath[MAX_URL_STRING];
  2157. WCHAR szBuf[MAX_PATH];
  2158. DWORD cchBuf = ARRAYSIZE(szBuf);
  2159. CDwnCodePage DwnCodePage(pbc, uiCP);
  2160. DWORD len;
  2161. // Initialize for failure case
  2162. *ppidlOut = NULL;
  2163. // if we are passed a NULL path, then there is no way we can convert it to a pidl.
  2164. // in some cases the reason we are passed a NULL path is because the IShellFolder
  2165. // provider was unable to generate a parseable display name (MSN Classic 1.3 is
  2166. // a very good example, they return E_NOTIMPL).
  2167. if ( ((len = lstrlen( pszPath )) == 0) || len >= MAX_URL_STRING )
  2168. {
  2169. return E_FAIL;
  2170. }
  2171. // Is this a "file:" URL?
  2172. if (IsFileUrlW(pszPath) && SUCCEEDED(hr = PathCreateFromUrl(pszPath, szBuf, &cchBuf, 0)))
  2173. pszPath = szBuf;
  2174. BOOL fIsFilePath = PathIsFilePath(pszPath);
  2175. #ifdef FEATURE_IE_USE_DESKTOP_PARSING
  2176. //
  2177. // in order to take advantage of whatever enhancements the desktop
  2178. // makes to parsing (eg, WebFolders and shell: URLs), then we allow
  2179. // the desktop first go at it. it will loop back into the internet
  2180. // shell folder if all the special cases fail.
  2181. // maybe use a reg setting to control???
  2182. //
  2183. //
  2184. if (fIsFilePath || GetUIVersion() >= 5)
  2185. #else // !FEATURE_IE_USE_DESKTOP_PARSING
  2186. //
  2187. // right now we just use the desktop if its a file path or
  2188. // it is a shell: URL on NT5
  2189. //
  2190. if (fIsFilePath || (GetUIVersion() >= 5 && URL_SCHEME_SHELL == GetUrlSchemeW(pszPath)))
  2191. #endif // FEATURE_IE_USE_DESKTOP_PARSING
  2192. {
  2193. ASSERT(SUCCEEDED(hr));
  2194. // Resolve any dot-dot path reference and remove trailing backslash
  2195. if (fIsFilePath)
  2196. {
  2197. PathCanonicalize(szPath, pszPath);
  2198. pszPath = szPath;
  2199. // This call will cause a network hit: one connection attempt to \\server\IPC$
  2200. // and then a series of FindFirst's - one for each directory.
  2201. if (StrChr(pszPath, L'*') || StrChr(pszPath, L'?'))
  2202. {
  2203. hr = E_FAIL;
  2204. }
  2205. }
  2206. if (SUCCEEDED(hr))
  2207. {
  2208. hr = SHILCreateFromPath(pszPath, ppidlOut, NULL);
  2209. TraceMsg(DM_CDOFPDN, "IECreateFromPath psDesktop->PDN(%s) returned %x", pszPath, hr);
  2210. }
  2211. }
  2212. else
  2213. {
  2214. //
  2215. // Need to put in buffer since ParseDisplayName doesn't take a 'const' string.
  2216. StrCpyN(szPath, pszPath, ARRAYSIZE(szPath));
  2217. pszPath = szPath;
  2218. // Avoid the network and disk hits above for non-file urls.
  2219. // This code path is taken on http: folks so a nice optimization. We will then drop
  2220. // down below where we check the internet namespace.
  2221. IShellFolder *psfRoot;
  2222. hr = _GetInternetRoot(&psfRoot);
  2223. if (SUCCEEDED(hr))
  2224. {
  2225. TraceMsg(TF_URLNAMESPACE, "IECFP(%s) calling g_psfInternet->PDN %x", pszPath, hr);
  2226. LPITEMIDLIST pidlRel;
  2227. hr = psfRoot->ParseDisplayName(NULL, (IBindCtx*)&DwnCodePage, szPath, NULL, &pidlRel, NULL);
  2228. TraceMsg(DM_CDOFPDN, "IECreateFromPath called psfInternet->PDN(%s) %x", pszPath, hr);
  2229. if (SUCCEEDED(hr))
  2230. {
  2231. *ppidlOut = ILCombine(c_pidlURLRoot, pidlRel);
  2232. if (!*ppidlOut)
  2233. hr = E_OUTOFMEMORY;
  2234. ILFree(pidlRel);
  2235. }
  2236. psfRoot->Release();
  2237. }
  2238. }
  2239. // NOTE: NT5 beta 3 and before had a call to SHSimpleIDListFromPath().
  2240. // This is very bad because it will parse any garbage and prevent
  2241. // the caller from finding invalid strings. I(BryanSt) needed
  2242. // this fixed for IEParseDisplayNameWithBCW() would fail on invalid
  2243. // address bar strings ("Search Get Rich Quick").
  2244. TraceMsg(TF_URLNAMESPACE, "IECFP(%s) returning %x (hr=%x)",
  2245. pszPath, *ppidlOut, hr);
  2246. return hr;
  2247. }
  2248. HRESULT IECreateFromPathCPWithBCA(UINT uiCP, LPCSTR pszPath, IBindCtx * pbc, LPITEMIDLIST *ppidlOut)
  2249. {
  2250. WCHAR szPath[MAX_URL_STRING];
  2251. ASSERT(lstrlenA(pszPath) < ARRAYSIZE(szPath));
  2252. SHAnsiToUnicodeCP(uiCP, pszPath, szPath, ARRAYSIZE(szPath));
  2253. return IECreateFromPathCPWithBCW(uiCP, szPath, pbc, ppidlOut);
  2254. }
  2255. HRESULT IEParseDisplayName(UINT uiCP, LPCTSTR pszPath, LPITEMIDLIST * ppidlOut)
  2256. {
  2257. return IEParseDisplayNameWithBCW(uiCP, pszPath, NULL, ppidlOut);
  2258. }
  2259. // This function will do two things that IECreateFromPathCPWithBC() will not do:
  2260. // 1. It will add the "Query" section of the URL into the pidl.
  2261. // 2. If the URL has a fragment, this function will pass out a PIDL with the last
  2262. // ID being the location.
  2263. // NOTE: If the caller needs the string to be "cleaned up" because the user manually
  2264. // entered the URL, the caller needs to call ParseURLFromOutsideSource() before
  2265. // calling this function. That function should only be called on strings entered
  2266. // by the user because of the perf hit and it could incorrectly format valid
  2267. // parsible display names. For example, ParseURLFromOutsideSource() will
  2268. // convert the string "My Computer" into a search URL for yahoo.com
  2269. // (http://www.yahoo.com/search.asp?p=My+p=Computer) when some callers
  2270. // want that string parsed by an IShellFolder in the desktop.
  2271. HRESULT IEParseDisplayNameWithBCW(UINT uiCP, LPCWSTR pwszPath, IBindCtx * pbc, LPITEMIDLIST * ppidlOut)
  2272. {
  2273. TCHAR szPath[MAX_URL_STRING];
  2274. LPCWSTR pwszFileLocation = NULL;
  2275. WCHAR szQuery[MAX_URL_STRING];
  2276. HRESULT hres;
  2277. szQuery[0] = TEXT('\0');
  2278. #ifdef DEBUG
  2279. if (IsFlagSet(g_dwDumpFlags, DF_URL))
  2280. {
  2281. TraceMsg(DM_TRACE, "IEParseDisplayName got %s", szPath);
  2282. }
  2283. #endif
  2284. // We want to remove QUERY and FRAGMENT sections of
  2285. // FILE URLs because they need to be added in "Hidden" pidls.
  2286. // Also, URLs need to be escaped all the time except for paths
  2287. // to facility parsing and because we already removed all other
  2288. // parts of the URL (Query and Fragment).
  2289. if (IsFileUrlW(pwszPath))
  2290. {
  2291. DWORD cchQuery = SIZECHARS(szQuery) - 1;
  2292. pwszFileLocation = UrlGetLocationW(pwszPath);
  2293. if (SUCCEEDED(UrlGetPart(pwszPath, szQuery+1, &cchQuery, URL_PART_QUERY, 0)) && cchQuery)
  2294. szQuery[0] = TEXT('?');
  2295. DWORD cchPath = ARRAYSIZE(szPath);
  2296. if (FAILED(PathCreateFromUrl(pwszPath, szPath, &cchPath, 0)))
  2297. {
  2298. // Failed to parse it back. Use the original.
  2299. StrCpyN(szPath, pwszPath, ARRAYSIZE(szPath));
  2300. }
  2301. }
  2302. else
  2303. {
  2304. // If we failed, just try to use the original
  2305. StrCpyN(szPath, pwszPath, ARRAYSIZE(szPath));
  2306. }
  2307. #ifdef DEBUG
  2308. if (IsFlagSet(g_dwDumpFlags, DF_URL))
  2309. TraceMsg(DM_TRACE, "IEParseDisplayName calling IECreateFromPath %s", szPath);
  2310. #endif
  2311. hres = IECreateFromPathCPWithBC(uiCP, szPath, pbc, ppidlOut);
  2312. if (SUCCEEDED(hres) && pwszFileLocation)
  2313. {
  2314. ASSERT(*ppidlOut);
  2315. *ppidlOut = IEILAppendFragment(*ppidlOut, pwszFileLocation);
  2316. hres = *ppidlOut ? S_OK : E_OUTOFMEMORY;
  2317. }
  2318. if (SUCCEEDED(hres) && szQuery[0] == TEXT('?'))
  2319. {
  2320. *ppidlOut = ILAppendHiddenString(*ppidlOut, IDLHID_URLQUERY, szQuery);
  2321. hres = *ppidlOut ? S_OK : E_OUTOFMEMORY;
  2322. }
  2323. return hres;
  2324. }