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.

1420 lines
47 KiB

  1. /*****************************************************************************\
  2. * ftpsite.cpp - Internal object that manages a single FTP site
  3. \*****************************************************************************/
  4. #include "priv.h"
  5. #include "ftpsite.h"
  6. #include "ftpinet.h"
  7. #include "ftpurl.h"
  8. #include "statusbr.h"
  9. #include "offline.h"
  10. #include <ratings.h>
  11. #include <wininet.h>
  12. #ifdef DEBUG
  13. DWORD g_dwOpenConnections = 0; // Ref Counting Open Connections
  14. #endif // DEBUG
  15. /*****************************************************************************\
  16. * CFtpSite
  17. *
  18. * EEK! RFC 1738 is really scary. FTP sites don't necessarily
  19. * start you at the root, and RFC1738 says that ftp://foo/bar asks
  20. * for the file bar in the DEFAULT directory, not the root!
  21. \*****************************************************************************/
  22. CFtpList * g_FtpSiteCache = NULL; /* The list of all open FTP sites */
  23. void CFtpSite::FlushHint(void)
  24. {
  25. HINTERNET hint = m_hint;
  26. m_hint = NULL;
  27. if (hint)
  28. {
  29. // Our caller needs to be holding the critical section
  30. // while we modify m_hint
  31. ASSERTCRITICAL;
  32. InternetCloseHandle(hint);
  33. // DEBUG_CODE(g_dwOpenConnections--;);
  34. }
  35. }
  36. void CFtpSite::FlushHintCritial(void)
  37. {
  38. ASSERTNONCRITICAL;
  39. ENTERCRITICAL;
  40. FlushHint();
  41. LEAVECRITICAL;
  42. }
  43. void CFtpSite::FlushHintCB(LPVOID pvFtpSite)
  44. {
  45. CFtpSite * pfs = (CFtpSite *) pvFtpSite;
  46. if (pfs)
  47. {
  48. pfs->FlushHint();
  49. pfs->Release();
  50. }
  51. }
  52. /*****************************************************************************\
  53. * An InternetConnect has just completed. Get the motd and cache it.
  54. *
  55. * hint - the connected handle, possibly 0 if error
  56. \*****************************************************************************/
  57. void CFtpSite::CollectMotd(HINTERNET hint)
  58. {
  59. CFtpGlob * pfg = GetFtpResponse(&m_cwe);
  60. ENTERCRITICAL;
  61. m_fMotd = m_pfgMotd ? TRUE : FALSE; // We have a motd
  62. IUnknown_Set(&m_pfgMotd, NULL);
  63. m_pfgMotd = pfg;
  64. LEAVECRITICAL;
  65. }
  66. /*****************************************************************************\
  67. FUNCTION: ReleaseHint
  68. DESCRIPTION:
  69. An FtpDir client is finished with a handle to the FTP site.
  70. Put it into the cache, and throw away what used to be there.
  71. We always keep the most recent handle, because that reduces the
  72. likelihood that the server will close the connection due to extended
  73. inactivity.
  74. The critical section around this entire procedure is important,
  75. else we open up all sorts of really ugly race conditions. E.g.,
  76. the timeout might trigger before we're finished initializing it.
  77. Or somebody might ask for the handle before we're ready.
  78. \*****************************************************************************/
  79. void CFtpSite::ReleaseHint(LPCITEMIDLIST pidlFtpPath, HINTERNET hint)
  80. {
  81. ENTERCRITICAL;
  82. TriggerDelayedAction(&m_hgti); // Kick out the old one
  83. _SetPidl(pidlFtpPath);
  84. m_hint = hint;
  85. if (SUCCEEDED(SetDelayedAction(FlushHintCB, (LPVOID) this, &m_hgti)))
  86. AddRef(); // We just gave away a ref.
  87. else
  88. FlushHint(); // Oh well, can't cache it
  89. LEAVECRITICAL;
  90. }
  91. // NT #362108: We need to set the redirect password for the CFtpSite that
  92. // contains the server, the user name, but a blank password to be redirected
  93. // to the CFtpSite that does have the correct password. This way, if a user
  94. // logs in and doesn't save the password in the URL or the secure cache, we
  95. // then put it in the in memory password cache so it stays valid for that
  96. // "browser" session (defined by process lifetime). We then need to redirect
  97. // future navigations that go to that
  98. HRESULT CFtpSite::_SetRedirPassword(LPCTSTR pszServer, INTERNET_PORT ipPortNum, LPCTSTR pszUser, LPCTSTR pszPassword, LPCITEMIDLIST pidlFtpPath, LPCTSTR pszFragment)
  99. {
  100. TCHAR szUrl[MAX_URL_STRING];
  101. HRESULT hr;
  102. hr = UrlCreate(pszServer, pszUser, TEXT(""), TEXT(""), pszFragment, ipPortNum, NULL, szUrl, ARRAYSIZE(szUrl));
  103. if (EVAL(SUCCEEDED(hr)))
  104. {
  105. LPITEMIDLIST pidlServer;
  106. hr = CreateFtpPidlFromUrl(szUrl, GetCWireEncoding(), NULL, &pidlServer, m_pm, TRUE);
  107. if (SUCCEEDED(hr))
  108. {
  109. LPITEMIDLIST pidl = ILCombine(pidlServer, pidlFtpPath);
  110. if (pidl)
  111. {
  112. CFtpSite * pfsDest = NULL;
  113. // The user name has changed so we need to update the
  114. // CFtpSite with the new user name also.
  115. hr = SiteCache_PidlLookup(pidl, FALSE, m_pm, &pfsDest);
  116. if (SUCCEEDED(hr))
  117. {
  118. pfsDest->SetRedirPassword(pszPassword);
  119. pfsDest->Release();
  120. }
  121. ILFree(pidl);
  122. }
  123. ILFree(pidlServer);
  124. }
  125. }
  126. return hr;
  127. }
  128. HRESULT CFtpSite::_RedirectAndUpdate(LPCTSTR pszServer, INTERNET_PORT ipPortNum, LPCTSTR pszUser, LPCTSTR pszPassword, LPCITEMIDLIST pidlFtpPath, LPCTSTR pszFragment, IUnknown * punkSite, CFtpFolder * pff)
  129. {
  130. TCHAR szUrl[MAX_URL_STRING];
  131. TCHAR szUser[INTERNET_MAX_USER_NAME_LENGTH];
  132. HRESULT hr;
  133. StrCpyN(szUser, pszUser, ARRAYSIZE(szUser)); // Copy because of possible reentrancy
  134. EscapeString(NULL, szUser, ARRAYSIZE(szUser));
  135. hr = UrlCreate(pszServer, szUser, pszPassword, TEXT(""), pszFragment, ipPortNum, NULL, szUrl, ARRAYSIZE(szUrl));
  136. if (EVAL(SUCCEEDED(hr) && pff))
  137. {
  138. LPITEMIDLIST pidlServer;
  139. hr = CreateFtpPidlFromUrl(szUrl, GetCWireEncoding(), NULL, &pidlServer, m_pm, TRUE);
  140. if (SUCCEEDED(hr))
  141. {
  142. LPITEMIDLIST pidl = ILCombine(pidlServer, pidlFtpPath);
  143. if (pidl)
  144. {
  145. // If the user changed the password, we need to setup a redirect so
  146. // they can return later. (NT #362108)
  147. if (m_pszUser && !StrCmp(m_pszUser, szUser) && StrCmp(m_pszPassword, pszPassword))
  148. {
  149. _SetRedirPassword(pszServer, ipPortNum, szUser, pszPassword, pidlFtpPath, pszFragment);
  150. }
  151. // If the user name changed, set a redirect.
  152. if (!m_pszUser || StrCmp(m_pszUser, szUser))
  153. {
  154. CFtpSite * pfsDest = NULL;
  155. // The user name has changed so we need to update the
  156. // CFtpSite with the new user name also.
  157. hr = SiteCache_PidlLookup(pidl, FALSE, m_pm, &pfsDest);
  158. if (SUCCEEDED(hr))
  159. {
  160. pfsDest->SetRedirPassword(pszPassword);
  161. pfsDest->Release();
  162. }
  163. }
  164. hr = _Redirect(pidl, punkSite, pff);
  165. ILFree(pidl);
  166. }
  167. ILFree(pidlServer);
  168. }
  169. }
  170. return hr;
  171. }
  172. HRESULT CFtpSite::_Redirect(LPITEMIDLIST pidl, IUnknown * punkSite, CFtpFolder * pff)
  173. {
  174. LPITEMIDLIST pidlFull = pff->CreateFullPublicPidl(pidl);
  175. HRESULT hr = E_INVALIDARG;
  176. if (pidlFull)
  177. {
  178. hr = IUnknown_PidlNavigate(punkSite, pidlFull, FALSE);
  179. ASSERT(SUCCEEDED(hr));
  180. ILFree(pidlFull);
  181. }
  182. return hr;
  183. }
  184. /*****************************************************************************\
  185. FUNCTION: _SetDirectory
  186. DESCRIPTION:
  187. When the caller wants a handle to the server, they often want a different
  188. directory than what's in the cache. This function needs to change into
  189. the new directory.
  190. \*****************************************************************************/
  191. HRESULT CFtpSite::_SetDirectory(HINTERNET hint, HWND hwnd, LPCITEMIDLIST pidlNewDir, CStatusBar * psb, int * pnTriesLeft)
  192. {
  193. HRESULT hr = S_OK;
  194. if (pidlNewDir && FtpID_IsServerItemID(pidlNewDir))
  195. pidlNewDir = _ILNext(pidlNewDir); // Skip the server.
  196. ASSERT(m_pidl);
  197. // NT #300889: I would like to cache the dir but sometimes it gets
  198. // out of wack and m_pidl doesn't match the HINTERNET's
  199. // cwd. PERF: This could be fixed in the future but
  200. // this perf tweak isn't work the work now (small gain).
  201. // if (m_pidl && !FtpPidl_IsPathEqual(_ILNext(m_pidl), pidlNewDir))
  202. {
  203. LPITEMIDLIST pidlWithVirtualRoot = NULL;
  204. if (psb)
  205. {
  206. WCHAR wzDisplayPath[MAX_PATH]; // For Statusbar.
  207. if (pidlNewDir && SUCCEEDED(GetDisplayPathFromPidl(pidlNewDir, wzDisplayPath, ARRAYSIZE(wzDisplayPath), TRUE)))
  208. psb->SetStatusMessage(IDS_CHDIR, wzDisplayPath);
  209. else
  210. psb->SetStatusMessage(IDS_CHDIR, L"\\");
  211. }
  212. hr = PidlInsertVirtualRoot(pidlNewDir, &pidlWithVirtualRoot);
  213. if (SUCCEEDED(hr))
  214. {
  215. hr = FtpSetCurrentDirectoryPidlWrap(hint, TRUE, pidlWithVirtualRoot, TRUE, TRUE);
  216. if (SUCCEEDED(hr)) // Ok if failed. (No Access?)
  217. {
  218. hr = _SetPidl(pidlNewDir);
  219. }
  220. else
  221. {
  222. ReleaseHint(NULL, hint); // Nowhere
  223. if (hr == HRESULT_FROM_WIN32(ERROR_FTP_DROPPED))
  224. FlushHintCritial(); // Don't cache dead hint
  225. else
  226. {
  227. DisplayWininetError(hwnd, TRUE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_CHANGEDIR, IDS_FTPERR_WININET, MB_OK, NULL);
  228. *pnTriesLeft = 0; // Make sure we don't keep display UI.
  229. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  230. }
  231. hint = 0;
  232. }
  233. ILFree(pidlWithVirtualRoot);
  234. }
  235. if (psb)
  236. psb->SetStatusMessage(IDS_EMPTY, 0);
  237. }
  238. return hr;
  239. }
  240. /*****************************************************************************\
  241. FUNCTION: _LoginToTheServer
  242. DESCRIPTION:
  243. We want an HINTERNET to do some FTP operation but we don't have one
  244. cached. So, login to create it.
  245. WARNING: This function will be called in a critical section and needs to
  246. return in one. However, it may leave the critical section for a
  247. while.
  248. \*****************************************************************************/
  249. HRESULT CFtpSite::_LoginToTheServer(HWND hwnd, HINTERNET hintDll, HINTERNET * phint, LPCITEMIDLIST pidlFtpPath, CStatusBar * psb, IUnknown * punkSite, CFtpFolder * pff)
  250. {
  251. HRESULT hr = S_OK;
  252. ASSERTCRITICAL;
  253. BOOL fKeepTryingToLogin = FALSE;
  254. BOOL fTryOldPassword = TRUE;
  255. LEAVECRITICALNOASSERT;
  256. TCHAR szUser[INTERNET_MAX_USER_NAME_LENGTH];
  257. TCHAR szPassword[INTERNET_MAX_PASSWORD_LENGTH];
  258. StrCpyN(szUser, m_pszUser, ARRAYSIZE(szUser));
  259. StrCpyN(szPassword, m_pszPassword, ARRAYSIZE(szPassword));
  260. ASSERT(m_pszServer);
  261. if (psb)
  262. psb->SetStatusMessage(IDS_CONNECTING, m_pszServer);
  263. do
  264. {
  265. hr = InternetConnectWrap(hintDll, TRUE, HANDLE_NULLSTR(m_pszServer), m_ipPortNum, NULL_FOR_EMPTYSTR(szUser), NULL_FOR_EMPTYSTR(szPassword), INTERNET_SERVICE_FTP, 0, 0, phint);
  266. if (*phint)
  267. fKeepTryingToLogin = FALSE; // Move up.
  268. else
  269. {
  270. BOOL fSkipLoginDialog = FALSE;
  271. // Display Login dialog to get new user name/password to try again or cancel login.
  272. // fKeepTryingToLogin = TRUE if Dialog said [LOGIN].
  273. if (((ERROR_INTERNET_LOGIN_FAILURE == HRESULT_CODE(hr)) ||
  274. (ERROR_INTERNET_INCORRECT_USER_NAME == HRESULT_CODE(hr)) ||
  275. (ERROR_INTERNET_INCORRECT_PASSWORD == HRESULT_CODE(hr))) && hwnd)
  276. {
  277. BOOL fIsAnonymous = (!szUser[0] || !StrCmpI(szUser, TEXT("anonymous")) ? TRUE : FALSE);
  278. DWORD dwLoginFlags = (fIsAnonymous ? LOGINFLAGS_ANON_LOGINJUSTFAILED : LOGINFLAGS_USER_LOGINJUSTFAILED);
  279. if (fTryOldPassword)
  280. {
  281. hr = m_cAccount.GetUserName(HANDLE_NULLSTR(m_pszServer), szUser, ARRAYSIZE(szUser));
  282. if (S_OK == hr)
  283. {
  284. hr = m_cAccount.GetPassword(HANDLE_NULLSTR(m_pszServer), szUser, szPassword, ARRAYSIZE(szPassword));
  285. if (S_OK == hr)
  286. {
  287. fKeepTryingToLogin = TRUE;
  288. fSkipLoginDialog = TRUE;
  289. }
  290. }
  291. }
  292. if (!fSkipLoginDialog)
  293. {
  294. // If the user tried to log in anonymously and failed, we want to try
  295. // logging in with a password. If the user tried logging in with a password
  296. // and failed, we want to keep trying to log in with a password.
  297. //
  298. // DisplayLoginDialog returns S_OK for OK pressed, S_FALSE for Cancel button, and
  299. // FAILED() for something is really messed up.
  300. hr = m_cAccount.DisplayLoginDialog(hwnd, dwLoginFlags, HANDLE_NULLSTR(m_pszServer),
  301. szUser, ARRAYSIZE(szUser), szPassword, ARRAYSIZE(szPassword));
  302. }
  303. // S_FALSE means the user cancelled out of the Login dialog.
  304. // We need to turn this into an error value so the caller,
  305. // CFtpDir::WithHint() won't call the callback.
  306. if (S_FALSE == hr)
  307. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  308. fKeepTryingToLogin = (SUCCEEDED(hr) ? TRUE : FALSE);
  309. if (fKeepTryingToLogin)
  310. {
  311. // We need to set the cancelled error so we don't display the
  312. // error message after this.
  313. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  314. }
  315. fTryOldPassword = FALSE;
  316. }
  317. else
  318. fKeepTryingToLogin = FALSE;
  319. }
  320. }
  321. while (fKeepTryingToLogin);
  322. if (!*phint)
  323. {
  324. ASSERT(2 != HRESULT_CODE(hr)); // error 2 = wininet not configured
  325. #ifdef DEBUG
  326. // Gee, I wonder why I couldn't connect, let's find out.
  327. TCHAR szBuff[1500];
  328. InternetGetLastResponseInfoDisplayWrap(FALSE, NULL, szBuff, ARRAYSIZE(szBuff));
  329. // This may happen if the server has too many connections. We may want to sniff
  330. // for this and offer to keep trying. These are the response from the various
  331. // FTP Servers in this case:
  332. // IIS v5: 421 Too many people are connected. Please come back when the server is less busy.
  333. // UNIX: ???
  334. #endif // DEBUG
  335. }
  336. // Was a different login name or password needed in order to login successfully?
  337. else
  338. {
  339. LPITEMIDLIST pidlVirtualDir;
  340. CollectMotd(*phint);
  341. _QueryServerFeatures(*phint);
  342. // Ref Count the open connections.
  343. // DEBUG_CODE(g_dwOpenConnections++;);
  344. // Is it a VMS Server?
  345. if (m_fIsServerVMS)
  346. {
  347. // Yes, so skip getting pidlVirtualDir because wininet gives us
  348. // garbage for FtpGetCurrentDirectoryA().
  349. }
  350. else
  351. {
  352. // NOTE: If the connection isn't annonymous, the server may put the user
  353. // into a sub directory called a virtual root. We need to squirel that
  354. // directory away because it may be needed when going into sub directories
  355. // relative to this virtual root.
  356. // Example: ftp://user1:password@server/ puts you into /users/user1/
  357. // Then: ftp://user1:password@server/dir1 really should be /users/user1/dir1/
  358. hr = FtpGetCurrentDirectoryPidlWrap(*phint, TRUE, GetCWireEncoding(), &pidlVirtualDir);
  359. if (SUCCEEDED(hr))
  360. {
  361. // Are we rooted at '/'? (Meaning no virtual root)
  362. Pidl_Set(&m_pidlVirtualDir, pidlVirtualDir);
  363. ILFree(pidlVirtualDir);
  364. }
  365. }
  366. //DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "CFtpSite::GetHint() FtpGetCurrentDirectory() returned %#08lx", hr));
  367. if (StrCmp(HANDLE_NULLSTR(m_pszUser), szUser) || StrCmp(HANDLE_NULLSTR(m_pszPassword), szPassword))
  368. {
  369. // Yes, so redirect so the AddressBand and User Status Bar pane update.
  370. // We normally log in with m_pidl because normally we login with
  371. // a default directory ('\') and then change directories to the final location.
  372. // we do this so isolate access denied to the server and access denied to the
  373. // directory.
  374. //
  375. // We pass pidlFtpPath instead in this case because it will tell the browser
  376. // to re-direct and we won't get a chance to do the ChangeDir later.
  377. Str_SetPtr(&m_pszRedirPassword, szPassword);
  378. _RedirectAndUpdate(m_pszServer, m_ipPortNum, szUser, szPassword, pidlFtpPath, m_pszFragment, punkSite, pff);
  379. hr = HRESULT_FROM_WIN32(ERROR_NETWORK_ACCESS_DENIED);
  380. }
  381. }
  382. // Can we assume annonymous logins don't use virtual roots?
  383. ASSERT(FAILED(hr) || (m_pidlVirtualDir && szUser[0]) || !(m_pidlVirtualDir && szUser[0]));
  384. if (psb)
  385. psb->SetStatusMessage(IDS_EMPTY, NULL);
  386. ENTERCRITICALNOASSERT;
  387. // The directory is empty.
  388. _SetPidl(NULL);
  389. return hr;
  390. }
  391. /*****************************************************************************\
  392. FUNCTION: GetHint
  393. DESCRIPTION:
  394. An IShellFolder client wants a handle to the FTP site.
  395. Pull it from the cache if possible.
  396. The caller should have marked the IShellFolder as busy.
  397. EEK! RFC 1738 is really scary. FTP sites don't necessarily
  398. start you at the root, and RFC1738 says that ftp://foo/bar asks
  399. for the file bar in the DEFAULT directory, not the root!
  400. \*****************************************************************************/
  401. HRESULT CFtpSite::GetHint(HWND hwnd, LPCITEMIDLIST pidlFtpPath, CStatusBar * psb, HINTERNET * phint, IUnknown * punkSite, CFtpFolder * pff)
  402. {
  403. HINTERNET hint = NULL;
  404. HINTERNET hintDll = GetWininetSessionHandle();
  405. HRESULT hr = S_OK;
  406. if (!hintDll)
  407. {
  408. // No point in retrying if we can't init Wininet
  409. hr = HRESULT_FROM_WIN32(GetLastError()); // Save error code
  410. }
  411. else
  412. {
  413. int cTriesLeft = 1; // This is a feature that would be cool to implement.
  414. hr = AssureNetConnection(NULL, hwnd, m_pszServer, NULL, TRUE);
  415. if (ILIsEmpty(pidlFtpPath))
  416. pidlFtpPath = NULL;
  417. if (SUCCEEDED(hr))
  418. {
  419. // The CS is protecting m_hint. First guy gets to remove and use the cached internet session.
  420. // subsequent callers need create new ones since internet sessions have state while in use. (Current dir, etc.)
  421. ASSERTNONCRITICAL;
  422. ENTERCRITICALNOASSERT;
  423. do
  424. {
  425. BOOL fReuseExistingConnection = FALSE;
  426. hr = E_FAIL; // We don't have our hint yet...
  427. ASSERTCRITICAL;
  428. hint = (HINTERNET) InterlockedExchangePointer(&m_hint, 0);
  429. if (hint)
  430. {
  431. HINTERNET hintResponse;
  432. TriggerDelayedAction(&m_hgti); // Nothing will happen
  433. fReuseExistingConnection = TRUE; // We will need to change it for the current user.
  434. // We want (S_OK == hr) if our login session is still good. Else, we want to
  435. // re-login.
  436. hr = FtpCommandWrap(hint, FALSE, FALSE, FTP_TRANSFER_TYPE_ASCII, FTP_CMD_NO_OP, NULL, &hintResponse);
  437. if (SUCCEEDED(hr))
  438. {
  439. TraceMsg(TF_FTPOPERATION, "CFtpSite::GetHint() We are going to use a cached HINTERNET.");
  440. InternetCloseHandleWrap(hintResponse, TRUE);
  441. }
  442. else
  443. {
  444. TraceMsg(TF_FTPOPERATION, "CFtpSite::GetHint() Can't used cached HINTERNET because server didn't respond to NOOP.");
  445. InternetCloseHandleWrap(hint, TRUE);
  446. }
  447. }
  448. if (FAILED(hr))
  449. {
  450. hr = _LoginToTheServer(hwnd, hintDll, &hint, pidlFtpPath, psb, punkSite, pff);
  451. TraceMsg(TF_FTPOPERATION, "CFtpSite::GetHint() We had to login because we didn't have a cached HINTERNET.");
  452. }
  453. ASSERTCRITICAL;
  454. LEAVECRITICALNOASSERT;
  455. // Do we need to CD into a specific directory? Yes, if...
  456. // 1. We succeeded above, AND
  457. // 2. We are already using a connection so the dir may be incorrect, OR
  458. // 3. We need a non-default dir.
  459. if (SUCCEEDED(hr) && (fReuseExistingConnection || pidlFtpPath)) // pidlFtpPath may be NULL.
  460. hr = _SetDirectory(hint, hwnd, pidlFtpPath, psb, &cTriesLeft);
  461. ENTERCRITICALNOASSERT;
  462. ASSERTCRITICAL;
  463. }
  464. while (hr == HRESULT_FROM_WIN32(ERROR_FTP_DROPPED) && --cTriesLeft);
  465. LEAVECRITICALNOASSERT;
  466. }
  467. }
  468. *phint = hint;
  469. return hr;
  470. }
  471. HRESULT CFtpSite::_CheckToEnableCHMOD(LPCWIRESTR pwResponse)
  472. {
  473. HRESULT hr = S_FALSE;
  474. // TODO: We should probably be more restictive in how we parse the
  475. // response. We should probably verify there is some kind of
  476. // white space before and after the command.
  477. LPCWIRESTR pwCommand = StrStrIA(pwResponse, FTP_UNIXCMD_CHMODA);
  478. // Does this FTP server support the "SITE CHMOD" command?
  479. if (pwCommand)
  480. {
  481. // Yes, so we may want to use it later.
  482. m_fIsCHMODSupported = TRUE;
  483. // We can later respond with:
  484. // "SITE chmod xyz FileName.txt"
  485. // x is for Owner, (4=Read, 2=Write, 1=Execute)
  486. // y is for Owner, (4=Read, 2=Write, 1=Execute)
  487. // z is for Owner, (4=Read, 2=Write, 1=Execute)
  488. }
  489. return hr;
  490. }
  491. /*****************************************************************************\
  492. FUNCTION: _QueryServerFeatures
  493. DESCRIPTION:
  494. Find out what the server is and isn't capable of. Information we could
  495. use:
  496. SITE: Find out OS specific commands that may be useful. "chmod" is one
  497. of them.
  498. HELP SITE: Find out what the OS supports.
  499. SYST: Find out the OS type.
  500. NOOP: See if the connection is still alive.
  501. MLST: Unambiguous Directory listing with dates in UTC.
  502. MLSD:
  503. FEAT: Features supported. UTF8 is the one we care about.
  504. Response to "SITE HELP" for these servers:
  505. UNIX Type: L8 Version: BSD-199506
  506. UNIX Type: L8
  507. UMASK CHMOD GROUP NEWER INDEX ALIAS GROUPS
  508. IDLE HELP GPASS MINFO EXEC CDPATH
  509. Windows_NT version 4.0
  510. CKM DIRSTYLE HELP STATS
  511. \*****************************************************************************/
  512. HRESULT CFtpSite::_QueryServerFeatures(HINTERNET hint)
  513. {
  514. HRESULT hr = E_FAIL;
  515. HINTERNET hintResponse;
  516. // Can we turn on 'UTF8' encoding?
  517. if (SUCCEEDED(FtpCommandWrap(hint, FALSE, FALSE, FTP_TRANSFER_TYPE_ASCII, FTP_CMD_UTF8, NULL, &hintResponse)))
  518. {
  519. m_fInUTF8Mode = TRUE;
  520. m_cwe.SetUTF8Support(TRUE);
  521. TraceMsg(TF_FTP_OTHER, "_QueryServerFeatures() in UTF8 Mode");
  522. InternetCloseHandleWrap(hintResponse, TRUE);
  523. }
  524. else
  525. {
  526. TraceMsg(TF_FTP_OTHER, "_QueryServerFeatures() NOT in UTF8 Mode");
  527. m_fInUTF8Mode = FALSE;
  528. }
  529. if (!m_fFeaturesQueried)
  530. {
  531. // Is type of server software is running? We want to know if we are running
  532. // on VMS, because in that case we want to fall back to HTML view (URLMON).
  533. // This is because the wininet guys don't want to support it.
  534. if (SUCCEEDED(FtpCommandWrap(hint, FALSE, FALSE, FTP_TRANSFER_TYPE_ASCII, FTP_CMD_SYSTEM, NULL, &hintResponse)))
  535. {
  536. DWORD dwError;
  537. WIRECHAR wResponse[MAX_URL_STRING];
  538. DWORD cchSize = ARRAYSIZE(wResponse);
  539. if (SUCCEEDED(InternetGetLastResponseInfoWrap(TRUE, &dwError, wResponse, &cchSize)))
  540. {
  541. // Is this a VMS server?
  542. if (StrStrIA(wResponse, FTP_SYST_VMS))
  543. m_fIsServerVMS = TRUE;
  544. TraceMsg(TF_FTP_OTHER, "_QueryServerFeatures() SYSTM returned %hs.", wResponse);
  545. }
  546. InternetCloseHandleWrap(hintResponse, TRUE);
  547. }
  548. #ifdef FEATURE_CHANGE_PERMISSIONS
  549. // Is the server capable of supporting the UNIX "chmod" command
  550. // to change permissions on the file?
  551. if (SUCCEEDED(FtpCommandWrap(hint, FALSE, FALSE, FTP_TRANSFER_TYPE_ASCII, FTP_CMD_SITE_HELP, NULL, &hintResponse)))
  552. {
  553. DWORD dwError;
  554. WIRECHAR wResponse[MAX_URL_STRING];
  555. DWORD cchSize = ARRAYSIZE(wResponse);
  556. if (SUCCEEDED(InternetGetLastResponseInfoWrap(TRUE, &dwError, wResponse, &cchSize)))
  557. {
  558. _CheckToEnableCHMOD(wResponse);
  559. // TraceMsg(TF_FTP_OTHER, "_QueryServerFeatures() SITE HELP returned success");
  560. }
  561. InternetCloseHandleWrap(hintResponse, TRUE);
  562. }
  563. #endif // FEATURE_CHANGE_PERMISSIONS
  564. /*
  565. // Is the server capable of supporting the UNIX "chmod" command
  566. // to change permissions on the file?
  567. if (SUCCEEDED(FtpCommandWrap(hint, FALSE, FALSE, FTP_TRANSFER_TYPE_ASCII, FTP_CMD_SITE, NULL, &hintResponse)))
  568. {
  569. DWORD dwError;
  570. WIRECHAR wResponse[MAX_URL_STRING];
  571. DWORD cchSize = ARRAYSIZE(wResponse);
  572. if (SUCCEEDED(InternetGetLastResponseInfoWrap(TRUE, &dwError, wResponse, &cchSize)))
  573. {
  574. TraceMsg(TF_FTP_OTHER, "_QueryServerFeatures() SITE returned succeess");
  575. }
  576. InternetCloseHandleWrap(hintResponse, TRUE);
  577. }
  578. */
  579. m_fFeaturesQueried = TRUE;
  580. }
  581. return S_OK; // This shouldn't fail.
  582. }
  583. LPITEMIDLIST CFtpSite::GetPidl(void)
  584. {
  585. return ILClone(m_pidl);
  586. }
  587. /*****************************************************************************\
  588. FUNCTION: _SetPidl
  589. DESCRIPTION:
  590. m_pidl contains the ServerID and the ItemIDs making up the path of where
  591. m_hint is currently located. This function will take a new path in pidlFtpPath
  592. and update m_pidl so it still has the server.
  593. \*****************************************************************************/
  594. HRESULT CFtpSite::_SetPidl(LPCITEMIDLIST pidlFtpPath)
  595. {
  596. HRESULT hr = E_FAIL;
  597. LPITEMIDLIST pidlServer = FtpCloneServerID(m_pidl);
  598. if (pidlServer)
  599. {
  600. LPITEMIDLIST pidlNew = ILCombine(pidlServer, pidlFtpPath);
  601. if (pidlNew)
  602. {
  603. ILFree(m_pidl);
  604. m_pidl = pidlNew;
  605. hr = S_OK;
  606. }
  607. ILFree(pidlServer);
  608. }
  609. return hr;
  610. }
  611. /*****************************************************************************\
  612. FUNCTION: QueryMotd
  613. DESCRIPTION:
  614. Determine whether there is a motd at all.
  615. \*****************************************************************************/
  616. BOOL CFtpSite::QueryMotd(void)
  617. {
  618. return m_fMotd;
  619. }
  620. HRESULT CFtpSite::GetVirtualRoot(LPITEMIDLIST * ppidl)
  621. {
  622. HRESULT hr = S_FALSE;
  623. *ppidl = NULL;
  624. if (m_pidlVirtualDir)
  625. {
  626. *ppidl = ILClone(m_pidlVirtualDir);
  627. hr = S_OK;
  628. }
  629. return S_OK;
  630. }
  631. HRESULT CFtpSite::PidlInsertVirtualRoot(LPCITEMIDLIST pidlFtpPath, LPITEMIDLIST * ppidl)
  632. {
  633. HRESULT hr = S_OK;
  634. *ppidl = NULL;
  635. if (!m_pidlVirtualDir)
  636. *ppidl = ILClone(pidlFtpPath);
  637. else
  638. {
  639. LPITEMIDLIST pidlTemp = NULL;
  640. if (pidlFtpPath && FtpID_IsServerItemID(pidlFtpPath))
  641. {
  642. pidlTemp = FtpCloneServerID(pidlFtpPath);
  643. pidlFtpPath = _ILNext(pidlFtpPath);
  644. }
  645. LPITEMIDLIST pidlWithVRoot = ILCombine(pidlTemp, m_pidlVirtualDir);
  646. if (pidlWithVRoot)
  647. {
  648. *ppidl = ILCombine(pidlWithVRoot, pidlFtpPath);
  649. ILFree(pidlWithVRoot);
  650. }
  651. ILFree(pidlTemp);
  652. }
  653. return S_OK;
  654. }
  655. BOOL CFtpSite::HasVirtualRoot(void)
  656. {
  657. return (m_pidlVirtualDir ? TRUE : FALSE);
  658. }
  659. /*****************************************************************************\
  660. GetMotd
  661. Returns the HFGLOB that babysits the motd. The refcount has been
  662. incremented.
  663. \*****************************************************************************/
  664. CFtpGlob * CFtpSite::GetMotd(void)
  665. {
  666. if (m_pfgMotd)
  667. m_pfgMotd->AddRef();
  668. return m_pfgMotd;
  669. }
  670. /*****************************************************************************\
  671. GetCFtpList
  672. Return the CFtpList * that remembers which folders live in this CFtpSite *.
  673. WARNING! The caller must own the critical section when calling
  674. this routine, because the returned CFtpList * is not refcounted!
  675. \*****************************************************************************/
  676. CFtpList * CFtpSite::GetCFtpList(void)
  677. {
  678. return m_FtpDirList;
  679. }
  680. /*****************************************************************************\
  681. _CompareSites
  682. Callback during SiteCache_PrivSearch to see if the site is already in the
  683. list.
  684. \*****************************************************************************/
  685. int CALLBACK _CompareSites(LPVOID pvStrSite, LPVOID pvFtpSite, LPARAM lParam)
  686. {
  687. CFtpSite * pfs = (CFtpSite *) pvFtpSite;
  688. LPCTSTR pszLookupStrNew = (LPCTSTR) pvStrSite;
  689. LPCTSTR pszLookupStr = (pfs->m_pszLookupStr ? pfs->m_pszLookupStr : TEXT(""));
  690. ASSERT(pszLookupStr && pszLookupStr);
  691. return StrCmpI(pszLookupStr, pszLookupStrNew);
  692. }
  693. /*****************************************************************************\
  694. FUNCTION: SiteCache_PrivSearch
  695. DESCRIPTION:
  696. We cache information about an FTP Server to prevent hitting the net all
  697. the time. This state is stored in CFtpSite objects and we use 'lookup strings'
  698. to find them. This is what makes one server different from another. Since
  699. we store password state in a CFtpSite object, we need to have one per
  700. user/password combo.
  701. \*****************************************************************************/
  702. HRESULT SiteCache_PrivSearch(LPCTSTR pszLookup, LPCITEMIDLIST pidl, IMalloc * pm, CFtpSite ** ppfs)
  703. {
  704. CFtpSite * pfs = NULL;
  705. HRESULT hr = S_OK;
  706. ENTERCRITICAL;
  707. // CFtpSite_Init() can fail in low memory
  708. if (SUCCEEDED(CFtpSite_Init()))
  709. {
  710. pfs = (CFtpSite *) g_FtpSiteCache->Find(_CompareSites, (LPVOID)pszLookup); // Add CFtpSite:: ?
  711. if (!pfs)
  712. {
  713. // We need to hold the critical section while setting up
  714. // the new CFtpSite structure, lest somebody else come in
  715. // and try to create the same CFtpSite while we are busy.
  716. hr = CFtpSite_Create(pidl, pszLookup, pm, &pfs);
  717. if (SUCCEEDED(hr))
  718. {
  719. hr = g_FtpSiteCache->AppendItem(pfs);
  720. if (!(SUCCEEDED(hr)))
  721. IUnknown_Set(&pfs, NULL);
  722. }
  723. }
  724. }
  725. *ppfs = pfs;
  726. if (pfs)
  727. pfs->AddRef();
  728. LEAVECRITICAL;
  729. ASSERT_POINTER_MATCHES_HRESULT(*ppfs, hr);
  730. return hr;
  731. }
  732. /*****************************************************************************\
  733. FUNCTION: SiteCache_PidlLookupPrivHelper
  734. DESCRIPTION:
  735. We cache information about an FTP Server to prevent hitting the net all
  736. the time. This state is stored in CFtpSite objects and we use 'lookup strings'
  737. to find them. This is what makes one server different from another. Since
  738. we store password state in a CFtpSite object, we need to have one per
  739. user/password combo.
  740. SiteCache_PidlLookup() does the high level work of deciding if we want
  741. to do a password redirect. This function just wraps the creating of the
  742. lookup string and the fetching of the site.
  743. \*****************************************************************************/
  744. HRESULT SiteCache_PidlLookupPrivHelper(LPCITEMIDLIST pidl, IMalloc * pm, CFtpSite ** ppfs)
  745. {
  746. HRESULT hr = E_FAIL;
  747. TCHAR szLookup[MAX_PATH];
  748. *ppfs = NULL;
  749. hr = PidlGenerateSiteLookupStr(pidl, szLookup, ARRAYSIZE(szLookup));
  750. // May fail w/Outofmemory
  751. if (SUCCEEDED(hr))
  752. hr = SiteCache_PrivSearch((pidl ? szLookup : TEXT('\0')), pidl, pm, ppfs);
  753. ASSERT_POINTER_MATCHES_HRESULT(*ppfs, hr);
  754. return hr;
  755. }
  756. /*****************************************************************************\
  757. FUNCTION: SiteCache_PidlLookupPrivHelper
  758. DESCRIPTION:
  759. We cache information about an FTP Server to prevent hitting the net all
  760. the time. This state is stored in CFtpSite objects and we use 'lookup strings'
  761. to find them. This is what makes one server different from another. Since
  762. we store password state in a CFtpSite object, we need to have one per
  763. user/password combo.
  764. \*****************************************************************************/
  765. HRESULT SiteCache_PidlLookup(LPCITEMIDLIST pidl, BOOL fPasswordRedir, IMalloc * pm, CFtpSite ** ppfs)
  766. {
  767. HRESULT hr = E_FAIL;
  768. if (pidl && !ILIsEmpty(pidl))
  769. {
  770. hr = SiteCache_PidlLookupPrivHelper(pidl, pm, ppfs);
  771. // Okay, we found a site but we may need to redirect to another site
  772. // because the password is wrong. This happens if a user goes to
  773. // ServerA w/UserA and PasswordA but PasswordA is invalid. So,
  774. // PasswordB is entered and the navigation completes successfully.
  775. // Now either the navigation occurs again with PasswordA or w/o
  776. // a password (because the addrbar removes it), then we need to
  777. // look it up again and get it.
  778. if (SUCCEEDED(hr) && (*ppfs)->m_pszRedirPassword && fPasswordRedir)
  779. {
  780. LPITEMIDLIST pidlNew; // with new (redirected) password
  781. if (FtpPidl_IsAnonymous(pidl))
  782. {
  783. pidlNew = ILClone(pidl);
  784. if (!pidlNew)
  785. hr = E_OUTOFMEMORY;
  786. }
  787. else
  788. {
  789. // We need to redirect to get that CFtpSite.
  790. hr = PidlReplaceUserPassword(pidl, &pidlNew, pm, NULL, (*ppfs)->m_pszRedirPassword);
  791. }
  792. (*ppfs)->Release();
  793. *ppfs = NULL;
  794. if (SUCCEEDED(hr))
  795. {
  796. hr = SiteCache_PidlLookupPrivHelper(pidlNew, pm, ppfs);
  797. ILFree(pidlNew);
  798. }
  799. }
  800. }
  801. ASSERT_POINTER_MATCHES_HRESULT(*ppfs, hr);
  802. return hr;
  803. }
  804. /*****************************************************************************\
  805. FUNCTION: UpdateHiddenPassword
  806. DESCRIPTION:
  807. Since our IShellFolder::GetDisplayNameOf() will hide the password in some
  808. cases, we need to 'patch' display names that come thru our
  809. IShellFolder::GetDisplayName(). If a display name is coming in, we will
  810. see if the CFtpSite has a m_pszRedirPassword. If it did, then the user entered
  811. a password via the 'Login As...' dialog in place of the empty password,
  812. which made it hidden. If this is the case, we then have IShellFolder::ParseDisplayName()
  813. patch back in the password.
  814. \*****************************************************************************/
  815. HRESULT CFtpSite::UpdateHiddenPassword(LPITEMIDLIST pidl)
  816. {
  817. HRESULT hr = S_FALSE;
  818. TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH];
  819. TCHAR szPassword[INTERNET_MAX_PASSWORD_LENGTH];
  820. // Is it a candidate to a password to be inserted?
  821. if (m_pszPassword &&
  822. EVAL(SUCCEEDED(FtpPidl_GetUserName(pidl, szUserName, ARRAYSIZE(szUserName)))) &&
  823. szUserName[0] &&
  824. SUCCEEDED(FtpPidl_GetPassword(pidl, szPassword, ARRAYSIZE(szPassword), TRUE)) &&
  825. !szPassword[0])
  826. {
  827. // Yes...
  828. hr = FtpServerID_SetHiddenPassword(pidl, m_pszPassword);
  829. }
  830. return hr;
  831. }
  832. /*****************************************************************************\
  833. CFtpSite::GetFtpDir
  834. \*****************************************************************************/
  835. HRESULT CFtpSite::GetFtpDir(LPCTSTR pszServer, LPCWSTR pszUrlPath, CFtpDir ** ppfd)
  836. {
  837. HRESULT hr = S_OK;
  838. TCHAR szUrl[MAX_URL_STRING];
  839. *ppfd = NULL;
  840. hr = UrlCreate(pszServer, NULL, NULL, pszUrlPath, NULL, INTERNET_DEFAULT_FTP_PORT, NULL, szUrl, ARRAYSIZE(szUrl)); // Cannot fail on valid URLs.
  841. if (EVAL(SUCCEEDED(hr)))
  842. {
  843. LPITEMIDLIST pidl;
  844. // We know this is a path.
  845. hr = CreateFtpPidlFromUrlEx(szUrl, GetCWireEncoding(), NULL, &pidl, m_pm, FALSE, TRUE, TRUE); // Can fail in out of memory
  846. if (SUCCEEDED(hr))
  847. {
  848. hr = GetFtpDir(pidl, ppfd);
  849. ILFree(pidl);
  850. }
  851. }
  852. return hr;
  853. }
  854. /*****************************************************************************\
  855. FUNCTION: GetFtpDir
  856. DESCRIPTION:
  857. Obtain the FtpDir structure for an FTP site, creating one if
  858. necessary. It is the caller's responsibility to Release the
  859. FtpDir when finished.
  860. \*****************************************************************************/
  861. HRESULT CFtpSite::GetFtpDir(LPCITEMIDLIST pidl, CFtpDir ** ppfd)
  862. {
  863. HRESULT hr = S_OK;
  864. CFtpDir * pfd = NULL;
  865. ENTERCRITICAL;
  866. ASSERT(ppfd && m_FtpDirList);
  867. pfd = (CFtpDir *) m_FtpDirList->Find(_CompareDirs, (LPVOID) pidl);
  868. if (!pfd)
  869. {
  870. // We need to hold the critical section while setting up
  871. // the new FtpDir structure, lest somebody else come in
  872. // and try to create the same FtpDir while we are busy.
  873. hr = CFtpDir_Create(this, pidl, &pfd);
  874. if (SUCCEEDED(hr))
  875. {
  876. // NOTE: REF-COUNTING
  877. // Note that CFtpDir has a pointer (m_pfs) to a CFtpSite.
  878. // We just added a back pointer in CFtpSite's list of CFtpDir(s),
  879. // so it's necessary for that back pointer to not have a ref.
  880. // This will not be a problem because the back pointers will
  881. // always be valid because: 1) CFtpDir's destructor removes the backpointer,
  882. // and 2) CFtpDir holds a ref on CFtpSite, so it won't go away until
  883. // all the CFtpDir(s) are good and ready. -BryanSt
  884. hr = m_FtpDirList->AppendItem(pfd);
  885. if (FAILED(hr))
  886. IUnknown_Set(&pfd, NULL);
  887. }
  888. }
  889. LEAVECRITICAL;
  890. *ppfd = pfd;
  891. if (pfd)
  892. pfd->AddRef();
  893. return hr;
  894. }
  895. /*****************************************************************************\
  896. FUNCTION: FlushSubDirs
  897. DESCRIPTION:
  898. Every subdir of pidl is no longer valid so flush them. This is done
  899. because the parent dir may have changed names so they are invalid.
  900. PARAMETERS:
  901. pidl: Path of ItemIDs (no-ServerID) that includes the full path w/o
  902. the virtual root. This matches CFtpDir::m_pidlFtpDir
  903. \*****************************************************************************/
  904. HRESULT CFtpSite::FlushSubDirs(LPCITEMIDLIST pidl)
  905. {
  906. HRESULT hr = S_OK;
  907. CFtpDir * pfd = NULL;
  908. int nIndex;
  909. ENTERCRITICAL;
  910. // Count down so deleting items won't mess up the indicies.
  911. for (nIndex = (m_FtpDirList->GetCount() - 1); nIndex >= 0; nIndex--)
  912. {
  913. pfd = (CFtpDir *) m_FtpDirList->GetItemPtr(nIndex);
  914. if (pfd)
  915. {
  916. // Is this a child?
  917. if (FtpItemID_IsParent(pidl, pfd->GetPathPidlReference()))
  918. {
  919. // Yes, pfd is a child of pidl so delete it.
  920. m_FtpDirList->DeletePtrByIndex(nIndex);
  921. pfd->Release();
  922. }
  923. }
  924. }
  925. LEAVECRITICAL;
  926. return hr;
  927. }
  928. BOOL CFtpSite::IsSiteBlockedByRatings(HWND hwndDialogOwner)
  929. {
  930. if (!m_fRatingsChecked)
  931. {
  932. void * pvRatingDetails = NULL;
  933. TCHAR szUrl[MAX_URL_STRING];
  934. CHAR szUrlAnsi[MAX_URL_STRING];
  935. HRESULT hr = S_OK; // Assume allowed (in case no ratings)
  936. EVAL(SUCCEEDED(UrlCreateFromPidlW(m_pidl, SHGDN_FORPARSING, szUrl, ARRAYSIZE(szUrl), (ICU_ESCAPE | ICU_USERNAME), FALSE)));
  937. SHTCharToAnsi(szUrl, szUrlAnsi, ARRAYSIZE(szUrlAnsi));
  938. if (IS_RATINGS_ENABLED())
  939. {
  940. // S_OK - Allowed, S_FALSE - Not Allowed, FAILED() - not rated.
  941. hr = RatingCheckUserAccess(NULL, szUrlAnsi, NULL, NULL, 0, &pvRatingDetails);
  942. if (S_OK != hr) // Does user want to override with parent password in dialog?
  943. hr = RatingAccessDeniedDialog2(hwndDialogOwner, NULL, pvRatingDetails);
  944. if (pvRatingDetails)
  945. RatingFreeDetails(pvRatingDetails);
  946. }
  947. if (S_OK == hr) // It's off by default.
  948. m_fRatingsAllow = TRUE;
  949. m_fRatingsChecked = TRUE;
  950. }
  951. return !m_fRatingsAllow;
  952. }
  953. /*****************************************************************************\
  954. CFtpSite_Init
  955. Initialize the global list of FTP sites.
  956. Note that the DLL refcount is decremented after this is created,
  957. so that this internal list doesn't prevent us from unloading.
  958. \*****************************************************************************/
  959. HRESULT CFtpSite_Init(void)
  960. {
  961. HRESULT hr = S_OK;
  962. if (!g_FtpSiteCache)
  963. hr = CFtpList_Create(10, NULL, 10, &g_FtpSiteCache);
  964. return hr;
  965. }
  966. /*****************************************************************************\
  967. FtpSitePurge_CallBack
  968. Purge the global list of FTP sites.
  969. \*****************************************************************************/
  970. int FtpSitePurge_CallBack(LPVOID pvPunk, LPVOID pv)
  971. {
  972. IUnknown * punk = (IUnknown *) pvPunk;
  973. if (punk)
  974. punk->Release();
  975. return 1;
  976. }
  977. /*****************************************************************************\
  978. CFtpPunkList_Purge
  979. Purge the global list of FTP sites.
  980. \*****************************************************************************/
  981. HRESULT CFtpPunkList_Purge(CFtpList ** pfl)
  982. {
  983. TraceMsg(TF_FTP_DLLLOADING, "CFtpPunkList_Purge() Purging our cache.");
  984. if (*pfl)
  985. {
  986. (*pfl)->Enum(FtpSitePurge_CallBack, NULL);
  987. IUnknown_Set(pfl, NULL);
  988. }
  989. return S_OK;
  990. }
  991. /*****************************************************************************\
  992. CFtpSite_Create
  993. Create a brand new CFtpSite given a name.
  994. \*****************************************************************************/
  995. HRESULT CFtpSite_Create(LPCITEMIDLIST pidl, LPCTSTR pszLookupStr, IMalloc * pm, CFtpSite ** ppfs)
  996. {
  997. CFtpSite * pfs = new CFtpSite();
  998. HRESULT hr = E_OUTOFMEMORY;
  999. ASSERT(pidl && pszLookupStr && ppfs);
  1000. *ppfs = NULL;
  1001. if (pfs)
  1002. {
  1003. Str_SetPtr(&pfs->m_pszLookupStr, pszLookupStr);
  1004. IUnknown_Set((IUnknown **) &(pfs->m_pm), pm);
  1005. hr = CFtpList_Create(10, NULL, 10, &pfs->m_FtpDirList);
  1006. if (SUCCEEDED(hr))
  1007. {
  1008. // Did someone give us an empty URL?
  1009. if (EVAL(pidl) && EVAL(FtpPidl_IsValid(pidl)))
  1010. {
  1011. TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
  1012. TCHAR szUser[INTERNET_MAX_USER_NAME_LENGTH];
  1013. TCHAR szPassword[INTERNET_MAX_PASSWORD_LENGTH];
  1014. TCHAR szFragment[INTERNET_MAX_PASSWORD_LENGTH];
  1015. EVAL(SUCCEEDED(FtpPidl_GetServer(pidl, szServer, ARRAYSIZE(szServer))));
  1016. Str_SetPtr(&pfs->m_pszServer, szServer);
  1017. Pidl_Set(&pfs->m_pidl, pidl);
  1018. EVAL(SUCCEEDED(FtpPidl_GetUserName(pidl, szUser, ARRAYSIZE(szUser))));
  1019. Str_SetPtr(&pfs->m_pszUser, szUser);
  1020. if (FAILED(FtpPidl_GetPassword(pidl, szPassword, ARRAYSIZE(szPassword), TRUE)))
  1021. {
  1022. // Password expired
  1023. szPassword[0] = 0;
  1024. }
  1025. Str_SetPtr(&pfs->m_pszPassword, szPassword);
  1026. FtpPidl_GetFragment(pidl, szFragment, ARRAYSIZE(szFragment));
  1027. Str_SetPtr(&pfs->m_pszFragment, szFragment);
  1028. pfs->m_ipPortNum = FtpPidl_GetPortNum(pidl);
  1029. switch (FtpPidl_GetDownloadType(pidl))
  1030. {
  1031. case FTP_TRANSFER_TYPE_UNKNOWN:
  1032. pfs->m_fDLTypeSpecified = FALSE;
  1033. pfs->m_fASCIIDownload = FALSE;
  1034. break;
  1035. case FTP_TRANSFER_TYPE_ASCII:
  1036. pfs->m_fDLTypeSpecified = TRUE;
  1037. pfs->m_fASCIIDownload = TRUE;
  1038. break;
  1039. case FTP_TRANSFER_TYPE_BINARY:
  1040. pfs->m_fDLTypeSpecified = TRUE;
  1041. pfs->m_fASCIIDownload = FALSE;
  1042. break;
  1043. default:
  1044. ASSERT(0);
  1045. }
  1046. }
  1047. else
  1048. {
  1049. Str_SetPtr(&pfs->m_pszServer, NULL);
  1050. Str_SetPtr(&pfs->m_pszUser, NULL);
  1051. Str_SetPtr(&pfs->m_pszPassword, NULL);
  1052. Str_SetPtr(&pfs->m_pszFragment, NULL);
  1053. Pidl_Set(&pfs->m_pidl, NULL);
  1054. pfs->m_fDLTypeSpecified = FALSE;
  1055. }
  1056. *ppfs = pfs;
  1057. }
  1058. else
  1059. {
  1060. hr = E_FAIL;
  1061. pfs->Release();
  1062. }
  1063. }
  1064. return hr;
  1065. }
  1066. /****************************************************\
  1067. Constructor
  1068. \****************************************************/
  1069. CFtpSite::CFtpSite() : m_cRef(1)
  1070. {
  1071. DllAddRef();
  1072. // This needs to be allocated in Zero Inited Memory.
  1073. // Assert that all Member Variables are inited to Zero.
  1074. ASSERT(!m_pszServer);
  1075. ASSERT(!m_pidl);
  1076. ASSERT(!m_pszUser);
  1077. ASSERT(!m_pszPassword);
  1078. ASSERT(!m_pszFragment);
  1079. ASSERT(!m_pszLookupStr);
  1080. ASSERT(!m_pidlVirtualDir);
  1081. ASSERT(!m_fMotd);
  1082. ASSERT(!m_hint);
  1083. ASSERT(!m_hgti);
  1084. ASSERT(!m_FtpDirList);
  1085. ASSERT(!m_fRatingsChecked);
  1086. ASSERT(!m_fRatingsAllow);
  1087. LEAK_ADDREF(LEAK_CFtpSite);
  1088. }
  1089. /****************************************************\
  1090. Destructor
  1091. \****************************************************/
  1092. CFtpSite::~CFtpSite()
  1093. {
  1094. FlushHint(); // Frees m_hgti
  1095. Str_SetPtr(&m_pszServer, NULL);
  1096. Str_SetPtr(&m_pszUser, NULL);
  1097. Str_SetPtr(&m_pszPassword, NULL);
  1098. Str_SetPtr(&m_pszFragment, NULL);
  1099. Str_SetPtr(&m_pszLookupStr, NULL);
  1100. Str_SetPtr(&m_pszRedirPassword, NULL);
  1101. Pidl_Set(&m_pidlVirtualDir, NULL);
  1102. Pidl_Set(&m_pidl, NULL);
  1103. IUnknown_Set(&m_pfgMotd, NULL);
  1104. ASSERTCRITICAL;
  1105. CFtpPunkList_Purge(&m_FtpDirList);
  1106. TriggerDelayedAction(&m_hgti); // Out goes the cached handle
  1107. ASSERT(m_hint == 0); // Make sure he's gone
  1108. ATOMICRELEASE(m_pm);
  1109. DllRelease();
  1110. LEAK_DELREF(LEAK_CFtpSite);
  1111. }
  1112. //===========================
  1113. // *** IUnknown Interface ***
  1114. ULONG CFtpSite::AddRef()
  1115. {
  1116. m_cRef++;
  1117. return m_cRef;
  1118. }
  1119. ULONG CFtpSite::Release()
  1120. {
  1121. ASSERT(m_cRef > 0);
  1122. m_cRef--;
  1123. if (m_cRef > 0)
  1124. return m_cRef;
  1125. delete this;
  1126. return 0;
  1127. }
  1128. HRESULT CFtpSite::QueryInterface(REFIID riid, void **ppvObj)
  1129. {
  1130. if (IsEqualIID(riid, IID_IUnknown))
  1131. {
  1132. *ppvObj = SAFECAST(this, IUnknown*);
  1133. }
  1134. else
  1135. {
  1136. TraceMsg(TF_FTPQI, "CFtpSite::QueryInterface() failed.");
  1137. *ppvObj = NULL;
  1138. return E_NOINTERFACE;
  1139. }
  1140. AddRef();
  1141. return S_OK;
  1142. }