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.

1034 lines
36 KiB

  1. /*****************************************************************************\
  2. FILE: ftpapi.cpp
  3. DESCRIPTION:
  4. This file contains functions to perform the following 2 things:
  5. 1. WININET WRAPPERS: Wininet APIs have either wierd bugs or bugs that come thru the APIs
  6. from the server. It's also important to keep track of the perf impact
  7. of each call. These wrappers solve these problems.
  8. 2. FTP STRs to PIDLs: These wrappers will take ftp filenames and file paths
  9. that come in from the server and turn them into pidls. These pidls contain
  10. both a unicode display string and the filename/path in wire bytes for future
  11. server requests.
  12. \*****************************************************************************/
  13. #include "priv.h"
  14. #include "util.h"
  15. #include "encoding.h"
  16. #include "ftpapi.h"
  17. ///////////////////////////////////////////////////////////////////////////////////////////
  18. // 1. WININET WRAPPERS: Wininet APIs have either wierd bugs or bugs that come thru the APIs
  19. // from the server. It's also important to keep track of the perf impact
  20. // of each call. These wrappers solve these problems.
  21. ///////////////////////////////////////////////////////////////////////////////////////////
  22. /*****************************************************************************\
  23. FUNCTION: FtpSetCurrentDirectoryWrap
  24. DESCRIPTION:
  25. PERF Notes:
  26. [Direct Net Connection]
  27. To: shapitst <Down the Hall>: 1ms
  28. To: rigel.cyberpass.net <San Diego, CA>: 140ms - 200ms
  29. To: ftp.rz.uni-frankfurt.de <Germany>: 570ms - 2496ms
  30. \*****************************************************************************/
  31. HRESULT FtpSetCurrentDirectoryWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFtpPath)
  32. {
  33. HRESULT hr = S_OK;
  34. DWORD dwError = 0;
  35. // WARNING: FtpSetCurrentDirectory() may fail if it's not really a directory.
  36. // PERF: Status FtpGetCurrentDirectory/FtpSetCurrentDirectory() takes
  37. // 180-280ms on ftp.microsoft.com on average.
  38. // 500-2000ms on ftp://ftp.tu-clausthal.de/ on average
  39. // 0-10ms on ftp://shapitst/ on average
  40. DEBUG_CODE(DebugStartWatch());
  41. if (!FtpSetCurrentDirectoryA(hConnect, pwFtpPath))
  42. {
  43. dwError = GetLastError();
  44. hr = HRESULT_FROM_WIN32(dwError);
  45. }
  46. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpSetCurrentDirectory(%#08lx, \"%hs\") returned %u. Time=%lums", hConnect, pwFtpPath, dwError, DebugStopWatch()));
  47. if (fAssertOnFailure)
  48. {
  49. WININET_ASSERT(SUCCEEDED(hr));
  50. }
  51. return hr;
  52. }
  53. /*****************************************************************************\
  54. FUNCTION: FtpGetCurrentDirectoryWrap
  55. DESCRIPTION:
  56. PERF Notes:
  57. These are for short directory listings. Long listings can take 3-10x.
  58. [Direct Net Connection]
  59. To: shapitst <Down the Hall>: 1ms - 4ms
  60. To: rigel.cyberpass.net <San Diego, CA>: 132ms - 213ms
  61. To: ftp.rz.uni-frankfurt.de <Germany>: 507ms - 2012ms
  62. \*****************************************************************************/
  63. HRESULT FtpGetCurrentDirectoryWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPWIRESTR pwFtpPath, DWORD cchCurrentDirectory)
  64. {
  65. HRESULT hr = S_OK;
  66. DWORD dwError = 0;
  67. // PERF: Status FtpGetCurrentDirectory/FtpSetCurrentDirectory() takes
  68. // 180-280ms on ftp.microsoft.com on average.
  69. // 500-2000ms on ftp://ftp.tu-clausthal.de/ on average
  70. // 0-10ms on ftp://shapitst/ on average
  71. DEBUG_CODE(DebugStartWatch());
  72. if (!FtpGetCurrentDirectoryA(hConnect, pwFtpPath, &cchCurrentDirectory))
  73. {
  74. dwError = GetLastError();
  75. hr = HRESULT_FROM_WIN32(dwError);
  76. }
  77. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpGetCurrentDirectoryA(%#08lx, \"%hs\") returned %u. Time=%lums", hConnect, pwFtpPath, dwError, DebugStopWatch()));
  78. if (fAssertOnFailure)
  79. {
  80. WININET_ASSERT(SUCCEEDED(hr));
  81. }
  82. return hr;
  83. }
  84. /*****************************************************************************\
  85. FUNCTION: FtpGetFileExWrap
  86. DESCRIPTION:
  87. PERF Notes: (*** Depends on file size ***)
  88. [Direct Net Connection]
  89. To: shapitst <Down the Hall>: 100ms - 1,000ms+
  90. To: rigel.cyberpass.net <San Diego, CA>: 1210ms - 1610ms
  91. To: ftp.rz.uni-frankfurt.de <Germany>: ???????
  92. \*****************************************************************************/
  93. HRESULT FtpGetFileExWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFtpPath/*Src*/, LPCWSTR pwzFilePath/*Dest*/, BOOL fFailIfExists,
  94. DWORD dwFlagsAndAttributes, DWORD dwFlags, DWORD_PTR dwContext)
  95. {
  96. HRESULT hr = S_OK;
  97. DWORD dwError = 0;
  98. DEBUG_CODE(DebugStartWatch());
  99. if (!FtpGetFileEx(hConnect, pwFtpPath, pwzFilePath, fFailIfExists, dwFlagsAndAttributes, dwFlags, dwContext))
  100. {
  101. dwError = GetLastError();
  102. hr = HRESULT_FROM_WIN32(dwError);
  103. if (HRESULT_FROM_WIN32(ERROR_INTERNET_OPERATION_CANCELLED) == hr)
  104. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  105. }
  106. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpGetFileEx(%#08lx, \"%hs\", \"%ls\") returned %u. Time=%lums", hConnect, pwFtpPath, pwzFilePath, dwError, DebugStopWatch()));
  107. if (fAssertOnFailure)
  108. {
  109. WININET_ASSERT(SUCCEEDED(hr));
  110. }
  111. return hr;
  112. }
  113. /*****************************************************************************\
  114. FUNCTION: FtpPutFileExWrap
  115. DESCRIPTION:
  116. PERF Notes: (*** Depends on file size ***)
  117. [Direct Net Connection]
  118. To: shapitst <Down the Hall>: 194ms - 400ms+
  119. To: rigel.cyberpass.net <San Diego, CA>: 1662ms - 8454ms
  120. To: ftp.rz.uni-frankfurt.de <Germany>: ???????
  121. \*****************************************************************************/
  122. HRESULT FtpPutFileExWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWSTR pwzFilePath/*Src*/, LPCWIRESTR pwFtpPath/*Dest*/, DWORD dwFlags, DWORD_PTR dwContext)
  123. {
  124. HRESULT hr = S_OK;
  125. DWORD dwError = 0;
  126. DEBUG_CODE(DebugStartWatch());
  127. if (!FtpPutFileEx(hConnect, pwzFilePath, pwFtpPath, dwFlags, dwContext))
  128. {
  129. dwError = GetLastError();
  130. hr = HRESULT_FROM_WIN32(dwError);
  131. }
  132. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpPutFileEx(%#08lx, \"%ls\", \"%hs\") returned %u. Time=%lums", hConnect, pwzFilePath, pwFtpPath, dwError, DebugStopWatch()));
  133. if (fAssertOnFailure)
  134. {
  135. WININET_ASSERT(SUCCEEDED(hr));
  136. }
  137. return hr;
  138. }
  139. /*****************************************************************************\
  140. FUNCTION: FtpDeleteFileWrap
  141. DESCRIPTION:
  142. PERF Notes:
  143. [Direct Net Connection]
  144. To: shapitst <Down the Hall>: 4ms (22ms once in a while)
  145. To: rigel.cyberpass.net <San Diego, CA>: 175ms - 291ms
  146. To: ftp.rz.uni-frankfurt.de <Germany>: ???????
  147. \*****************************************************************************/
  148. HRESULT FtpDeleteFileWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFtpFileName)
  149. {
  150. HRESULT hr = S_OK;
  151. DWORD dwError = 0;
  152. DEBUG_CODE(DebugStartWatch());
  153. if (!FtpDeleteFileA(hConnect, pwFtpFileName))
  154. {
  155. dwError = GetLastError();
  156. hr = HRESULT_FROM_WIN32(dwError);
  157. }
  158. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpDeleteFile(%#08lx, \"%hs\") returned %u. Time=%lums", hConnect, pwFtpFileName, dwError, DebugStopWatch()));
  159. if (fAssertOnFailure)
  160. {
  161. WININET_ASSERT(SUCCEEDED(hr));
  162. }
  163. return hr;
  164. }
  165. /*****************************************************************************\
  166. FUNCTION: FtpRenameFileWrap
  167. DESCRIPTION:
  168. PERF Notes:
  169. [Direct Net Connection]
  170. To: shapitst <Down the Hall>: 4ms
  171. To: rigel.cyberpass.net <San Diego, CA>: 329ms - 446ms
  172. To: ftp.rz.uni-frankfurt.de <Germany>: ???????
  173. \*****************************************************************************/
  174. HRESULT FtpRenameFileWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFtpFileNameExisting, LPCWIRESTR pwFtpFileNameNew)
  175. {
  176. HRESULT hr = S_OK;
  177. DWORD dwError = 0;
  178. DEBUG_CODE(DebugStartWatch());
  179. if (!FtpRenameFileA(hConnect, pwFtpFileNameExisting, pwFtpFileNameNew))
  180. {
  181. dwError = GetLastError();
  182. hr = HRESULT_FROM_WIN32(dwError);
  183. }
  184. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpRenameFile(%#08lx, \"%hs\", \"%hs\") returned %u. Time=%lums", hConnect, pwFtpFileNameExisting, pwFtpFileNameNew, dwError, DebugStopWatch()));
  185. if (fAssertOnFailure)
  186. {
  187. WININET_ASSERT(SUCCEEDED(hr));
  188. }
  189. return hr;
  190. }
  191. /*****************************************************************************\
  192. FUNCTION: FtpOpenFileWrap
  193. DESCRIPTION:
  194. PERF Notes:
  195. [Direct Net Connection]
  196. To: shapitst <Down the Hall>: 2ms
  197. To: rigel.cyberpass.net <San Diego, CA>: 757ms - 817ms
  198. To: ftp.rz.uni-frankfurt.de <Germany>: 2112ms - 10026ms
  199. \*****************************************************************************/
  200. HRESULT FtpOpenFileWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFtpFileName, DWORD dwAccess, DWORD dwFlags, DWORD_PTR dwContext, HINTERNET * phFileHandle)
  201. {
  202. HRESULT hr = S_OK;
  203. DWORD dwError = 0;
  204. DEBUG_CODE(DebugStartWatch());
  205. *phFileHandle = FtpOpenFileA(hConnect, pwFtpFileName, dwAccess, dwFlags, dwContext);
  206. if (!*phFileHandle)
  207. {
  208. dwError = GetLastError();
  209. hr = HRESULT_FROM_WIN32(dwError);
  210. }
  211. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpOpenFile(%#08lx, \"%hs\") returned %u. Time=%lums", hConnect, pwFtpFileName, dwError, DebugStopWatch()));
  212. if (fAssertOnFailure)
  213. {
  214. WININET_ASSERT(SUCCEEDED(hr));
  215. }
  216. return hr;
  217. }
  218. /*****************************************************************************\
  219. FUNCTION: FtpCreateDirectoryWrap
  220. DESCRIPTION:
  221. PERF Notes:
  222. [Direct Net Connection]
  223. To: shapitst <Down the Hall>: 3ms
  224. To: rigel.cyberpass.net <San Diego, CA>: 210ms - 350ms
  225. To: ftp.rz.uni-frankfurt.de <Germany>: ???????
  226. \*****************************************************************************/
  227. HRESULT FtpCreateDirectoryWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFtpPath)
  228. {
  229. HRESULT hr = S_OK;
  230. DWORD dwError = 0;
  231. DEBUG_CODE(DebugStartWatch());
  232. if (!FtpCreateDirectoryA(hConnect, pwFtpPath))
  233. {
  234. dwError = GetLastError();
  235. hr = HRESULT_FROM_WIN32(dwError);
  236. }
  237. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpCreateDirectoryA(%#08lx, \"%hs\") returned %u. Time=%lums", hConnect, pwFtpPath, dwError, DebugStopWatch()));
  238. if (fAssertOnFailure)
  239. {
  240. WININET_ASSERT(SUCCEEDED(hr));
  241. }
  242. return hr;
  243. }
  244. /*****************************************************************************\
  245. FUNCTION: FtpRemoveDirectoryWrap
  246. DESCRIPTION:
  247. PERF Notes:
  248. [Direct Net Connection]
  249. To: shapitst <Down the Hall>: 2ms
  250. To: rigel.cyberpass.net <San Diego, CA>: 157ms - 227ms
  251. To: ftp.rz.uni-frankfurt.de <Germany>: ???????
  252. \*****************************************************************************/
  253. HRESULT FtpRemoveDirectoryWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFtpPath)
  254. {
  255. HRESULT hr = S_OK;
  256. DWORD dwError = 0;
  257. DEBUG_CODE(DebugStartWatch());
  258. if (!FtpRemoveDirectoryA(hConnect, pwFtpPath))
  259. {
  260. dwError = GetLastError();
  261. hr = HRESULT_FROM_WIN32(dwError);
  262. }
  263. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpRemoveDirectory(%#08lx, \"%hs\") returned %u. Time=%lums", hConnect, pwFtpPath, dwError, DebugStopWatch()));
  264. if (fAssertOnFailure)
  265. {
  266. WININET_ASSERT(SUCCEEDED(hr));
  267. }
  268. return hr;
  269. }
  270. /*****************************************************************************\
  271. FUNCTION: FtpFindFirstFileWrap
  272. DESCRIPTION:
  273. PERF Notes:
  274. [Direct Net Connection]
  275. To: shapitst <Down the Hall>: 166ms - 189ms
  276. To: rigel.cyberpass.net <San Diego, CA>: 550ms - 815ms
  277. To: ftp.rz.uni-frankfurt.de <Germany>: 1925ms - 11,390ms
  278. \*****************************************************************************/
  279. HRESULT FtpFindFirstFileWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFilterStr, LPFTP_FIND_DATA pwfd, DWORD dwINetFlags, DWORD_PTR dwContext, HINTERNET * phFindHandle)
  280. {
  281. HRESULT hr = S_OK;
  282. DWORD dwError = 0;
  283. DEBUG_CODE(StrCpyNA(pwfd->cFileName, "<Not Found>", ARRAYSIZE(pwfd->cFileName)));
  284. ASSERT(phFindHandle);
  285. DEBUG_CODE(DebugStartWatch());
  286. // _UNDOCUMENTED_: If you pass NULL as the second argument, it's the
  287. // same as passing TEXT("*.*"), but much faster.
  288. // PERF: Status
  289. // FtpFindFirstFile() takes 500-700ms on ftp.microsoft.com on average.
  290. // takes 2-10 secs on ftp://ftp.tu-clausthal.de/ on average
  291. // takes 150-250 secs on ftp://shapitst/ on average
  292. *phFindHandle = FtpFindFirstFileA(hConnect, pwFilterStr, pwfd, dwINetFlags, dwContext);
  293. if (!*phFindHandle)
  294. {
  295. dwError = GetLastError();
  296. hr = HRESULT_FROM_WIN32(dwError);
  297. }
  298. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpFindFirstFile(\"%hs\")==\"%hs\" atrb=%#08lx, hr=%#08lx, Time=%lums", EMPTYSTR_FOR_NULLA(pwFilterStr), pwfd->cFileName, pwfd->dwFileAttributes, hr, DebugStopWatch()));
  299. if (fAssertOnFailure)
  300. {
  301. // This fails in normal cases when we are checking if files exist.
  302. // WININET_ASSERT(SUCCEEDED(hr));
  303. }
  304. ASSERT_POINTER_MATCHES_HRESULT(*phFindHandle, hr);
  305. return hr;
  306. }
  307. // Do not localize this because it's always returned from the server in english.g
  308. #define SZ_FINDFIRSTFILE_FAILURESTR ": No such file or directory"
  309. /**************************************************************\
  310. FUNCTION: FtpDoesFileExist
  311. DESCRIPTION:
  312. IE #34868 is the fact that some FTP servers erronous
  313. results on find requests:
  314. [The file does not exist in the following cases]
  315. Request: "foo.txt" result: SUCCEEDED & "foo.txt: No such file or directory"
  316. Request: "foo bat.txt" result: SUCCEEDED & "foo: No such file or directory"
  317. \**************************************************************/
  318. HRESULT FtpDoesFileExist(HINTERNET hConnect, BOOL fAssertOnFailure, LPCWIRESTR pwFilterStr, LPFTP_FIND_DATA pwfd, DWORD dwINetFlags)
  319. {
  320. FTP_FIND_DATA wfd;
  321. HINTERNET hIntFind;
  322. HRESULT hr;
  323. if (!pwfd) // pwfd is optional
  324. pwfd = &wfd;
  325. // Some servers like "ftp://wired/" will fail to find "BVTBaby.gif" even
  326. // though it exists. It will find it if "BVTBaby.gif*" is used.
  327. // Wininet should fix this or implement this hack but they probably won't.
  328. WIRECHAR wFilterStr[MAX_PATH];
  329. StrCpyNA(wFilterStr, pwFilterStr, ARRAYSIZE(wFilterStr));
  330. // WININET WORK AROUND: Wininet won't find "BVTBaby.gif" on an IIS server (ftp://wired/)
  331. // unless it has an "*" behind it. So add one if it doesn't exist.
  332. if ('*' != wFilterStr[lstrlenA(wFilterStr) - 1])
  333. {
  334. // We need to add it.
  335. StrCatBuffA(wFilterStr, "*", ARRAYSIZE(wFilterStr));
  336. }
  337. hr = FtpFindFirstFileWrap(hConnect, fAssertOnFailure, wFilterStr, pwfd, dwINetFlags, 0, &hIntFind);
  338. if (S_OK == hr)
  339. {
  340. do
  341. {
  342. // is it an exact match?
  343. // #248535: Make sure we get what we asked for. Either WININET or
  344. // some weird FTP servers are screwing up. If we ask for
  345. // foobar.gif as the filter string, sometimes we get back
  346. // ".".
  347. if (!StrCmpIA(pwfd->cFileName, pwFilterStr))
  348. {
  349. // Yes it "Should"
  350. hr = S_OK;
  351. break;
  352. }
  353. else
  354. {
  355. // However, wininet will return TRUE but the display name will be "One: No such file or directory"
  356. // if the file name is "One Two.htm"
  357. // This is a work around for bug #34868 because UNIX servers sometimes return success
  358. // and a file name of "thefile.txt: No such file or directory"
  359. if ((lstrlenA(pwfd->cFileName) > (ARRAYSIZE(SZ_FINDFIRSTFILE_FAILURESTR) - 1)) &&
  360. !StrCmpA(&(pwfd->cFileName[lstrlenA(pwfd->cFileName) - (ARRAYSIZE(SZ_FINDFIRSTFILE_FAILURESTR) - 1)]), SZ_FINDFIRSTFILE_FAILURESTR))
  361. {
  362. hr = S_OK;
  363. break;
  364. }
  365. else
  366. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  367. }
  368. // Next...
  369. hr = InternetFindNextFileWrap(hIntFind, TRUE, pwfd);
  370. }
  371. while (S_OK == hr);
  372. InternetCloseHandle(hIntFind);
  373. }
  374. return hr;
  375. }
  376. /*****************************************************************************\
  377. FUNCTION: FtpCommandWrap
  378. DESCRIPTION:
  379. PERF Notes:
  380. [Direct Net Connection]
  381. To: shapitst <Down the Hall>: 1ms - 12ms
  382. To: rigel.cyberpass.net <San Diego, CA>: 133ms - 184ms
  383. To: ftp.rz.uni-frankfurt.de <Germany>: 1711ms - 2000ms
  384. \*****************************************************************************/
  385. HRESULT FtpCommandWrap(HINTERNET hConnect, BOOL fAssertOnFailure, BOOL fExpectResponse, DWORD dwFlags, LPCWIRESTR pszCommand,
  386. DWORD_PTR dwContext, HINTERNET *phFtpCommand)
  387. {
  388. HRESULT hr = S_OK;
  389. DWORD dwError = 0;
  390. DEBUG_CODE(DebugStartWatch());
  391. if (!FtpCommandA(hConnect, fExpectResponse, dwFlags, pszCommand, dwContext, phFtpCommand))
  392. {
  393. dwError = GetLastError();
  394. hr = HRESULT_FROM_WIN32(dwError);
  395. }
  396. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "FtpCommand(%#08lx, \"%hs\") returned %u. Time=%lums", hConnect, pszCommand, dwError, DebugStopWatch()));
  397. if (fAssertOnFailure)
  398. {
  399. WININET_ASSERT(SUCCEEDED(hr));
  400. }
  401. return hr;
  402. }
  403. /*** TODO
  404. INTERNETAPI
  405. DWORD
  406. WINAPI
  407. FtpGetFileSize(
  408. IN HINTERNET hFile,
  409. OUT LPDWORD lpdwFileSizeHigh OPTIONAL
  410. );
  411. ******/
  412. /*****************************************************************************\
  413. FUNCTION: InternetOpenWrap
  414. DESCRIPTION:
  415. PERF Notes:
  416. [Direct Net Connection]
  417. Destination not applicable. 677-907ms
  418. \*****************************************************************************/
  419. HRESULT InternetOpenWrap(BOOL fAssertOnFailure, LPCTSTR pszAgent, DWORD dwAccessType, LPCTSTR pszProxy, LPCTSTR pszProxyBypass, DWORD dwFlags, HINTERNET * phFileHandle)
  420. {
  421. HRESULT hr = S_OK;
  422. DWORD dwError = 0;
  423. DEBUG_CODE(DebugStartWatch());
  424. *phFileHandle = InternetOpen(pszAgent, dwAccessType, pszProxy, pszProxyBypass, dwFlags);
  425. if (!*phFileHandle)
  426. {
  427. dwError = GetLastError();
  428. hr = HRESULT_FROM_WIN32(dwError);
  429. }
  430. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetOpen(\"%ls\") returned %u. Time=%lums", pszAgent, dwError, DebugStopWatch()));
  431. if (fAssertOnFailure)
  432. {
  433. WININET_ASSERT(SUCCEEDED(hr));
  434. }
  435. return hr;
  436. }
  437. HRESULT InternetCloseHandleWrap(HINTERNET hInternet, BOOL fAssertOnFailure)
  438. {
  439. HRESULT hr = S_OK;
  440. DWORD dwError = 0;
  441. DEBUG_CODE(DebugStartWatch());
  442. if (!InternetCloseHandle(hInternet))
  443. {
  444. dwError = GetLastError();
  445. hr = HRESULT_FROM_WIN32(dwError);
  446. }
  447. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetCloseHandle(%#08lx) returned %u. Time=%lums", hInternet, dwError, DebugStopWatch()));
  448. if (fAssertOnFailure)
  449. {
  450. WININET_ASSERT(SUCCEEDED(hr));
  451. }
  452. return hr;
  453. }
  454. /*****************************************************************************\
  455. FUNCTION: _GetPASVMode()
  456. DESCRIPTION:
  457. Check for Reg Key {SZ_REGKEY_FTPFOLDER}\{SZ_REGVALUE_FTP_PASV}. If the
  458. key exists, then use INTERNET_FLAG_PASSIVE in calls to wininet APIs.
  459. \*****************************************************************************/
  460. DWORD _GetPASVMode()
  461. {
  462. DWORD dwPASVflag = -1;
  463. // We don't cache this value because we don't want to require
  464. // people to restart the process to pick up any changes. This
  465. // isn't a big deal since we are going to hit the net which is
  466. // a million times slower than the registery.
  467. //
  468. // This is why PASV is needed:
  469. // Mill #120818: FTP either uses PORT or PASV but only one. We are guaranteed that some
  470. // users will have firewalls, switches, or routers that will be incompatible in one of the
  471. // methods and support the other. Since there isn't any way for us to take care of this
  472. // automatically, we need to give the user the option to choose. We default to PORT
  473. // since that is the most compatible (MS Proxy, and others). The user can use the
  474. // Advanced Tab of the Internet Control Panel to switch. PASV will work on US West
  475. // DSL modems for example.
  476. // Give users the options of using PASV
  477. if (SHRegGetBoolUSValue(SZ_REGKEY_FTPFOLDER, SZ_REGVALUE_FTP_PASV, FALSE, FALSE))
  478. {
  479. dwPASVflag = INTERNET_FLAG_PASSIVE;
  480. }
  481. else
  482. {
  483. dwPASVflag = NULL;
  484. }
  485. return dwPASVflag;
  486. }
  487. /*****************************************************************************\
  488. FUNCTION: InternetConnectWrap
  489. DESCRIPTION:
  490. PERF Notes:
  491. [Direct Net Connection]
  492. To: shapitst <Down the Hall>: 144ms - 250ms (Min: 2; Max: 1,667ms)
  493. To: rigel.cyberpass.net <San Diego, CA>: 717ms - 1006ms
  494. To: ftp.rz.uni-frankfurt.de <Germany>: 2609ms - 14,012ms
  495. COMMON ERROR VALUES:
  496. These are the return values in these different cases:
  497. ERROR_INTERNET_NAME_NOT_RESOLVED: No Proxy & DNS Lookup failed.
  498. ERROR_INTERNET_CANNOT_CONNECT: Some Auth Proxies and Netscape's Web/Auth Proxy
  499. ERROR_INTERNET_NAME_NOT_RESOLVED: Web Proxy
  500. ERROR_INTERNET_TIMEOUT: Invalid or Web Proxy blocked IP Address
  501. ERROR_INTERNET_INCORRECT_PASSWORD: IIS & UNIX, UserName may not exist or password for the user may be incorrect on.
  502. ERROR_INTERNET_LOGIN_FAILURE: Too many Users on IIS.
  503. ERROR_INTERNET_INCORRECT_USER_NAME: I haven't seen it.
  504. ERROR_INTERNET_EXTENDED_ERROR: yahoo.com exists, but ftp.yahoo.com doesn't.
  505. \*****************************************************************************/
  506. HRESULT InternetConnectWrap(HINTERNET hInternet, BOOL fAssertOnFailure, LPCTSTR pszServerName, INTERNET_PORT nServerPort,
  507. LPCTSTR pszUserName, LPCTSTR pszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext, HINTERNET * phFileHandle)
  508. {
  509. HRESULT hr = S_OK;
  510. DWORD dwError = 0;
  511. // Call BryanSt if this assert fires.
  512. // Did the user turn off FTP Folders?
  513. // If so, don't connect. This will fix NT #406423 where the user turned
  514. // of FTP Folders because they have a firewall (CISCO filtering Router)
  515. // that will kill packets in such a way the caller (WinSock/Wininet) needs
  516. // to wait for a timeout. During this timeout, the browser will hang causing
  517. // the user to think it crashed.
  518. AssertMsg(!SHRegGetBoolUSValue(SZ_REGKEY_FTPFOLDER, SZ_REGKEY_USE_OLD_UI, FALSE, FALSE), TEXT("BUG: We can't hit this code or we will hang the browser for 45 seconds if the user is using a certain kind of proxy. Call BryanSt."));
  519. DEBUG_CODE(DebugStartWatch());
  520. *phFileHandle = InternetConnect(hInternet, pszServerName, nServerPort, pszUserName, pszPassword, dwService, dwFlags | _GetPASVMode(), dwContext);
  521. if (!*phFileHandle)
  522. {
  523. dwError = GetLastError();
  524. hr = HRESULT_FROM_WIN32(dwError);
  525. }
  526. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetConnect(%#08lx, \"%ls\", \"%ls\", \"%ls\") returned %u. Time=%lums", hInternet, pszServerName, EMPTYSTR_FOR_NULL(pszUserName), EMPTYSTR_FOR_NULL(pszPassword), dwError, DebugStopWatch()));
  527. if (fAssertOnFailure)
  528. {
  529. // ERROR_INTERNET_NAME_NOT_RESOLVED happens when we are blocked by the
  530. // proxy.
  531. WININET_ASSERT(SUCCEEDED(hr) ||
  532. (HRESULT_FROM_WIN32(ERROR_INTERNET_NAME_NOT_RESOLVED) == hr) ||
  533. (HRESULT_FROM_WIN32(ERROR_INTERNET_LOGIN_FAILURE) == hr) ||
  534. (HRESULT_FROM_WIN32(ERROR_INTERNET_INCORRECT_PASSWORD) == hr) ||
  535. (HRESULT_FROM_WIN32(ERROR_INTERNET_INCORRECT_USER_NAME) == hr));
  536. }
  537. return hr;
  538. }
  539. /*****************************************************************************\
  540. FUNCTION: InternetOpenUrlWrap
  541. DESCRIPTION:
  542. PERF Notes:
  543. [Direct Net Connection]
  544. To: shapitst <Down the Hall>: 29ms
  545. To: rigel.cyberpass.net <San Diego, CA>: ???????
  546. To: ftp.rz.uni-frankfurt.de <Germany>: ???????
  547. \*****************************************************************************/
  548. HRESULT InternetOpenUrlWrap(HINTERNET hInternet, BOOL fAssertOnFailure, LPCTSTR pszUrl, LPCTSTR pszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD_PTR dwContext, HINTERNET * phFileHandle)
  549. {
  550. HRESULT hr = S_OK;
  551. DWORD dwError = 0;
  552. DEBUG_CODE(DebugStartWatch());
  553. *phFileHandle = InternetOpenUrl(hInternet, pszUrl, pszHeaders, dwHeadersLength, dwFlags | _GetPASVMode(), dwContext);
  554. if (!*phFileHandle)
  555. {
  556. dwError = GetLastError();
  557. hr = HRESULT_FROM_WIN32(dwError);
  558. }
  559. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetOpenUrl(%#08lx, \"%ls\") returned %u. Time=%lums", hInternet, pszUrl, dwError, DebugStopWatch()));
  560. if (fAssertOnFailure)
  561. {
  562. WININET_ASSERT(SUCCEEDED(hr));
  563. }
  564. return hr;
  565. }
  566. HRESULT InternetReadFileWrap(HINTERNET hFile, BOOL fAssertOnFailure, LPVOID pvBuffer, DWORD dwNumberOfBytesToRead, LPDWORD pdwNumberOfBytesRead)
  567. {
  568. HRESULT hr = S_OK;
  569. DWORD dwError = 0;
  570. // DEBUG_CODE(DebugStartWatch());
  571. if (!InternetReadFile(hFile, pvBuffer, dwNumberOfBytesToRead, pdwNumberOfBytesRead))
  572. {
  573. dwError = GetLastError();
  574. hr = HRESULT_FROM_WIN32(dwError);
  575. }
  576. // DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetReadFile(%#08lx, ToRead=%d, Read=%d) returned %u. Time=%lums", hFile, dwNumberOfBytesToRead, (pdwNumberOfBytesRead ? *pdwNumberOfBytesRead : -1), dwError, DebugStopWatch()));
  577. if (fAssertOnFailure)
  578. {
  579. WININET_ASSERT(SUCCEEDED(hr));
  580. }
  581. return hr;
  582. }
  583. HRESULT InternetWriteFileWrap(HINTERNET hFile, BOOL fAssertOnFailure, LPCVOID pvBuffer, DWORD dwNumberOfBytesToWrite, LPDWORD pdwNumberOfBytesWritten)
  584. {
  585. HRESULT hr = S_OK;
  586. DWORD dwError = 0;
  587. // DEBUG_CODE(DebugStartWatch());
  588. if (!InternetWriteFile(hFile, pvBuffer, dwNumberOfBytesToWrite, pdwNumberOfBytesWritten))
  589. {
  590. dwError = GetLastError();
  591. hr = HRESULT_FROM_WIN32(dwError);
  592. }
  593. // DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetWriteFile(%#08lx, ToWrite=%d, Writen=%d) returned %u. Time=%lums", hFile, dwNumberOfBytesToWrite, (pdwNumberOfBytesWritten ? *pdwNumberOfBytesWritten : -1), dwError, DebugStopWatch()));
  594. if (fAssertOnFailure)
  595. {
  596. WININET_ASSERT(SUCCEEDED(hr));
  597. }
  598. return hr;
  599. }
  600. /*****************************************************************************\
  601. FUNCTION: InternetGetLastResponseInfoWrap
  602. DESCRIPTION:
  603. PERF Notes:
  604. Always takes 0 (zero) ms because it doesn't have to hit the net.
  605. \*****************************************************************************/
  606. HRESULT InternetGetLastResponseInfoWrap(BOOL fAssertOnFailure, LPDWORD pdwError, LPWIRESTR pwBuffer, LPDWORD pdwBufferLength)
  607. {
  608. HRESULT hr = S_OK;
  609. DWORD dwDummyError;
  610. if (!pdwError)
  611. pdwError = &dwDummyError;
  612. if (pwBuffer)
  613. pwBuffer[0] = 0;
  614. DEBUG_CODE(DebugStartWatch());
  615. InternetGetLastResponseInfoA(pdwError, pwBuffer, pdwBufferLength);
  616. if (pwBuffer)
  617. {
  618. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetGetLastResponseInfo(\"%hs\") took %lu milliseconds", pwBuffer, DebugStopWatch()));
  619. }
  620. else
  621. {
  622. DEBUG_CODE(DebugStopWatch());
  623. }
  624. if (fAssertOnFailure)
  625. {
  626. WININET_ASSERT(SUCCEEDED(hr));
  627. }
  628. return hr;
  629. }
  630. HRESULT InternetGetLastResponseInfoDisplayWrap(BOOL fAssertOnFailure, LPDWORD pdwError, LPWSTR pwzBuffer, DWORD cchBufferSize)
  631. {
  632. LPWIRESTR pwWireResponse;
  633. DWORD dwError = 0;
  634. DWORD cchResponse = 0;
  635. HRESULT hr = InternetGetLastResponseInfoWrap(TRUE, &dwError, NULL, &cchResponse);
  636. cchResponse++; /* +1 for the terminating 0 */
  637. pwWireResponse = (LPWIRESTR)LocalAlloc(LPTR, cchResponse * sizeof(WIRECHAR));
  638. if (pwWireResponse)
  639. {
  640. hr = InternetGetLastResponseInfoWrap(TRUE, &dwError, pwWireResponse, &cchResponse);
  641. if (SUCCEEDED(hr))
  642. {
  643. CWireEncoding cWireEncoding;
  644. hr = cWireEncoding.WireBytesToUnicode(NULL, pwWireResponse, WIREENC_IMPROVE_ACCURACY, pwzBuffer, cchBufferSize);
  645. }
  646. LocalFree(pwWireResponse);
  647. }
  648. else
  649. hr = E_OUTOFMEMORY;
  650. return hr;
  651. }
  652. INTERNET_STATUS_CALLBACK InternetSetStatusCallbackWrap(HINTERNET hInternet, BOOL fAssertOnFailure, INTERNET_STATUS_CALLBACK pfnInternetCallback)
  653. {
  654. HRESULT hr = S_OK;
  655. DWORD dwError = 0;
  656. INTERNET_STATUS_CALLBACK pfnCallBack;
  657. DEBUG_CODE(DebugStartWatch());
  658. pfnCallBack = InternetSetStatusCallback(hInternet, pfnInternetCallback);
  659. if (!pfnCallBack)
  660. {
  661. dwError = GetLastError();
  662. hr = HRESULT_FROM_WIN32(dwError);
  663. }
  664. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetSetStatusCallback(%#08lx) returned %u. Time=%lums", hInternet, dwError, DebugStopWatch()));
  665. if (fAssertOnFailure)
  666. {
  667. WININET_ASSERT(SUCCEEDED(hr));
  668. }
  669. return pfnCallBack;
  670. }
  671. HRESULT InternetCheckConnectionWrap(BOOL fAssertOnFailure, LPCTSTR pszUrl, DWORD dwFlags, DWORD dwReserved)
  672. {
  673. HRESULT hr = S_OK;
  674. DWORD dwError = 0;
  675. DEBUG_CODE(DebugStartWatch());
  676. if (!InternetCheckConnection(pszUrl, dwFlags, dwReserved))
  677. {
  678. dwError = GetLastError();
  679. hr = HRESULT_FROM_WIN32(dwError);
  680. }
  681. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetCheckConnection(\"%ls\") returned %u. Time=%lums", pszUrl, dwError, DebugStopWatch()));
  682. if (fAssertOnFailure)
  683. {
  684. WININET_ASSERT(SUCCEEDED(hr));
  685. }
  686. return hr;
  687. }
  688. //#define FEATURE_OFFLINE
  689. HRESULT InternetAttemptConnectWrap(BOOL fAssertOnFailure, DWORD dwReserved)
  690. {
  691. HRESULT hr = S_OK;
  692. #ifdef FEATURE_OFFLINE
  693. DEBUG_CODE(DebugStartWatch());
  694. hr = HRESULT_FROM_WIN32(InternetAttemptConnect(dwReserved));
  695. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetAttemptConnect() returned hr=%#08lx. Time=%lums", hr, DebugStopWatch()));
  696. if (fAssertOnFailure)
  697. {
  698. WININET_ASSERT(SUCCEEDED(hr));
  699. }
  700. #endif // FEATURE_OFFLINE
  701. return hr;
  702. }
  703. /*****************************************************************************\
  704. FUNCTION: InternetFindNextFileWrap
  705. DESCRIPTION:
  706. PERF Notes:
  707. Always takes 0 (zero) ms because all the work is done in FtpFindFirstFile()
  708. \*****************************************************************************/
  709. HRESULT InternetFindNextFileWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPFTP_FIND_DATA pwfd)
  710. {
  711. HRESULT hr = S_OK;
  712. DWORD dwError = 0;
  713. DEBUG_CODE(StrCpyNA(pwfd->cFileName, "<Not Found>", ARRAYSIZE(pwfd->cFileName)));
  714. DEBUG_CODE(DebugStartWatch());
  715. // Bug #206068 Fix Info:
  716. // We need to treat dwFileAttributes = 0x00000000 as a directory
  717. // link. We can do this by FtpChangeDirectory() into it, call FtpGetDirectory(),
  718. // and then creating a pidl with UrlPath and navigating to it w/o creating history entry if needed.
  719. // This will solve the problem that going to ftp://ftp.cdrom.com/pub/ and clicking
  720. // on any of the soft links will change into that directory and update the address
  721. // bar to show the real destination directory.
  722. //
  723. // We correctly support SoftLinks within the virtual directory but not out of it.
  724. // PERF: The perf of this function normally is nothing because the enum of the entire directory
  725. // is done in the FtpFindFirstFile(). It will also cache the results.
  726. if (!InternetFindNextFileA(hConnect, pwfd))
  727. {
  728. dwError = GetLastError();
  729. hr = HRESULT_FROM_WIN32(dwError);
  730. }
  731. DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "InternetFindNextFile(%#08lx)==\"%hs\", atrbs=%#08lx, hr=%#08lx, Time=%lums",
  732. hConnect, pwfd->cFileName, pwfd->dwFileAttributes, hr, DebugStopWatch()));
  733. if (fAssertOnFailure && (HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES) != hr))
  734. {
  735. WININET_ASSERT(SUCCEEDED(hr));
  736. }
  737. return hr;
  738. }
  739. ///////////////////////////////////////////////////////////////////////////////////////////
  740. // 2. FTP STRs to PIDLs: These wrappers will take ftp filenames and file paths
  741. // that come in from the server and turn them into pidls. These pidls contain
  742. // both a unicode display string and the filename/path in wire bytes for future
  743. // server requests.
  744. ///////////////////////////////////////////////////////////////////////////////////////////
  745. /*****************************************************************************\
  746. FUNCTION: FtpSetCurrentDirectoryPidlWrap
  747. DESCRIPTION:
  748. Change the current directory to the one specified.
  749. PARAMETERS:
  750. pidlFtpPath: If this is NULL, then go to "\".
  751. \*****************************************************************************/
  752. HRESULT FtpSetCurrentDirectoryPidlWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCITEMIDLIST pidlFtpPath, BOOL fAbsolute, BOOL fOnlyDirs)
  753. {
  754. WIRECHAR wFtpPath[MAX_PATH];
  755. LPWIRESTR pwFtpPath = wFtpPath;
  756. HRESULT hr = S_OK;
  757. // If pidlFtpPath is NULL, then go to "\".
  758. if (pidlFtpPath)
  759. {
  760. hr = GetWirePathFromPidl(pidlFtpPath, wFtpPath, ARRAYSIZE(wFtpPath), fOnlyDirs);
  761. if (!fAbsolute)
  762. pwFtpPath++; // Skip past the starting '\'
  763. }
  764. else
  765. StrCpyNA(wFtpPath, SZ_URL_SLASHA, ARRAYSIZE(wFtpPath));
  766. if (SUCCEEDED(hr))
  767. hr = FtpSetCurrentDirectoryWrap(hConnect, fAssertOnFailure, pwFtpPath);
  768. return hr;
  769. }
  770. HRESULT FtpGetCurrentDirectoryPidlWrap(HINTERNET hConnect, BOOL fAssertOnFailure, CWireEncoding * pwe, LPITEMIDLIST * ppidlFtpPath)
  771. {
  772. WIRECHAR wFtpPath[MAX_PATH];
  773. HRESULT hr = FtpGetCurrentDirectoryWrap(hConnect, fAssertOnFailure, wFtpPath, ARRAYSIZE(wFtpPath));
  774. *ppidlFtpPath = NULL;
  775. if (SUCCEEDED(hr))
  776. hr = CreateFtpPidlFromFtpWirePath(wFtpPath, pwe, NULL, ppidlFtpPath, TRUE, TRUE);
  777. return hr;
  778. }
  779. HRESULT FtpFindFirstFilePidlWrap(HINTERNET hConnect, BOOL fAssertOnFailure, CMultiLanguageCache * pmlc,
  780. CWireEncoding * pwe, LPCWIRESTR pwFilterStr, LPITEMIDLIST * ppidlFtpItem, DWORD dwINetFlags, DWORD_PTR dwContext, HINTERNET * phFindHandle)
  781. {
  782. FTP_FIND_DATA wfd;
  783. *phFindHandle = NULL;
  784. HRESULT hr = FtpFindFirstFileWrap(hConnect, fAssertOnFailure, pwFilterStr, &wfd, dwINetFlags, dwContext, phFindHandle);
  785. *ppidlFtpItem = NULL;
  786. if (SUCCEEDED(hr))
  787. {
  788. // Skip "." and ".." entries.
  789. if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || IS_VALID_FILE(wfd.cFileName))
  790. hr = pwe->CreateFtpItemID(pmlc, &wfd, ppidlFtpItem);
  791. else
  792. hr = InternetFindNextFilePidlWrap(*phFindHandle, fAssertOnFailure, pmlc, pwe, ppidlFtpItem);
  793. if (FAILED(hr) && *phFindHandle)
  794. {
  795. InternetCloseHandle(*phFindHandle);
  796. *phFindHandle = NULL;
  797. }
  798. }
  799. return hr;
  800. }
  801. HRESULT InternetFindNextFilePidlWrap(HINTERNET hConnect, BOOL fAssertOnFailure, CMultiLanguageCache * pmlc, CWireEncoding * pwe, LPITEMIDLIST * ppidlFtpItem)
  802. {
  803. FTP_FIND_DATA wfd;
  804. HRESULT hr = InternetFindNextFileWrap(hConnect, fAssertOnFailure, &wfd);
  805. *ppidlFtpItem = NULL;
  806. if (SUCCEEDED(hr))
  807. {
  808. ASSERT(pmlc); // We use this often enought that this might as well exist.
  809. // Skip "." and ".." entries.
  810. if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || IS_VALID_FILE(wfd.cFileName))
  811. hr = pwe->CreateFtpItemID(pmlc, &wfd, ppidlFtpItem);
  812. else
  813. hr = InternetFindNextFilePidlWrap(hConnect, fAssertOnFailure, pmlc, pwe, ppidlFtpItem);
  814. }
  815. return hr;
  816. }
  817. HRESULT FtpRenameFilePidlWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCITEMIDLIST pidlExisting, LPCITEMIDLIST pidlNew)
  818. {
  819. return FtpRenameFileWrap(hConnect, fAssertOnFailure, FtpPidl_GetLastItemWireName(pidlExisting), FtpPidl_GetLastItemWireName(pidlNew));
  820. }
  821. HRESULT FtpGetFileExPidlWrap(HINTERNET hConnect, BOOL fAssertOnFailure, LPCITEMIDLIST pidlFtpPath/*Src*/, LPCWSTR pwzFilePath/*Dest*/, BOOL fFailIfExists,
  822. DWORD dwFlagsAndAttributes, DWORD dwFlags, DWORD_PTR dwContext)
  823. {
  824. return FtpGetFileExWrap(hConnect, fAssertOnFailure, FtpPidl_GetLastItemWireName(pidlFtpPath), pwzFilePath, fFailIfExists, dwFlagsAndAttributes, dwFlags, dwContext);
  825. }