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.

581 lines
20 KiB

  1. #include "stdafx.h"
  2. #include "netplace.h"
  3. #include "msdasc.h"
  4. #pragma hdrstop
  5. CNetworkPlace::CNetworkPlace() :
  6. _pidl(NULL), _fSupportWebFolders(FALSE), _fIsWebFolder(FALSE), _fDeleteWebFolder(FALSE)
  7. {
  8. _szTarget[0] = TEXT('\0');
  9. _szName[0] = TEXT('\0');
  10. _szDescription[0] = TEXT('\0');
  11. }
  12. // destructor - clean up our state
  13. CNetworkPlace::~CNetworkPlace()
  14. {
  15. _InvalidateCache();
  16. }
  17. void CNetworkPlace::_InvalidateCache()
  18. {
  19. // web folders will create a shortcut to objects if we go through its binding
  20. // process, therefore when we attempt to invalidate our cache we should
  21. // clean up our mess.
  22. //
  23. // if the user has commited the change then we can/will keep the shortcut
  24. // around, otherwise we call the delete verb on it.
  25. if (_fIsWebFolder && _fDeleteWebFolder && _pidl)
  26. {
  27. IShellFolder *psf;
  28. LPCITEMIDLIST pidlLast;
  29. HRESULT hr = SHBindToIDListParent(_pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
  30. if (SUCCEEDED(hr))
  31. {
  32. IContextMenu *pcm;
  33. hr = psf->GetUIObjectOf(NULL, 1, &pidlLast, IID_X_PPV_ARG(IContextMenu, NULL, &pcm));
  34. if (SUCCEEDED(hr))
  35. {
  36. CMINVOKECOMMANDINFO ici = {0};
  37. ici.cbSize = sizeof (ici);
  38. ici.fMask = CMIC_MASK_FLAG_NO_UI;
  39. ici.lpVerb = "Delete";
  40. ici.nShow = SW_SHOWNORMAL;
  41. hr = pcm->InvokeCommand(&ici);
  42. pcm->Release();
  43. }
  44. psf->Release();
  45. }
  46. }
  47. // now clean up the rest of our state.
  48. ILFree(_pidl);
  49. _pidl = NULL;
  50. _szTarget[0] = TEXT('\0');
  51. _szName[0] = TEXT('\0');
  52. _szDescription[0] = TEXT('\0');
  53. _fIsWebFolder = FALSE;
  54. _fDeleteWebFolder = FALSE;
  55. }
  56. HRESULT CNetworkPlace::SetTarget(HWND hwnd, LPCWSTR pszTarget, DWORD dwFlags)
  57. {
  58. _InvalidateCache();
  59. HRESULT hr = S_OK;
  60. if (pszTarget)
  61. {
  62. // set our state accordingly
  63. _fSupportWebFolders = (dwFlags & NPTF_ALLOWWEBFOLDERS) != 0;
  64. // copy the URL and prepare for parsing
  65. StrCpyN(_szTarget, pszTarget, ARRAYSIZE(_szTarget));
  66. INT cchTarget = lstrlen(_szTarget)-1;
  67. if ((_szTarget[cchTarget] == L'\\') || (_szTarget[cchTarget] == '/'))
  68. {
  69. _szTarget[cchTarget] = TEXT('\0');
  70. }
  71. if (dwFlags & NPTF_VALIDATE)
  72. {
  73. // connecting to a server root or local path is not supported
  74. if (PathIsUNCServer(_szTarget) || PathGetDriveNumber(_szTarget) != -1)
  75. {
  76. hr = E_INVALIDARG;
  77. }
  78. else
  79. {
  80. // check the policy to see if we are setting this.
  81. if (PathIsUNC(_szTarget) && SHRestricted(REST_NONETCONNECTDISCONNECT))
  82. {
  83. hr = E_INVALIDARG;
  84. }
  85. else
  86. {
  87. hr = _IDListFromTarget(hwnd);
  88. }
  89. }
  90. if (FAILED(hr))
  91. {
  92. if (hwnd && !(dwFlags & NPTF_SILENT))
  93. {
  94. ::DisplayFormatMessage(hwnd,
  95. IDS_ANP_CAPTION,
  96. PathIsUNCServer(_szTarget) ? IDS_PUB_ONLYSERVER:IDS_CANTFINDFOLDER,
  97. MB_OK|MB_ICONERROR);
  98. }
  99. _InvalidateCache();
  100. }
  101. }
  102. }
  103. return hr;
  104. }
  105. HRESULT CNetworkPlace::SetName(HWND hwnd, LPCWSTR pszName)
  106. {
  107. HRESULT hr = S_OK;
  108. if (!_fIsWebFolder)
  109. {
  110. // check to see if we are going to overwrite an existing place, if we
  111. // are then display a prompt and let the user choose. if they answer
  112. // yes, then have at it!
  113. TCHAR szPath[MAX_PATH];
  114. if (hwnd && _IsPlaceTaken(pszName, szPath))
  115. {
  116. if (IDNO == ::DisplayFormatMessage(hwnd,
  117. IDS_ANP_CAPTION , IDS_FRIENDLYNAMEINUSE,
  118. MB_YESNO|MB_ICONQUESTION,
  119. pszName))
  120. {
  121. hr = E_FAIL;
  122. }
  123. }
  124. }
  125. // if we succeed the above then lets use the new name.
  126. if (SUCCEEDED(hr))
  127. StrCpyN(_szName, pszName, ARRAYSIZE(_szName));
  128. return hr;
  129. }
  130. HRESULT CNetworkPlace::SetDescription(LPCWSTR pszDescription)
  131. {
  132. StrCpyN(_szDescription, pszDescription, ARRAYSIZE(_szDescription));
  133. return S_OK;
  134. }
  135. // recompute the URL based on the new user/password information that
  136. // we were just given.
  137. HRESULT CNetworkPlace::SetLoginInfo(LPCWSTR pszUser, LPCWSTR pszPassword)
  138. {
  139. TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH + 1];
  140. TCHAR szUrlPath[INTERNET_MAX_PATH_LENGTH + 1];
  141. TCHAR szExtraInfo[MAX_PATH + 1]; // Includes Port Number and download type (ASCII, Binary, Detect)
  142. URL_COMPONENTS urlComps = {0};
  143. urlComps.dwStructSize = sizeof(urlComps);
  144. urlComps.lpszHostName = szServer;
  145. urlComps.dwHostNameLength = ARRAYSIZE(szServer);
  146. urlComps.lpszUrlPath = szUrlPath;
  147. urlComps.dwUrlPathLength = ARRAYSIZE(szUrlPath);
  148. urlComps.lpszExtraInfo = szExtraInfo;
  149. urlComps.dwExtraInfoLength = ARRAYSIZE(szExtraInfo);
  150. // WARNING - the ICU_DECODE/ICU_ESCAPE is a lossy roundtrip - ZekeL - 26-MAR-2001
  151. // many escaped characters are not correctly identified and re-escaped.
  152. // any characters that are reserved for URL parsing purposes
  153. // will be interpreted as their parsing char (ie '/').
  154. BOOL fResult = InternetCrackUrl(_szTarget, 0, 0, &urlComps);
  155. if (fResult)
  156. {
  157. urlComps.lpszUserName = (LPTSTR) pszUser;
  158. urlComps.dwUserNameLength = (pszUser ? lstrlen(pszUser) : 0);
  159. urlComps.lpszPassword = (LPTSTR) pszPassword;
  160. urlComps.dwPasswordLength = (pszPassword ? lstrlen(pszPassword) : 0);
  161. DWORD cchSize = ARRAYSIZE(_szTarget);
  162. fResult = InternetCreateUrl(&urlComps, (ICU_ESCAPE | ICU_USERNAME), _szTarget, &cchSize);
  163. // if we have a cached IDList then lets ensure that we clear it up
  164. // so that we rebind and the FTP namespace gets a crack at it.
  165. if (fResult && _pidl)
  166. {
  167. ILFree(_pidl);
  168. _pidl = NULL;
  169. }
  170. }
  171. return fResult ? S_OK : HRESULT_FROM_WIN32(GetLastError());
  172. }
  173. HRESULT CNetworkPlace::GetIDList(HWND hwnd, LPITEMIDLIST *ppidl)
  174. {
  175. HRESULT hr = _IDListFromTarget(hwnd);
  176. if (SUCCEEDED(hr))
  177. {
  178. hr = SHILClone(_pidl, ppidl);
  179. }
  180. return hr;
  181. }
  182. HRESULT CNetworkPlace::GetObject(HWND hwnd, REFIID riid, void **ppv)
  183. {
  184. HRESULT hr = _IDListFromTarget(hwnd);
  185. if (SUCCEEDED(hr))
  186. {
  187. hr = SHBindToObject(NULL, riid, _pidl, ppv);
  188. }
  189. return hr;
  190. }
  191. HRESULT CNetworkPlace::GetName(LPWSTR pszBuffer, int cchBuffer)
  192. {
  193. HRESULT hr = _IDListFromTarget(NULL);
  194. if (SUCCEEDED(hr))
  195. {
  196. StrCpyN(pszBuffer, _szName, cchBuffer);
  197. hr = S_OK;
  198. }
  199. return hr;
  200. }
  201. // check to see if we are going to overwrite a network place
  202. BOOL CNetworkPlace::_IsPlaceTaken(LPCTSTR pszName, LPTSTR pszPath)
  203. {
  204. BOOL fOverwriting = FALSE;
  205. SHGetSpecialFolderPath(NULL, pszPath, CSIDL_NETHOOD, TRUE);
  206. PathCombine(pszPath, pszPath, pszName);
  207. IShellFolder *psf;
  208. HRESULT hr = SHGetDesktopFolder(&psf);
  209. if (SUCCEEDED(hr))
  210. {
  211. LPITEMIDLIST pidl;
  212. if (SUCCEEDED(psf->ParseDisplayName(NULL, NULL, pszPath, NULL, &pidl, NULL)))
  213. {
  214. // we think we are going to overwrite an existing net place, so lets
  215. // check first to see if the place which is there is not actually
  216. // pointing at our new target. if its is then we can just
  217. // ignore all of this.
  218. TCHAR szTarget[INTERNET_MAX_URL_LENGTH];
  219. hr = _GetTargetPath(pidl, szTarget, ARRAYSIZE(szTarget));
  220. if (FAILED(hr) || (0 != StrCmpI(szTarget, _szTarget)))
  221. {
  222. fOverwriting = TRUE;
  223. }
  224. ILFree(pidl);
  225. }
  226. psf->Release();
  227. }
  228. return fOverwriting;
  229. }
  230. // handle creating the web folders IDLIST for this item. we check with the
  231. // rosebud binder to find out if this scheme is supported, if so then
  232. // we attempt to have the Web Folders code crack the URL
  233. static const BYTE c_pidlWebFolders[] =
  234. {
  235. 0x14,0x00,0x1F,0x0F,0xE0,0x4F,0xD0,0x20,
  236. 0xEA,0x3A,0x69,0x10,0xA2,0xD8,0x08,0x00,
  237. 0x2B,0x30,0x30,0x9D,0x14,0x00,0x2E,0x00,
  238. 0x00,0xDF,0xEA,0xBD,0x65,0xC2,0xD0,0x11,
  239. 0xBC,0xED,0x00,0xA0,0xC9,0x0A,0xB5,0x0F,
  240. 0x00,0x00
  241. };
  242. HRESULT CNetworkPlace::_TryWebFolders(HWND hwnd)
  243. {
  244. // lets see if Rosebud can handle this scheme item by checking the
  245. // scheme and seeing if the rosebud binder can handle it.
  246. TCHAR szScheme[INTERNET_MAX_SCHEME_LENGTH + 1];
  247. DWORD cchScheme = ARRAYSIZE(szScheme);
  248. HRESULT hr = UrlGetPart(_szTarget, szScheme, &cchScheme, URL_PART_SCHEME, 0);
  249. if (SUCCEEDED(hr))
  250. {
  251. IRegisterProvider *prp;
  252. hr = CoCreateInstance(CLSID_RootBinder, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IRegisterProvider, &prp));
  253. if (SUCCEEDED(hr))
  254. {
  255. // let the web folders code have a go at creating a link to this storage,
  256. // the IDLIST we generate points to the folder inside My Computer (hidden)
  257. CLSID clsidOut;
  258. hr = prp->GetURLMapping(szScheme, 0, &clsidOut);
  259. if (hr == S_OK)
  260. {
  261. IShellFolder *psf;
  262. hr = SHBindToObject(NULL, IID_IShellFolder, (LPCITEMIDLIST)c_pidlWebFolders, (void**)&psf);
  263. if (SUCCEEDED(hr))
  264. {
  265. IBindCtx *pbc;
  266. hr = CreateBindCtx(NULL, &pbc);
  267. if (SUCCEEDED(hr))
  268. {
  269. BIND_OPTS bo = {sizeof(bo), 0, STGM_CREATE};
  270. hr = pbc->SetBindOptions(&bo);
  271. if (SUCCEEDED(hr))
  272. {
  273. // we need to pase NULL hWnd to this so that Web Folders doesn't display any
  274. // UI, in particular its ever so useful NULL error message box... mumble mumble
  275. LPITEMIDLIST pidl;
  276. hr = psf->ParseDisplayName(NULL, pbc, _szTarget, NULL, &pidl, NULL);
  277. if (SUCCEEDED(hr))
  278. {
  279. ASSERT(!_pidl);
  280. hr = SHILCombine((LPCITEMIDLIST)c_pidlWebFolders, pidl, &_pidl);
  281. ILFree(pidl);
  282. _fDeleteWebFolder = TRUE; // we now have the magic web folders link (clean it up)
  283. }
  284. }
  285. pbc->Release();
  286. }
  287. psf->Release();
  288. }
  289. }
  290. else
  291. {
  292. hr = E_FAIL;
  293. }
  294. prp->Release();
  295. }
  296. }
  297. return hr;
  298. }
  299. // dereference a link and get the target path
  300. HRESULT CNetworkPlace::_GetTargetPath(LPCITEMIDLIST pidl, LPTSTR pszPath, int cchPath)
  301. {
  302. LPITEMIDLIST pidlTarget;
  303. HRESULT hr = SHGetTargetFolderIDList(pidl, &pidlTarget);
  304. if (SUCCEEDED(hr))
  305. {
  306. SHGetNameAndFlags(pidlTarget, SHGDN_FORPARSING, pszPath, cchPath, NULL);
  307. ILFree(pidlTarget);
  308. }
  309. return hr;
  310. }
  311. // create an IDLIST for the target that we have, this code attempts to parse the name and
  312. // then set our state for the item. if we fail to parse then we attempt to have Web Folders
  313. // look at it - this most common scenario for this will be the DAV RDR failing because
  314. // the server isn't a DAV store, so instead we try Web Folders to handle WEC etc.
  315. HRESULT CNetworkPlace::_IDListFromTarget(HWND hwnd)
  316. {
  317. HRESULT hr = S_OK;
  318. if (!_pidl)
  319. {
  320. if (_szTarget[0])
  321. {
  322. _fIsWebFolder = FALSE; // not a web folder
  323. BINDCTX_PARAM rgParams[] =
  324. {
  325. { STR_PARSE_PREFER_FOLDER_BROWSING, NULL},
  326. { L"BUT NOT WEBFOLDERS", NULL},
  327. };
  328. IBindCtx *pbc;
  329. hr = BindCtx_RegisterObjectParams(NULL, rgParams, ARRAYSIZE(rgParams), &pbc);
  330. if (SUCCEEDED(hr))
  331. {
  332. IBindCtx *pbcWindow;
  333. hr = BindCtx_RegisterUIWindow(pbc, hwnd, &pbcWindow);
  334. if (SUCCEEDED(hr))
  335. {
  336. SFGAOF sfgao;
  337. hr = SHParseDisplayName(_szTarget, pbcWindow, &_pidl, SFGAO_FOLDER, &sfgao);
  338. // if we parsed something that turns out to not
  339. // be a folder, we want to throw it away
  340. if (SUCCEEDED(hr) && !(sfgao & SFGAO_FOLDER))
  341. {
  342. ILFree(_pidl);
  343. _pidl = 0;
  344. hr = E_FAIL;
  345. }
  346. // if that failed, its is a HTTP/HTTPS and we have web folders support then lets try
  347. // and fall back to the old behaviour.
  348. if (FAILED(hr) && _fSupportWebFolders)
  349. {
  350. DWORD scheme = GetUrlScheme(_szTarget);
  351. if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS)
  352. {
  353. switch (hr)
  354. {
  355. #if 0
  356. case HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND):
  357. case HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND):
  358. case HRESULT_FROM_WIN32(ERROR_BAD_NET_NAME):
  359. case HRESULT_FROM_WIN32(ERROR_BAD_NETPATH):
  360. #endif
  361. case HRESULT_FROM_WIN32(ERROR_CANCELLED):
  362. break;
  363. default:
  364. {
  365. hr = _TryWebFolders(hwnd);
  366. if (SUCCEEDED(hr))
  367. {
  368. _fIsWebFolder = TRUE;
  369. }
  370. }
  371. }
  372. }
  373. }
  374. if (SUCCEEDED(hr))
  375. {
  376. // given that we may have translated the name above for the parse
  377. // to work, lets read back the name we used into our _szTarget.
  378. SHGetNameAndFlags(_pidl, SHGDN_FORPARSING, _szTarget, ARRAYSIZE(_szTarget), NULL);
  379. }
  380. pbcWindow->Release();
  381. }
  382. // compute the place name for the location we have hit, this includes reusing
  383. // any places we have already created.
  384. if (SUCCEEDED(hr) && !_szName[0])
  385. {
  386. SHGetNameAndFlags(_pidl, SHGDN_NORMAL, _szName, ARRAYSIZE(_szName), NULL);
  387. TCHAR szPath[MAX_PATH];
  388. if (!_fIsWebFolder && _IsPlaceTaken(_szName, szPath))
  389. {
  390. PathYetAnotherMakeUniqueName(szPath, szPath, NULL, NULL);
  391. StrCpyN(_szName, PathFindFileName(szPath), ARRAYSIZE(_szName)); // update our state
  392. }
  393. }
  394. pbc->Release();
  395. }
  396. }
  397. else
  398. {
  399. hr = E_FAIL;
  400. }
  401. }
  402. return hr;
  403. }
  404. // handle creating the network place shortcut
  405. HRESULT CNetworkPlace::CreatePlace(HWND hwnd, BOOL fOpen)
  406. {
  407. HRESULT hr = _IDListFromTarget(hwnd);
  408. if (SUCCEEDED(hr))
  409. {
  410. // web folders already have their links created, therefore we can ignore this
  411. // whole process for them, and instead fall back to just executing their link.
  412. //
  413. // for regular folders though we must attempt to find a unique name and create
  414. // the link, or if the link already exists that we can use then just open it.
  415. if (!_fIsWebFolder)
  416. {
  417. IShellLink *psl;
  418. hr = CoCreateInstance(CLSID_FolderShortcut, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellLink, &psl));
  419. if (SUCCEEDED(hr))
  420. {
  421. hr = psl->SetIDList(_pidl);
  422. if (SUCCEEDED(hr))
  423. hr = psl->SetDescription(_szDescription[0] ? _szDescription:_szTarget);
  424. if (SUCCEEDED(hr))
  425. {
  426. IPersistFile *ppf;
  427. hr = psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
  428. if (SUCCEEDED(hr))
  429. {
  430. // get the name to the shortcut, we assume that this is unique
  431. TCHAR szPath[MAX_PATH];
  432. SHGetSpecialFolderPath(NULL, szPath, CSIDL_NETHOOD, TRUE);
  433. PathCombine(szPath, szPath, _szName);
  434. hr = ppf->Save(szPath, TRUE);
  435. ppf->Release();
  436. }
  437. }
  438. psl->Release();
  439. }
  440. }
  441. else
  442. {
  443. // this is the web folder case, so we now need to set the display
  444. // name for this guy. note that we don't have any control over
  445. // the description text we are going to be seeing.
  446. IShellFolder *psf;
  447. LPCITEMIDLIST pidlLast;
  448. hr = SHBindToIDListParent(_pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
  449. if (SUCCEEDED(hr))
  450. {
  451. LPITEMIDLIST pidlNew;
  452. hr = psf->SetNameOf(hwnd, pidlLast, _szName, SHGDN_INFOLDER, &pidlNew);
  453. if (SUCCEEDED(hr))
  454. {
  455. _fDeleteWebFolder = FALSE;
  456. //Web folders will return S_FALSE with bogus pidlNew if _szName is the same as the current name
  457. if (S_OK == hr)
  458. {
  459. ILFree(_pidl);
  460. hr = SHILCombine((LPCITEMIDLIST)c_pidlWebFolders, pidlNew, &_pidl);
  461. }
  462. }
  463. psf->Release();
  464. }
  465. }
  466. // now open the target if thats what they asked for
  467. if (SUCCEEDED(hr) && fOpen)
  468. {
  469. LPITEMIDLIST pidlNetPlaces;
  470. hr = SHGetSpecialFolderLocation(hwnd, CSIDL_NETWORK, &pidlNetPlaces);
  471. if (SUCCEEDED(hr))
  472. {
  473. IShellFolder *psf;
  474. hr = SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidlNetPlaces, &psf));
  475. if (SUCCEEDED(hr))
  476. {
  477. LPITEMIDLIST pidl;
  478. hr = psf->ParseDisplayName(hwnd, NULL, _szName, NULL, &pidl, NULL);
  479. if (SUCCEEDED(hr))
  480. {
  481. LPITEMIDLIST pidlToOpen;
  482. hr = SHILCombine(pidlNetPlaces, pidl, &pidlToOpen);
  483. if (SUCCEEDED(hr))
  484. {
  485. BrowseToPidl(pidlToOpen);
  486. ILFree(pidlToOpen);
  487. }
  488. ILFree(pidl);
  489. }
  490. psf->Release();
  491. }
  492. ILFree(pidlNetPlaces);
  493. }
  494. }
  495. }
  496. return hr;
  497. }