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.

4666 lines
118 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. ftpapia.cxx
  5. Abstract:
  6. ANSI versions of Windows Internet Client DLL FTP APIs
  7. Contents:
  8. FtpFindFirstFileA
  9. FtpGetFileA
  10. FtpPutFileA
  11. FtpDeleteFileA
  12. FtpRenameFileA
  13. FtpOpenFileA
  14. FtpCreateDirectoryA
  15. FtpRemoveDirectoryA
  16. FtpSetCurrentDirectoryA
  17. FtpGetCurrentDirectoryA
  18. FtpCommandA
  19. FtpGetFileSize
  20. FtpGetSystemNameA
  21. FtpFindNextFileA
  22. FtpReadFile
  23. FtpWriteFile
  24. pFtpGetUrlString
  25. Author:
  26. Heath Hunnicutt [t-heathh] 13-Jul-1994
  27. Environment:
  28. Win32 user-level DLL
  29. Revision History:
  30. 09-Mar-1995 rfirth
  31. moved from findfile.c, ftphelp.c
  32. 13-Jul-1994 t-heathh
  33. Created
  34. --*/
  35. #include <wininetp.h>
  36. #include "ftpapih.h"
  37. //
  38. // manifests
  39. //
  40. #define DEFAULT_TRANSFER_BUFFER_LENGTH (4 K)
  41. #define ALLOWED_FTP_FLAGS (INTERNET_FLAGS_MASK \
  42. | FTP_TRANSFER_TYPE_MASK \
  43. )
  44. //
  45. // private prototypes
  46. //
  47. PRIVATE
  48. BOOL
  49. FBeginCacheReadProcessing(
  50. IN HINTERNET hFtpSession,
  51. IN LPCSTR lpszFileName,
  52. IN DWORD dwAccess,
  53. IN DWORD dwFlags,
  54. IN DWORD_PTR dwContext,
  55. IN BOOL fIsHtmlFind
  56. );
  57. PRIVATE
  58. BOOL
  59. FFtpCanReadFromCache(
  60. IN HINTERNET hFtpSession
  61. );
  62. PRIVATE
  63. BOOL
  64. FBeginCacheWriteProcessing(
  65. IN HINTERNET hFtpSession,
  66. IN LPCSTR lpszFileName,
  67. IN DWORD dwAccess,
  68. IN DWORD dwFlags,
  69. IN DWORD_PTR dwContext,
  70. IN BOOL fIsHtmlFind
  71. );
  72. PRIVATE
  73. BOOL
  74. FFtpCanWriteToCache(
  75. HINTERNET hFtpSession
  76. );
  77. DWORD
  78. InbLocalEndCacheWrite(
  79. IN HINTERNET hFtpFile,
  80. IN LPSTR lpszFileExtension,
  81. IN BOOL fNormal
  82. );
  83. PRIVATE
  84. BOOL
  85. FGetCWDFromCache(
  86. HINTERNET hFtpSession,
  87. LPSTR lpBuff,
  88. LPDWORD lpdwBuffSize
  89. );
  90. PRIVATE
  91. BOOL
  92. FIsFtpExpired(
  93. HINTERNET handle,
  94. LPCACHE_ENTRY_INFO lpCEI
  95. );
  96. VOID
  97. LocalSetObjectName(
  98. HINTERNET hFtpMapped,
  99. LPSTR lpszFileName
  100. );
  101. PRIVATE
  102. BOOL
  103. IsSearchFileDirectory(
  104. LPCSTR lpszFileDirName
  105. );
  106. //
  107. // functions
  108. //
  109. INTERNETAPI_(HINTERNET) FtpFindFirstFileA(
  110. IN HINTERNET hFtpSession,
  111. IN LPCSTR lpszSearchFile OPTIONAL,
  112. OUT LPWIN32_FIND_DATA lpFindFileData OPTIONAL,
  113. IN DWORD dwFlags,
  114. IN DWORD_PTR dwContext
  115. )
  116. /*++
  117. Routine Description:
  118. Download the remote site's directory listing and parse it into
  119. WIN32_FIND_DATA structures that we can pass back to the app.
  120. If the FTP session is currently involved in a data transfer, such as
  121. a FtpOpenFile()....FtpCloseFile() series of calls, this function will
  122. fail.
  123. Arguments:
  124. hFtpSession - Handle to an FTP session, as returned from FtpOpen()
  125. lpszSearchFile - Pointer to a string containing a file specification
  126. that constrains the search. (e.g., "*.txt"). A NULL
  127. pointer is treated the same as an empty string
  128. lpFindFileData - Pointer to a buffer that will contain WIN32_FIND_DATA
  129. information when this call succeeds. If this parameter
  130. is NULL, then we can still return success, but all find
  131. information will be returned via InternetFindNextFile()
  132. dwFlags - controlling caching, etc.
  133. dwContext - app-supplied context value for call-backs
  134. Return Value:
  135. HINTERNET
  136. Success - new find handle
  137. Failure - NULL. Call GetLastError() for more information:
  138. ERROR_INVALID_HANDLE
  139. The session handle is not recognized
  140. ERROR_FTP_TRANSFER_IN_PROGRESS
  141. The data connection is already in use
  142. ERROR_NO_MORE_FILES
  143. The end of the directory listing has been reached
  144. ERROR_INTERNET_EXTENDED_ERROR
  145. Call InternetGetLastResponseInfo() for the text
  146. ERROR_INTERNET_INTERNAL_ERROR
  147. Something bad happened
  148. --*/
  149. {
  150. DEBUG_ENTER_API((DBG_API,
  151. Handle,
  152. "FtpFindFirstFileA",
  153. "%#x, %.80q, %#x, %#x, %#x",
  154. hFtpSession,
  155. lpszSearchFile,
  156. lpFindFileData,
  157. dwFlags,
  158. dwContext
  159. ));
  160. HINTERNET hFind = InternalFtpFindFirstFileA(hFtpSession,
  161. lpszSearchFile,
  162. lpFindFileData,
  163. dwFlags,
  164. dwContext,
  165. FALSE // not a CACHE_ONLY request
  166. );
  167. DEBUG_LEAVE_API(hFind);
  168. return hFind;
  169. }
  170. HINTERNET
  171. InternalFtpFindFirstFileA(
  172. IN HINTERNET hFtpSession,
  173. IN LPCSTR lpszSearchFile OPTIONAL,
  174. OUT LPWIN32_FIND_DATA lpFindFileData OPTIONAL,
  175. IN DWORD dwFlags,
  176. IN DWORD_PTR dwContext,
  177. IN BOOL fCacheOnly,
  178. IN BOOL fAllowEmpty
  179. )
  180. /*++
  181. Routine Description:
  182. Download the remote site's directory listing and parse it into
  183. WIN32_FIND_DATA structures that we can pass back to the app.
  184. If the FTP session is currently involved in a data transfer, such as
  185. a FtpOpenFile()....FtpCloseFile() series of calls, this function will
  186. fail.
  187. Arguments:
  188. hFtpSession - Handle to an FTP session, as returned from FtpOpen()
  189. lpszSearchFile - Pointer to a string containing a file specification
  190. that constrains the search. (e.g., "*.txt"). A NULL
  191. pointer is treated the same as an empty string
  192. lpFindFileData - Pointer to a buffer that will contain WIN32_FIND_DATA
  193. information when this call succeeds. If this parameter
  194. is NULL, then we can still return success, but all find
  195. information will be returned via InternetFindNextFile()
  196. dwFlags - controlling caching, etc.
  197. dwContext - app-supplied context value for call-backs,
  198. fCacheOnly - don't go remote if didn't find in the cache
  199. fAllowEmpty - return handle even if no files found
  200. Return Value:
  201. HINTERNET
  202. Success - new find handle
  203. Failure - NULL. Call GetLastError() for more information:
  204. ERROR_INVALID_HANDLE
  205. The session handle is not recognized
  206. ERROR_FTP_TRANSFER_IN_PROGRESS
  207. The data connection is already in use
  208. ERROR_NO_MORE_FILES
  209. The end of the directory listing has been reached
  210. ERROR_INTERNET_EXTENDED_ERROR
  211. Call InternetGetLastResponseInfo() for the text
  212. ERROR_INTERNET_INTERNAL_ERROR
  213. Something bad happened
  214. --*/
  215. {
  216. DEBUG_ENTER((DBG_FTP,
  217. Handle,
  218. "InternalFtpFindFirstFileA",
  219. "%#x, %.80q, %#x, %#x, %#x, %B, %B",
  220. hFtpSession,
  221. lpszSearchFile,
  222. lpFindFileData,
  223. dwFlags,
  224. dwContext,
  225. fCacheOnly,
  226. fAllowEmpty
  227. ));
  228. HINTERNET findHandle = NULL;
  229. HINTERNET hConnectMapped = NULL;
  230. BOOL isLocal;
  231. BOOL isAsync = FALSE;
  232. LPINTERNET_THREAD_INFO lpThreadInfo;
  233. DWORD error;
  234. BOOL bIsWorker = FALSE;
  235. BOOL bNonNestedAsync = FALSE;
  236. if (!GlobalDataInitialized) {
  237. error = ERROR_INTERNET_NOT_INITIALIZED;
  238. goto done;
  239. }
  240. lpThreadInfo = InternetGetThreadInfo();
  241. if (lpThreadInfo == NULL) {
  242. INET_ASSERT(FALSE);
  243. error = ERROR_INTERNET_INTERNAL_ERROR;
  244. goto done;
  245. }
  246. _InternetIncNestingCount();
  247. bIsWorker = lpThreadInfo->IsAsyncWorkerThread;
  248. bNonNestedAsync = bIsWorker && (lpThreadInfo->NestedRequests == 1);
  249. //
  250. // if this is the async part of the request and this function is not nested
  251. // then hFtpSession is actually the mapped address of the find handle
  252. //
  253. if (bNonNestedAsync) {
  254. findHandle = hFtpSession;
  255. hConnectMapped = ((FTP_FIND_HANDLE_OBJECT *)findHandle)->GetParent();
  256. } else {
  257. error = MapHandleToAddress(hFtpSession, (LPVOID *)&hConnectMapped, FALSE);
  258. if ((error != ERROR_SUCCESS) && (hConnectMapped == NULL)) {
  259. goto quit;
  260. }
  261. error = RIsHandleLocal(hConnectMapped,
  262. &isLocal,
  263. &isAsync,
  264. TypeFtpConnectHandle
  265. );
  266. if (error != ERROR_SUCCESS) {
  267. goto quit;
  268. }
  269. //
  270. // validate parameters
  271. //
  272. if ((ARGUMENT_PRESENT(lpFindFileData)
  273. && IsBadWritePtr(lpFindFileData, sizeof(*lpFindFileData)))
  274. || (ARGUMENT_PRESENT(lpszSearchFile)
  275. && IsBadStringPtr(lpszSearchFile, INTERNET_MAX_PATH_LENGTH + 1))) {
  276. error = ERROR_INVALID_PARAMETER;
  277. goto quit;
  278. }
  279. //
  280. // convert NULL search argument to empty string
  281. //
  282. if (!ARGUMENT_PRESENT(lpszSearchFile)) {
  283. lpszSearchFile = "";
  284. }
  285. //
  286. // set the context and handle info and clear last error variables
  287. //
  288. _InternetSetObjectHandle(lpThreadInfo, hFtpSession, hConnectMapped);
  289. _InternetSetContext(lpThreadInfo, dwContext);
  290. _InternetClearLastError(lpThreadInfo);
  291. //
  292. // create the handle object now. This can be used to cancel the async
  293. // operation, or the sync operation if InternetCloseHandle() is called
  294. // from a different thread
  295. //
  296. INET_ASSERT(findHandle == NULL);
  297. error = RMakeFtpFindObjectHandle(hConnectMapped,
  298. &findHandle,
  299. (CLOSE_HANDLE_FUNC)wFtpFindClose,
  300. dwContext
  301. );
  302. if (error != ERROR_SUCCESS) {
  303. INET_ASSERT(findHandle == NULL);
  304. goto quit;
  305. }
  306. //
  307. // add another reference: we need this to protect the handle against
  308. // closure in callbacks and across the async thread transition
  309. //
  310. ((HANDLE_OBJECT *)findHandle)->Reference();
  311. //
  312. // this new handle will be used in callbacks
  313. //
  314. _InternetSetObjectHandle(lpThreadInfo,
  315. ((HANDLE_OBJECT *)findHandle)->GetPseudoHandle(),
  316. findHandle
  317. );
  318. }
  319. //
  320. // check to see if the data is in the cache. Do it here so that we don't
  321. // waste any time going async if we already have the data locally
  322. //
  323. if (IsSearchFileDirectory(lpszSearchFile)
  324. && FBeginCacheReadProcessing(findHandle,
  325. lpszSearchFile,
  326. GENERIC_READ,
  327. dwFlags,
  328. dwContext,
  329. ((INTERNET_CONNECT_HANDLE_OBJECT *)hConnectMapped)
  330. ->IsHtmlFind()))// doing html finds
  331. {
  332. error = ERROR_SUCCESS;
  333. if (lpFindFileData) {
  334. DWORD dwBytes = sizeof(WIN32_FIND_DATA);
  335. error = ((INTERNET_CONNECT_HANDLE_OBJECT *)findHandle)->ReadCache(
  336. (LPBYTE)lpFindFileData,
  337. sizeof(WIN32_FIND_DATA),
  338. &dwBytes
  339. );
  340. }
  341. if (error == ERROR_SUCCESS) {
  342. goto quit;
  343. } else {
  344. ((INTERNET_CONNECT_HANDLE_OBJECT *)findHandle)->EndCacheRetrieval();
  345. }
  346. }
  347. if (!((INTERNET_CONNECT_HANDLE_OBJECT *)findHandle)->IsCacheReadInProgress()) {
  348. // if this is a cacheonly request or we are in OFFLINE mode
  349. // then fail
  350. if (fCacheOnly ||
  351. ((((INTERNET_CONNECT_HANDLE_OBJECT *)findHandle)->
  352. GetInternetOpenFlags() | dwFlags) & INTERNET_FLAG_OFFLINE)) {
  353. error = ERROR_PATH_NOT_FOUND;
  354. goto quit;
  355. }
  356. }
  357. //
  358. // the data wasn't in the cache. If the app requested async operation and
  359. // we are in the app's synchronous thread context then queue the request to
  360. // the async scheduler
  361. //
  362. if (!bIsWorker && isAsync && (dwContext != INTERNET_NO_CALLBACK)) {
  363. CFsm_FtpFindFirstFile * pFsm = new CFsm_FtpFindFirstFile(
  364. lpszSearchFile,
  365. lpFindFileData,
  366. dwFlags,
  367. dwContext
  368. );
  369. if (pFsm != NULL &&
  370. pFsm->GetError() == ERROR_SUCCESS)
  371. {
  372. if (error == ERROR_SUCCESS) {
  373. error = pFsm->QueueWorkItem();
  374. if (error == ERROR_IO_PENDING) {
  375. hConnectMapped = NULL;
  376. findHandle = NULL;
  377. }
  378. }
  379. }
  380. else
  381. {
  382. error = ERROR_NOT_ENOUGH_MEMORY;
  383. if ( pFsm )
  384. {
  385. error = pFsm->GetError();
  386. delete pFsm;
  387. pFsm = NULL;
  388. }
  389. }
  390. //
  391. // if we're here then ERROR_SUCCESS cannot have been returned from
  392. // the above calls
  393. //
  394. INET_ASSERT(error != ERROR_SUCCESS);
  395. DEBUG_PRINT(FTP,
  396. INFO,
  397. ("processing request asynchronously: error = %d\n",
  398. error
  399. ));
  400. goto quit;
  401. }
  402. //
  403. // if we're here then we're on the synchronous path: either async I/O was
  404. // not requested, or we failed to make the request asynchronous
  405. //
  406. HINTERNET protocolFtpHandle;
  407. error = RGetLocalHandle(hConnectMapped, &protocolFtpHandle);
  408. if (error == ERROR_SUCCESS) {
  409. HINTERNET protocolFindHandle;
  410. error = wFtpFindFirstFile(protocolFtpHandle,
  411. lpszSearchFile,
  412. lpFindFileData,
  413. &protocolFindHandle
  414. );
  415. if (error == ERROR_NO_MORE_FILES && fAllowEmpty) {
  416. //
  417. // The directory is empty. Allow a handle to be returned,
  418. // but mark it empty so FtpFindNextFile doesn't complain.
  419. //
  420. ((FTP_FIND_HANDLE_OBJECT *) findHandle)->SetIsEmpty();
  421. error = ERROR_SUCCESS;
  422. }
  423. if (error == ERROR_SUCCESS) {
  424. ((FTP_FIND_HANDLE_OBJECT *)findHandle)->SetFindHandle(
  425. protocolFindHandle
  426. );
  427. }
  428. }
  429. //
  430. // if we succeeded in getting the data, add it to the cache
  431. //
  432. if (error == ERROR_SUCCESS) {
  433. //
  434. // don't worry about errors if cache write fails
  435. //
  436. if (IsSearchFileDirectory(lpszSearchFile) && FBeginCacheWriteProcessing(findHandle,
  437. lpszSearchFile,
  438. GENERIC_READ,
  439. dwFlags,
  440. dwContext,
  441. ((INTERNET_CONNECT_HANDLE_OBJECT *)hConnectMapped)
  442. ->IsHtmlFind()// doing html finds
  443. )) {
  444. if (!((INTERNET_CONNECT_HANDLE_OBJECT *)hConnectMapped)->IsHtmlFind()
  445. && lpFindFileData) {
  446. DWORD dwBytes = sizeof(WIN32_FIND_DATA);
  447. DWORD errorCache;
  448. errorCache = ((INTERNET_CONNECT_HANDLE_OBJECT *)findHandle)->
  449. WriteCache((LPBYTE)lpFindFileData,
  450. sizeof(WIN32_FIND_DATA)
  451. );
  452. if (errorCache != ERROR_SUCCESS) {
  453. InbLocalEndCacheWrite(findHandle,
  454. NULL,
  455. (errorCache == ERROR_NO_MORE_FILES)
  456. );
  457. }
  458. }
  459. }
  460. }
  461. quit:
  462. _InternetDecNestingCount(1);
  463. if ((!bNonNestedAsync
  464. || ((error != ERROR_SUCCESS) && (error != ERROR_IO_PENDING)))
  465. && (findHandle != NULL)) {
  466. //
  467. // balance the extra reference we added to protect against closure in
  468. // callbacks and across the async thread transition. If non-nested
  469. // async request, this will be accomplished after REQUEST_COMPLETE
  470. // callback
  471. //
  472. if (((HANDLE_OBJECT *)findHandle)->Dereference()) {
  473. error = ERROR_INTERNET_OPERATION_CANCELLED;
  474. }
  475. }
  476. done:
  477. if (error == ERROR_SUCCESS) {
  478. //
  479. // success - return generated pseudo-handle
  480. //
  481. findHandle = ((HANDLE_OBJECT *)findHandle)->GetPseudoHandle();
  482. } else {
  483. if (bNonNestedAsync) {
  484. if (((HANDLE_OBJECT *)findHandle)->IsInvalidated()) {
  485. error = ERROR_INTERNET_OPERATION_CANCELLED;
  486. }
  487. }
  488. if ((error != ERROR_IO_PENDING) && (findHandle != NULL)) {
  489. _InternetCloseHandle(((HANDLE_OBJECT *)findHandle)->GetPseudoHandle());
  490. if (bNonNestedAsync) {
  491. //
  492. // this handle deref'd at async completion
  493. //
  494. hConnectMapped = NULL;
  495. }
  496. if (lpThreadInfo) {
  497. _InternetSetContext(lpThreadInfo, dwContext);
  498. }
  499. }
  500. findHandle = NULL;
  501. }
  502. if (hConnectMapped != NULL) {
  503. DereferenceObject((LPVOID)hConnectMapped);
  504. }
  505. if (error != ERROR_SUCCESS) {
  506. DEBUG_ERROR(API, error);
  507. SetLastError(error);
  508. }
  509. DEBUG_LEAVE(findHandle);
  510. return findHandle;
  511. }
  512. INTERNETAPI_(BOOL) FtpGetFileA(
  513. IN HINTERNET hFtpSession,
  514. IN LPCSTR lpszRemoteFile,
  515. IN LPCSTR lpszNewFile,
  516. IN BOOL fFailIfExists,
  517. IN DWORD dwFlagsAndAttributes,
  518. IN DWORD dwFlags,
  519. IN DWORD_PTR dwContext
  520. )
  521. /*++
  522. Routine Description:
  523. This is a 'wrapper' function that opens/creates a local file and calls other
  524. FTP APIs to copy a file from an FTP server to the local file.
  525. This API does not get remoted, although APIs called herein do
  526. Arguments:
  527. hFtpSession - identifies the FTP server where the file resides
  528. lpszRemoteFile - name of the file on the server to get
  529. lpszNewFile - name of the local file to create
  530. fFailIfExists - TRUE if we should not overwrite an existing file
  531. dwFlagsAndAttributes - various flags
  532. dwFlags - how to transfer the file: as ASCII text or binary
  533. and open options
  534. dwContext - app-supplied context value for call-backs
  535. Return Value:
  536. BOOL
  537. Success - TRUE
  538. Failure - FALSE. Use GetLastError() for more info
  539. --*/
  540. {
  541. DEBUG_ENTER_API((DBG_API,
  542. Bool,
  543. "FtpGetFileA",
  544. "%#x, %q, %q, %B, %#x, %#x, %#x",
  545. hFtpSession,
  546. lpszRemoteFile,
  547. lpszNewFile,
  548. fFailIfExists,
  549. dwFlagsAndAttributes,
  550. dwFlags,
  551. dwContext
  552. ));
  553. BOOL fSuccess = FALSE;
  554. DWORD error = ERROR_SUCCESS;
  555. PWSTR pwszRemoteFile = NULL, pwszNewFile = NULL;
  556. DWORD cc;
  557. if (IsBadStringPtr(lpszRemoteFile, INTERNET_MAX_PATH_LENGTH + 1)
  558. || (*lpszRemoteFile == '\0')
  559. || IsBadStringPtr(lpszNewFile, INTERNET_MAX_PATH_LENGTH + 1)
  560. || (*lpszNewFile == '\0'))
  561. {
  562. error = ERROR_INVALID_PARAMETER;
  563. goto done;
  564. }
  565. cc = MultiByteToWideChar(CP_ACP, 0, lpszRemoteFile, -1, NULL, 0);
  566. pwszRemoteFile = (PWSTR)ALLOCATE_FIXED_MEMORY((cc*sizeof(WCHAR)));
  567. if (!pwszRemoteFile)
  568. {
  569. error = ERROR_NOT_ENOUGH_MEMORY;
  570. goto done;
  571. }
  572. MultiByteToWideChar(CP_ACP, 0, lpszRemoteFile, -1, pwszRemoteFile, cc);
  573. cc = MultiByteToWideChar(CP_ACP, 0, lpszNewFile, -1, NULL, 0);
  574. pwszNewFile = (PWSTR)ALLOCATE_FIXED_MEMORY((cc*sizeof(WCHAR)));
  575. if (!pwszNewFile)
  576. {
  577. error = ERROR_NOT_ENOUGH_MEMORY;
  578. goto done;
  579. }
  580. MultiByteToWideChar(CP_ACP, 0, lpszNewFile, -1, pwszNewFile, cc);
  581. fSuccess = FtpGetFileW(
  582. hFtpSession,
  583. pwszRemoteFile,
  584. pwszNewFile,
  585. fFailIfExists,
  586. dwFlagsAndAttributes,
  587. dwFlags,
  588. dwContext);
  589. done:
  590. if (pwszRemoteFile)
  591. {
  592. FREE_MEMORY(pwszRemoteFile);
  593. }
  594. if (pwszNewFile)
  595. {
  596. FREE_MEMORY(pwszNewFile);
  597. }
  598. if (error != ERROR_SUCCESS) {
  599. SetLastError(error);
  600. DEBUG_ERROR(API, error);
  601. }
  602. DEBUG_LEAVE_API(fSuccess);
  603. return fSuccess;
  604. }
  605. INTERNETAPI_(BOOL) FtpPutFileA(
  606. IN HINTERNET hFtpSession,
  607. IN LPCSTR lpszLocalFile,
  608. IN LPCSTR lpszNewRemoteFile,
  609. IN DWORD dwFlags,
  610. IN DWORD_PTR dwContext
  611. )
  612. /*++
  613. Routine Description:
  614. This is a 'wrapper' function that opens/creates a local file and calls other
  615. FTP APIs to copy a file from an FTP server to the local file.
  616. This API does not get remoted, although APIs called herein do
  617. BUGBUG - this API is virtually the same as FtpGetFileA(). Check out
  618. possibility of commonalizing
  619. Arguments:
  620. hFtpSession - identifies the FTP server where the file resides
  621. lpszLocalFile - name of the local file to upload
  622. lpszNewRemoteFile - name of the file on the server to create
  623. dwFlags - how to transfer the file: as ASCII text or binary and
  624. open options
  625. dwContext - app-supplied context value for call-backs
  626. Return Value:
  627. BOOL
  628. Success - TRUE
  629. Failure - FALSE. Use GetLastError() for more info
  630. --*/
  631. {
  632. DEBUG_ENTER_API((DBG_API,
  633. Bool,
  634. "FtpPutFileA",
  635. "%#x, %q, %q, %#x, %#x",
  636. hFtpSession,
  637. lpszLocalFile,
  638. lpszNewRemoteFile,
  639. dwFlags,
  640. dwContext
  641. ));
  642. BOOL fSuccess = FALSE;
  643. DWORD error = ERROR_SUCCESS;
  644. PWSTR pwszRemoteFile = NULL, pwszNewFile = NULL;
  645. DWORD cc;
  646. if (IsBadStringPtr(lpszNewRemoteFile, INTERNET_MAX_PATH_LENGTH + 1)
  647. || (*lpszNewRemoteFile == '\0')
  648. || IsBadStringPtr(lpszLocalFile, INTERNET_MAX_PATH_LENGTH + 1)
  649. || (*lpszLocalFile == '\0'))
  650. {
  651. error = ERROR_INVALID_PARAMETER;
  652. goto done;
  653. }
  654. cc = MultiByteToWideChar(CP_ACP, 0, lpszNewRemoteFile, -1, NULL, 0);
  655. pwszRemoteFile = (PWSTR)ALLOCATE_FIXED_MEMORY((cc*sizeof(WCHAR)));
  656. if (!pwszRemoteFile)
  657. {
  658. error = ERROR_NOT_ENOUGH_MEMORY;
  659. goto done;
  660. }
  661. MultiByteToWideChar(CP_ACP, 0, lpszNewRemoteFile, -1, pwszRemoteFile, cc);
  662. cc = MultiByteToWideChar(CP_ACP, 0, lpszLocalFile, -1, NULL, 0);
  663. pwszNewFile = (PWSTR)ALLOCATE_FIXED_MEMORY((cc*sizeof(WCHAR)));
  664. if (!pwszNewFile)
  665. {
  666. error = ERROR_NOT_ENOUGH_MEMORY;
  667. goto done;
  668. }
  669. MultiByteToWideChar(CP_ACP, 0, lpszLocalFile, -1, pwszNewFile, cc);
  670. fSuccess = FtpPutFileW(
  671. hFtpSession,
  672. pwszNewFile,
  673. pwszRemoteFile,
  674. dwFlags,
  675. dwContext);
  676. done:
  677. if (pwszRemoteFile)
  678. {
  679. FREE_MEMORY(pwszRemoteFile);
  680. }
  681. if (pwszNewFile)
  682. {
  683. FREE_MEMORY(pwszNewFile);
  684. }
  685. if (error != ERROR_SUCCESS) {
  686. SetLastError(error);
  687. DEBUG_ERROR(API, error);
  688. }
  689. DEBUG_LEAVE_API(fSuccess);
  690. return fSuccess;
  691. }
  692. INTERNETAPI_(BOOL) FtpDeleteFileA(
  693. IN HINTERNET hFtpSession,
  694. IN LPCSTR lpszFileName
  695. )
  696. /*++
  697. Routine Description:
  698. Deletes the named file at the FTP server
  699. Arguments:
  700. hFtpSession - identifies FTP server where file is to be deleted
  701. lpszFileName - name of file to delete
  702. Return Value:
  703. BOOL
  704. Success - TRUE
  705. Failure - FALSE. Use GetLastError() for more info
  706. --*/
  707. {
  708. DEBUG_ENTER_API((DBG_API,
  709. Bool,
  710. "FtpDeleteFileA",
  711. "%#x, %q",
  712. hFtpSession,
  713. lpszFileName
  714. ));
  715. DWORD error;
  716. LPINTERNET_THREAD_INFO lpThreadInfo;
  717. DWORD nestingLevel = 0;
  718. HINTERNET hMapped = NULL;
  719. BOOL fDeref = TRUE;
  720. if (!GlobalDataInitialized) {
  721. error = ERROR_INTERNET_NOT_INITIALIZED;
  722. goto done;
  723. }
  724. lpThreadInfo = InternetGetThreadInfo();
  725. if (lpThreadInfo == NULL) {
  726. INET_ASSERT(FALSE);
  727. error = ERROR_INTERNET_INTERNAL_ERROR;
  728. goto done;
  729. }
  730. _InternetIncNestingCount();
  731. nestingLevel = 1;
  732. //
  733. // map the handle
  734. //
  735. error = MapHandleToAddress(hFtpSession, (LPVOID *)&hMapped, FALSE);
  736. if ((error != ERROR_SUCCESS) && (hMapped == NULL)) {
  737. goto quit;
  738. }
  739. //
  740. // set the context and handle info and clear last error variables
  741. //
  742. _InternetSetObjectHandle(lpThreadInfo, hFtpSession, hMapped);
  743. _InternetSetContext(lpThreadInfo,
  744. ((INTERNET_HANDLE_OBJECT *)hMapped)->GetContext()
  745. );
  746. _InternetClearLastError(lpThreadInfo);
  747. //
  748. // quit now if the handle is invalid
  749. //
  750. if (error != ERROR_SUCCESS) {
  751. goto quit;
  752. }
  753. //
  754. // validate handle
  755. //
  756. BOOL isLocal;
  757. BOOL isAsync;
  758. error = RIsHandleLocal(hMapped,
  759. &isLocal,
  760. &isAsync,
  761. TypeFtpConnectHandle
  762. );
  763. if (error != ERROR_SUCCESS) {
  764. goto quit;
  765. }
  766. //
  767. // perform sync work
  768. //
  769. if (!lpThreadInfo->IsAsyncWorkerThread
  770. || (lpThreadInfo->NestedRequests > 1)) {
  771. //
  772. // validate parameters
  773. //
  774. if (IsBadStringPtr(lpszFileName, INTERNET_MAX_PATH_LENGTH + 1)
  775. || (*lpszFileName == '\0')) {
  776. error = ERROR_INVALID_PARAMETER;
  777. goto quit;
  778. }
  779. // in offline mode modifications are disallowed
  780. // someday we will do internet briefcase but not today
  781. // BUGBUG there is hole in this API, there is no dwFlags
  782. // so there is no way to know whether these operations are
  783. // happening online or offline
  784. if (((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->GetInternetOpenFlags() &
  785. INTERNET_FLAG_OFFLINE) {
  786. error = ERROR_WRITE_PROTECT;
  787. goto quit;
  788. }
  789. if (!lpThreadInfo->IsAsyncWorkerThread && isAsync) {
  790. // MakeAsyncRequest
  791. CFsm_FtpDeleteFile * pFsm;
  792. pFsm = new CFsm_FtpDeleteFile(hFtpSession, lpszFileName);
  793. if (pFsm != NULL &&
  794. pFsm->GetError() == ERROR_SUCCESS)
  795. {
  796. error = pFsm->QueueWorkItem();
  797. if ( error == ERROR_IO_PENDING ) {
  798. fDeref = FALSE;
  799. }
  800. }
  801. else
  802. {
  803. error = ERROR_NOT_ENOUGH_MEMORY;
  804. if ( pFsm )
  805. {
  806. error = pFsm->GetError();
  807. delete pFsm;
  808. pFsm = NULL;
  809. }
  810. }
  811. //
  812. // if we're here then ERROR_SUCCESS cannot have been returned from
  813. // the above calls
  814. //
  815. INET_ASSERT(error != ERROR_SUCCESS);
  816. DEBUG_PRINT(FTP,
  817. INFO,
  818. ("processing request asynchronously: error = %d\n",
  819. error
  820. ));
  821. goto quit;
  822. }
  823. }
  824. LocalSetObjectName(hMapped, (LPSTR)lpszFileName);
  825. HINTERNET ftpHandle;
  826. error = RGetLocalHandle(hMapped, &ftpHandle);
  827. if (error == ERROR_SUCCESS) {
  828. error = wFtpDeleteFile(ftpHandle, lpszFileName);
  829. if (error == ERROR_SUCCESS) {
  830. ((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->ExpireDependents();
  831. }
  832. }
  833. quit:
  834. if ((hMapped != NULL) && fDeref) {
  835. DereferenceObject((LPVOID)hMapped);
  836. }
  837. _InternetDecNestingCount(nestingLevel);;
  838. done:
  839. BOOL success;
  840. if (error != ERROR_SUCCESS) {
  841. SetLastError(error);
  842. success = FALSE;
  843. DEBUG_ERROR(API, error);
  844. } else {
  845. success = TRUE;
  846. }
  847. DEBUG_LEAVE_API(success);
  848. return success;
  849. }
  850. INTERNETAPI_(BOOL) FtpRenameFileA(
  851. IN HINTERNET hFtpSession,
  852. IN LPCSTR lpszExisting,
  853. IN LPCSTR lpszNew
  854. )
  855. /*++
  856. Routine Description:
  857. Renames a file on an FTP server
  858. Arguments:
  859. hFtpSession - identifies FTP server where file is to be renamed
  860. lpszExisting - current file name
  861. lpszNew - new file name
  862. Return Value:
  863. BOOL
  864. Success - TRUE
  865. Failure - FALSE. Use GetLastError() for more info
  866. --*/
  867. {
  868. DEBUG_ENTER_API((DBG_API,
  869. Bool,
  870. "FtpRenameFileA",
  871. "%#x, %q, %q",
  872. hFtpSession,
  873. lpszExisting,
  874. lpszNew
  875. ));
  876. DWORD error;
  877. LPINTERNET_THREAD_INFO lpThreadInfo;
  878. DWORD nestingLevel = 0;
  879. HINTERNET hMapped = NULL;
  880. BOOL fDeref = TRUE;
  881. if (!GlobalDataInitialized) {
  882. error = ERROR_INTERNET_NOT_INITIALIZED;
  883. goto done;
  884. }
  885. //
  886. // get the thread info block
  887. //
  888. lpThreadInfo = InternetGetThreadInfo();
  889. if (lpThreadInfo == NULL) {
  890. INET_ASSERT(FALSE);
  891. error = ERROR_INTERNET_INTERNAL_ERROR;
  892. goto done;
  893. }
  894. _InternetIncNestingCount();
  895. nestingLevel = 1;
  896. //
  897. // map the handle
  898. //
  899. error = MapHandleToAddress(hFtpSession, (LPVOID *)&hMapped, FALSE);
  900. if ((error != ERROR_SUCCESS) && (hMapped == NULL)) {
  901. goto quit;
  902. }
  903. //
  904. // set the context and handle info and clear last error variables
  905. //
  906. _InternetSetObjectHandle(lpThreadInfo, hFtpSession, hMapped);
  907. _InternetSetContext(lpThreadInfo,
  908. ((INTERNET_HANDLE_OBJECT *)hMapped)->GetContext()
  909. );
  910. _InternetClearLastError(lpThreadInfo);
  911. //
  912. // quit now if the handle is invalid
  913. //
  914. if (error != ERROR_SUCCESS) {
  915. goto quit;
  916. }
  917. //
  918. // validate the handle
  919. //
  920. BOOL isLocal;
  921. BOOL isAsync;
  922. error = RIsHandleLocal(hMapped,
  923. &isLocal,
  924. &isAsync,
  925. TypeFtpConnectHandle
  926. );
  927. if (error != ERROR_SUCCESS) {
  928. goto quit;
  929. }
  930. //
  931. // perform sync work
  932. //
  933. if (!lpThreadInfo->IsAsyncWorkerThread
  934. || (lpThreadInfo->NestedRequests > 1))
  935. {
  936. //
  937. // validate parameters
  938. //
  939. if (IsBadStringPtr(lpszExisting, INTERNET_MAX_PATH_LENGTH + 1)
  940. || (*lpszExisting == '\0')
  941. || IsBadStringPtr(lpszNew, INTERNET_MAX_PATH_LENGTH + 1)
  942. || (*lpszNew == '\0')) {
  943. error = ERROR_INVALID_PARAMETER;
  944. goto quit;
  945. }
  946. // in offline mode modifications are disallowed
  947. // someday we will do internet briefcase but not today
  948. // BUGBUG there is hole in this API, there is no dwFlags
  949. // so there is no way to know whether these operations are
  950. // happening online or offline
  951. if (((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->GetInternetOpenFlags() &
  952. INTERNET_FLAG_OFFLINE) {
  953. error = ERROR_WRITE_PROTECT;
  954. goto quit;
  955. }
  956. if (!lpThreadInfo->IsAsyncWorkerThread && isAsync) {
  957. // MakeAsyncRequest
  958. CFsm_FtpRenameFile * pFsm;
  959. pFsm = new CFsm_FtpRenameFile(hFtpSession, lpszExisting, lpszNew);
  960. if (pFsm != NULL &&
  961. pFsm->GetError() == ERROR_SUCCESS)
  962. {
  963. error = pFsm->QueueWorkItem();
  964. if ( error == ERROR_IO_PENDING ) {
  965. fDeref = FALSE;
  966. }
  967. }
  968. else
  969. {
  970. error = ERROR_NOT_ENOUGH_MEMORY;
  971. if ( pFsm )
  972. {
  973. error = pFsm->GetError();
  974. delete pFsm;
  975. pFsm = NULL;
  976. }
  977. }
  978. //
  979. // if we're here then ERROR_SUCCESS cannot have been returned from
  980. // the above calls
  981. //
  982. INET_ASSERT(error != ERROR_SUCCESS);
  983. DEBUG_PRINT(FTP,
  984. INFO,
  985. ("processing request asynchronously: error = %d\n",
  986. error
  987. ));
  988. goto quit;
  989. }
  990. }
  991. HINTERNET ftpHandle;
  992. LocalSetObjectName(hMapped, (LPSTR)lpszExisting);
  993. error = RGetLocalHandle(hMapped, &ftpHandle);
  994. if (error == ERROR_SUCCESS) {
  995. error = wFtpRenameFile(ftpHandle,
  996. lpszExisting,
  997. lpszNew
  998. );
  999. if (error == ERROR_SUCCESS) {
  1000. ((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->ExpireDependents();
  1001. }
  1002. }
  1003. quit:
  1004. if ((hMapped != NULL) && fDeref) {
  1005. DereferenceObject((LPVOID)hMapped);
  1006. }
  1007. _InternetDecNestingCount(nestingLevel);;
  1008. done:
  1009. BOOL success;
  1010. if (error != ERROR_SUCCESS) {
  1011. SetLastError(error);
  1012. success = FALSE;
  1013. DEBUG_ERROR(API, error);
  1014. } else {
  1015. success = TRUE;
  1016. }
  1017. DEBUG_LEAVE_API(success);
  1018. return success;
  1019. }
  1020. INTERNETAPI_(HINTERNET) FtpOpenFileA(
  1021. IN HINTERNET hFtpSession,
  1022. IN LPCSTR lpszFileName,
  1023. IN DWORD dwAccess,
  1024. IN DWORD dwFlags,
  1025. IN DWORD_PTR dwContext
  1026. )
  1027. /*++
  1028. Routine Description:
  1029. Sets up the FTP session to read or write a file at the FTP server
  1030. Arguments:
  1031. hFtpSession - InternetConnect handle identifying FTP server
  1032. lpszFileName - name of file to open
  1033. dwAccess - how to access file - for read or write. Can be one of:
  1034. - GENERIC_READ
  1035. - GENERIC_WRITE
  1036. dwFlags - how to transfer file - ASCII text, or binary and open
  1037. options. Can be any or all of the following:
  1038. - INTERNET_FLAG_RELOAD
  1039. - INTERNET_FLAG_RAW_DATA (passed through by
  1040. InternetOpenUrl(), meaningless here)
  1041. - INTERNET_FLAG_EXISTING_CONNECT (passed through by
  1042. InternetOpenUrl(), meaningless here)
  1043. - FTP_TRANSFER_TYPE_XXX
  1044. dwContext - app-supplied context value for call-backs
  1045. Return Value:
  1046. HINTERNET
  1047. Success - handle of FTP file object
  1048. Failure - NULL. Use GetLastError() for more info
  1049. --*/
  1050. {
  1051. DEBUG_ENTER_API((DBG_API,
  1052. Handle,
  1053. "FtpOpenFileA",
  1054. "%#x, %q, %#x, %#x, %#x",
  1055. hFtpSession,
  1056. lpszFileName,
  1057. dwAccess,
  1058. dwFlags,
  1059. dwContext
  1060. ));
  1061. HINTERNET hFile = InternalFtpOpenFileA(hFtpSession,
  1062. lpszFileName,
  1063. dwAccess,
  1064. dwFlags,
  1065. dwContext,
  1066. FALSE // this is not a cachonly request
  1067. );
  1068. DEBUG_LEAVE_API(hFile);
  1069. return hFile;
  1070. }
  1071. HINTERNET
  1072. InternalFtpOpenFileA(
  1073. IN HINTERNET hFtpSession,
  1074. IN LPCSTR lpszFileName,
  1075. IN DWORD dwAccess,
  1076. IN DWORD dwFlags,
  1077. IN DWORD_PTR dwContext,
  1078. IN BOOL fCacheOnly
  1079. )
  1080. /*++
  1081. Routine Description:
  1082. Sets up the FTP session to read or write a file at the FTP server
  1083. Arguments:
  1084. hFtpSession - InternetConnect handle identifying FTP server
  1085. lpszFileName - name of file to open
  1086. dwAccess - how to access file - for read or write. Can be one of:
  1087. - GENERIC_READ
  1088. - GENERIC_WRITE
  1089. dwFlags - how to transfer file - ASCII text, or binary and open
  1090. options. Can be any or all of the following:
  1091. - INTERNET_FLAG_RELOAD
  1092. - INTERNET_FLAG_RAW_DATA (passed through by
  1093. InternetOpenUrl(), meaningless here)
  1094. - INTERNET_FLAG_EXISTING_CONNECT (passed through by
  1095. InternetOpenUrl(), meaningless here)
  1096. - FTP_TRANSFER_TYPE_XXX
  1097. dwContext - app-supplied context value for call-backs
  1098. fCacheOnly - TRUE if this operation must be satisfied from cache
  1099. Return Value:
  1100. HINTERNET
  1101. Success - handle of FTP file object
  1102. Failure - NULL. Use GetLastError() for more info
  1103. --*/
  1104. {
  1105. DEBUG_ENTER((DBG_FTP,
  1106. Handle,
  1107. "InternalFtpOpenFileA",
  1108. "%#x, %q, %#x, %#x, %#x, %B",
  1109. hFtpSession,
  1110. lpszFileName,
  1111. dwAccess,
  1112. dwFlags,
  1113. dwContext,
  1114. fCacheOnly
  1115. ));
  1116. HINTERNET fileHandle = NULL;
  1117. HINTERNET hConnectMapped;
  1118. HINTERNET hObject;
  1119. HINTERNET hObjectMapped = NULL;
  1120. BOOL bNonNestedAsync = FALSE;
  1121. LPINTERNET_THREAD_INFO lpThreadInfo;
  1122. DWORD error;
  1123. DWORD nestingLevel = 0;
  1124. if (!GlobalDataInitialized) {
  1125. error = ERROR_INTERNET_NOT_INITIALIZED;
  1126. goto done;
  1127. }
  1128. //
  1129. // get the thread info block
  1130. //
  1131. lpThreadInfo = InternetGetThreadInfo();
  1132. if (lpThreadInfo == NULL) {
  1133. INET_ASSERT(FALSE);
  1134. error = ERROR_INTERNET_INTERNAL_ERROR;
  1135. goto done;
  1136. }
  1137. bNonNestedAsync = lpThreadInfo->IsAsyncWorkerThread
  1138. && (lpThreadInfo->NestedRequests == 0);
  1139. _InternetIncNestingCount();
  1140. nestingLevel = 1;
  1141. //
  1142. // if this is the async worker thread AND we haven't been called from
  1143. // another API which is running asynchronously, then what we think is
  1144. // hFtpSession is really the file handle object. Get the handles in the
  1145. // right variables
  1146. //
  1147. if (bNonNestedAsync) {
  1148. hObject = hFtpSession;
  1149. error = MapHandleToAddress(hObject, (LPVOID *)&hObjectMapped, FALSE);
  1150. if ((error != ERROR_SUCCESS) && (hObjectMapped == NULL)) {
  1151. goto quit;
  1152. }
  1153. fileHandle = hObjectMapped;
  1154. hConnectMapped = ((FTP_FILE_HANDLE_OBJECT *)fileHandle)->GetParent();
  1155. } else {
  1156. error = MapHandleToAddress(hFtpSession, (LPVOID *)&hConnectMapped, FALSE);
  1157. if ((error != ERROR_SUCCESS) && (hConnectMapped == NULL)) {
  1158. goto quit;
  1159. }
  1160. hObject = hFtpSession;
  1161. hObjectMapped = hConnectMapped;
  1162. }
  1163. //
  1164. // set the context and handle info and clear last error variables
  1165. //
  1166. _InternetSetObjectHandle(lpThreadInfo, hObject, hObjectMapped);
  1167. _InternetSetContext(lpThreadInfo, dwContext);
  1168. _InternetClearLastError(lpThreadInfo);
  1169. //
  1170. // quit now if the handle is invalid
  1171. //
  1172. if (error != ERROR_SUCCESS) {
  1173. goto quit;
  1174. }
  1175. //
  1176. // validate handle
  1177. //
  1178. BOOL isLocal;
  1179. BOOL isAsync;
  1180. error = RIsHandleLocal(hConnectMapped,
  1181. &isLocal,
  1182. &isAsync,
  1183. TypeFtpConnectHandle
  1184. );
  1185. if (error != ERROR_SUCCESS) {
  1186. goto quit;
  1187. }
  1188. //
  1189. // perform sync work
  1190. //
  1191. if (!lpThreadInfo->IsAsyncWorkerThread
  1192. || (lpThreadInfo->NestedRequests > 1)) {
  1193. //
  1194. // validate parameters
  1195. //
  1196. if (IsBadStringPtr(lpszFileName, INTERNET_MAX_PATH_LENGTH + 1)
  1197. || (*lpszFileName == '\0')
  1198. //
  1199. // dwAccess must be GENERIC_READ or GENERIC_WRITE, but not both, and
  1200. // can't be zero or have undefined bits set. Comparing for equality
  1201. // works
  1202. //
  1203. || ((dwAccess != GENERIC_READ) && (dwAccess != GENERIC_WRITE))
  1204. //
  1205. // must be a recognized transfer type
  1206. //
  1207. || (((dwFlags & FTP_TRANSFER_TYPE_MASK) != 0)
  1208. ? (((dwFlags & FTP_TRANSFER_TYPE_MASK) != FTP_TRANSFER_TYPE_ASCII)
  1209. && ((dwFlags & FTP_TRANSFER_TYPE_MASK) != FTP_TRANSFER_TYPE_BINARY))
  1210. : FALSE)
  1211. || ((dwFlags & ~ALLOWED_FTP_FLAGS) != 0)) {
  1212. error = ERROR_INVALID_PARAMETER;
  1213. goto quit;
  1214. }
  1215. //
  1216. // use default transfer type if so requested
  1217. //
  1218. if ((dwFlags & FTP_TRANSFER_TYPE_MASK) == 0) {
  1219. dwFlags |= FTP_TRANSFER_TYPE_BINARY;
  1220. }
  1221. //
  1222. // create the handle object now. This can be used to cancel the async
  1223. // operation, or the sync operation if InternetCloseHandle() is called
  1224. // from a different thread
  1225. //
  1226. INET_ASSERT(fileHandle == NULL);
  1227. error = RMakeFtpFileObjectHandle(hConnectMapped,
  1228. &fileHandle,
  1229. (CLOSE_HANDLE_FUNC)wFtpCloseFile,
  1230. dwContext
  1231. );
  1232. if (error != ERROR_SUCCESS) {
  1233. INET_ASSERT(fileHandle == NULL);
  1234. goto quit;
  1235. }
  1236. //
  1237. // add reference to keep handle alive during callbacks and across
  1238. // async thread transition
  1239. //
  1240. ((HANDLE_OBJECT *)fileHandle)->Reference();
  1241. //
  1242. // this new handle will be used in callbacks
  1243. //
  1244. _InternetSetObjectHandle(lpThreadInfo,
  1245. ((HANDLE_OBJECT *)fileHandle)->GetPseudoHandle(),
  1246. fileHandle
  1247. );
  1248. }
  1249. if (((FTP_FILE_HANDLE_OBJECT *)fileHandle)->SetFileName(lpszFileName) != ERROR_SUCCESS)
  1250. {
  1251. error = ERROR_NOT_ENOUGH_MEMORY;
  1252. goto quit;
  1253. }
  1254. //
  1255. // check to see if the data is in the cache
  1256. //
  1257. if (FBeginCacheReadProcessing(fileHandle,
  1258. lpszFileName,
  1259. dwAccess,
  1260. dwFlags,
  1261. dwContext, FALSE)) {
  1262. error = ERROR_SUCCESS;
  1263. goto quit;
  1264. } else {
  1265. if (fCacheOnly ||
  1266. ((((INTERNET_CONNECT_HANDLE_OBJECT *)fileHandle)->
  1267. GetInternetOpenFlags() | dwFlags )& INTERNET_FLAG_OFFLINE)) {
  1268. // if we are offline,or doing cacheonly request
  1269. // let us give the right error and quit
  1270. error = ERROR_FILE_NOT_FOUND;
  1271. goto quit;
  1272. }
  1273. }
  1274. if (!lpThreadInfo->IsAsyncWorkerThread
  1275. && isAsync
  1276. && (dwContext != INTERNET_NO_CALLBACK)) {
  1277. // MakeAsyncRequest
  1278. CFsm_FtpOpenFile * pFsm;
  1279. pFsm = new CFsm_FtpOpenFile(((HANDLE_OBJECT *)fileHandle)->GetPseudoHandle(),
  1280. dwContext,
  1281. lpszFileName,
  1282. dwAccess,
  1283. dwFlags
  1284. );
  1285. if (pFsm != NULL &&
  1286. pFsm->GetError() == ERROR_SUCCESS)
  1287. {
  1288. error = pFsm->QueueWorkItem();
  1289. if (error == ERROR_IO_PENDING) {
  1290. fileHandle = NULL;
  1291. hObjectMapped = NULL;
  1292. }
  1293. }
  1294. else
  1295. {
  1296. error = ERROR_NOT_ENOUGH_MEMORY;
  1297. if ( pFsm )
  1298. {
  1299. error = pFsm->GetError();
  1300. delete pFsm;
  1301. pFsm = NULL;
  1302. }
  1303. }
  1304. //
  1305. // if we're here then ERROR_SUCCESS cannot have been returned from
  1306. // the above calls
  1307. //
  1308. INET_ASSERT(error != ERROR_SUCCESS);
  1309. DEBUG_PRINT(FTP,
  1310. INFO,
  1311. ("processing request asynchronously: error = %d\n",
  1312. error
  1313. ));
  1314. goto quit;
  1315. }
  1316. HINTERNET protocolFtpHandle;
  1317. error = RGetLocalHandle(hConnectMapped, &protocolFtpHandle);
  1318. if (error == ERROR_SUCCESS) {
  1319. HINTERNET protocolFileHandle;
  1320. error = wFtpOpenFile(protocolFtpHandle,
  1321. lpszFileName,
  1322. dwAccess,
  1323. dwFlags,
  1324. &protocolFileHandle
  1325. );
  1326. if (error == ERROR_SUCCESS) {
  1327. ((FTP_FILE_HANDLE_OBJECT *)fileHandle)->SetFileHandle(
  1328. protocolFileHandle
  1329. );
  1330. // Now seems like a reasonable time to remove the entry from the
  1331. // cache IFF the open has GENERIC_WRITE access set, i.e. the
  1332. // caller is about to write to this url and make the cache entry
  1333. // stale. This fixes a problem in FtpParseUrl where we bypass the expiry
  1334. // info by forcing an offline state.
  1335. if (dwAccess & GENERIC_WRITE) {
  1336. DeleteUrlCacheEntryA(((FTP_FILE_HANDLE_OBJECT *)fileHandle)->GetURL());
  1337. }
  1338. }
  1339. }
  1340. if (error == ERROR_SUCCESS) {
  1341. //
  1342. // don't worry about errors if cache write does not begin
  1343. //
  1344. FBeginCacheWriteProcessing(fileHandle,
  1345. lpszFileName,
  1346. dwAccess,
  1347. dwFlags,
  1348. dwContext,
  1349. FALSE // not a find
  1350. );
  1351. }
  1352. quit:
  1353. if (fileHandle != NULL) {
  1354. //
  1355. // balance the refcount we added to keep the handle alive during
  1356. // callbacks and across the async thread transition. If this is a non-
  1357. // nested async request then the reference will be balanced after the
  1358. // REQUEST_COMPLETE callback
  1359. //
  1360. ((HANDLE_OBJECT *)fileHandle)->Dereference();
  1361. }
  1362. _InternetDecNestingCount(nestingLevel);;
  1363. done:
  1364. if (bNonNestedAsync && (error == ERROR_SUCCESS)) {
  1365. hObjectMapped = hConnectMapped;
  1366. }
  1367. if (hObjectMapped != NULL) {
  1368. //
  1369. // if we are about to deref the file handle BUT it is already invalidated
  1370. // because the operation was cancelled, e.g., then do not perform the
  1371. // deref - leave it for the close. Otherwise, the close will fail and
  1372. // will not reinstate the callback parameters
  1373. //
  1374. if (!((hObjectMapped == fileHandle) && ((HANDLE_OBJECT *)fileHandle)->IsInvalidated())) {
  1375. DereferenceObject((LPVOID)hObjectMapped);
  1376. }
  1377. }
  1378. if (error != ERROR_SUCCESS) {
  1379. //
  1380. // if we are not pending an async request but we created a handle object
  1381. // then close it
  1382. //
  1383. if ((error != ERROR_IO_PENDING) && (fileHandle != NULL)) {
  1384. HANDLE_OBJECT * hConnMapped;
  1385. HINTERNET hConn;
  1386. hConnMapped = (HANDLE_OBJECT *)((HANDLE_OBJECT *)fileHandle)->GetParent();
  1387. if (hConnMapped != NULL) {
  1388. hConn = (HINTERNET)hConnMapped->GetPseudoHandle();
  1389. }
  1390. ((HANDLE_OBJECT *)fileHandle)->Invalidate();
  1391. ((HANDLE_OBJECT *)fileHandle)->Dereference();
  1392. if (hConnMapped != NULL) {
  1393. _InternetSetObjectHandle(lpThreadInfo, hConn, hConnMapped);
  1394. _InternetSetContext(lpThreadInfo, dwContext);
  1395. }
  1396. }
  1397. //
  1398. // error situation, or request is being processed asynchronously: return
  1399. // a NULL handle
  1400. //
  1401. fileHandle = NULL;
  1402. DEBUG_ERROR(API, error);
  1403. SetLastError(error);
  1404. } else {
  1405. //
  1406. // success - return generated pseudo-handle
  1407. //
  1408. fileHandle = ((HANDLE_OBJECT *)fileHandle)->GetPseudoHandle();
  1409. }
  1410. DEBUG_LEAVE(fileHandle);
  1411. return fileHandle;
  1412. }
  1413. INTERNETAPI_(BOOL) FtpCreateDirectoryA(
  1414. IN HINTERNET hFtpSession,
  1415. IN LPCSTR lpszDirectory
  1416. )
  1417. /*++
  1418. Routine Description:
  1419. Creates a directory on the FTP server
  1420. Arguments:
  1421. hFtpSession - identifies FTP server where directory is to be created
  1422. lpszDirectory - name of directory to create
  1423. Return Value:
  1424. BOOL
  1425. Success - TRUE
  1426. Failure - FALSE. Use GetLastError() for more info
  1427. --*/
  1428. {
  1429. DEBUG_ENTER_API((DBG_API,
  1430. Bool,
  1431. "FtpCreateDirectoryA",
  1432. "%#x, %q",
  1433. hFtpSession,
  1434. lpszDirectory
  1435. ));
  1436. DWORD error;
  1437. LPINTERNET_THREAD_INFO lpThreadInfo;
  1438. DWORD nestingLevel = 0;
  1439. HINTERNET hMapped = NULL;
  1440. BOOL fDeref = TRUE;
  1441. if (!GlobalDataInitialized) {
  1442. error = ERROR_INTERNET_NOT_INITIALIZED;
  1443. goto done;
  1444. }
  1445. //
  1446. // get the thread info block
  1447. //
  1448. lpThreadInfo = InternetGetThreadInfo();
  1449. if (lpThreadInfo == NULL) {
  1450. INET_ASSERT(FALSE);
  1451. error = ERROR_INTERNET_INTERNAL_ERROR;
  1452. goto quit;
  1453. }
  1454. _InternetIncNestingCount();
  1455. nestingLevel = 1;
  1456. //
  1457. // map the handle
  1458. //
  1459. error = MapHandleToAddress(hFtpSession, (LPVOID *)&hMapped, FALSE);
  1460. if ((error != ERROR_SUCCESS) && (hMapped == NULL)) {
  1461. goto quit;
  1462. }
  1463. //
  1464. // set the context and handle info and clear last error variables
  1465. //
  1466. _InternetSetObjectHandle(lpThreadInfo, hFtpSession, hMapped);
  1467. _InternetSetContext(lpThreadInfo,
  1468. ((INTERNET_HANDLE_OBJECT *)hMapped)->GetContext()
  1469. );
  1470. _InternetClearLastError(lpThreadInfo);
  1471. //
  1472. // quit now if the handle is invalid
  1473. //
  1474. if (error != ERROR_SUCCESS) {
  1475. goto quit;
  1476. }
  1477. //
  1478. // validate handle
  1479. //
  1480. BOOL isLocal;
  1481. BOOL isAsync;
  1482. error = RIsHandleLocal(hMapped,
  1483. &isLocal,
  1484. &isAsync,
  1485. TypeFtpConnectHandle
  1486. );
  1487. if (error != ERROR_SUCCESS) {
  1488. goto quit;
  1489. }
  1490. //
  1491. // perform sync work
  1492. //
  1493. if (!lpThreadInfo->IsAsyncWorkerThread
  1494. || (lpThreadInfo->NestedRequests > 1)) {
  1495. //
  1496. // validate parameters
  1497. //
  1498. if (IsBadStringPtr(lpszDirectory, INTERNET_MAX_PATH_LENGTH + 1)
  1499. || (*lpszDirectory == '\0')) {
  1500. error = ERROR_INVALID_PARAMETER;
  1501. goto quit;
  1502. }
  1503. // in offline mode modifications are disallowed
  1504. // someday we will do internet briefcase but not today
  1505. // BUGBUG there is hole in this API, there is no dwFlags
  1506. // so there is no way to know whether these operations are
  1507. // happening online or offline
  1508. if (((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->GetInternetOpenFlags() &
  1509. INTERNET_FLAG_OFFLINE) {
  1510. error = ERROR_WRITE_PROTECT;
  1511. goto quit;
  1512. }
  1513. if (!lpThreadInfo->IsAsyncWorkerThread && isAsync) {
  1514. // MakeAsyncRequest
  1515. CFsm_FtpCreateDirectory * pFsm;
  1516. pFsm = new CFsm_FtpCreateDirectory(hFtpSession, lpszDirectory);
  1517. if (pFsm != NULL &&
  1518. pFsm->GetError() == ERROR_SUCCESS)
  1519. {
  1520. error = pFsm->QueueWorkItem();
  1521. if ( error == ERROR_IO_PENDING ) {
  1522. fDeref = FALSE;
  1523. }
  1524. }
  1525. else
  1526. {
  1527. error = ERROR_NOT_ENOUGH_MEMORY;
  1528. if ( pFsm )
  1529. {
  1530. error = pFsm->GetError();
  1531. delete pFsm;
  1532. pFsm = NULL;
  1533. }
  1534. }
  1535. //
  1536. // if we're here then ERROR_SUCCESS cannot have been returned from
  1537. // the above calls
  1538. //
  1539. INET_ASSERT(error != ERROR_SUCCESS);
  1540. DEBUG_PRINT(FTP,
  1541. INFO,
  1542. ("processing request asynchronously: error = %d\n",
  1543. error
  1544. ));
  1545. goto quit;
  1546. }
  1547. }
  1548. HINTERNET ftpHandle;
  1549. LocalSetObjectName(hMapped, (LPSTR)lpszDirectory);
  1550. error = RGetLocalHandle(hMapped, &ftpHandle);
  1551. if (error == ERROR_SUCCESS) {
  1552. error = wFtpCreateDirectory(ftpHandle,
  1553. lpszDirectory
  1554. );
  1555. if (error == ERROR_SUCCESS) {
  1556. ((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->ExpireDependents();
  1557. }
  1558. }
  1559. quit:
  1560. _InternetDecNestingCount(nestingLevel);;
  1561. done:
  1562. BOOL success;
  1563. if (error != ERROR_SUCCESS) {
  1564. SetLastError(error);
  1565. success = FALSE;
  1566. DEBUG_ERROR(API, error);
  1567. } else {
  1568. success = TRUE;
  1569. }
  1570. if ((hMapped != NULL) && fDeref) {
  1571. DereferenceObject((LPVOID)hMapped);
  1572. }
  1573. DEBUG_LEAVE_API(success);
  1574. return success;
  1575. }
  1576. INTERNETAPI_(BOOL) FtpRemoveDirectoryA(
  1577. IN HINTERNET hFtpSession,
  1578. IN LPCSTR lpszDirectory
  1579. )
  1580. /*++
  1581. Routine Description:
  1582. Removes a directory at an FTP server
  1583. Arguments:
  1584. hFtpSession - identifies FTP server where directory is to be removed
  1585. lpszDirectory - name of directory to remove
  1586. Return Value:
  1587. BOOL
  1588. Success - TRUE
  1589. Failure - FALSE. Use GetLastError() for more info
  1590. --*/
  1591. {
  1592. DEBUG_ENTER_API((DBG_API,
  1593. Bool,
  1594. "FtpRemoveDirectoryA",
  1595. "%#x, %q",
  1596. hFtpSession,
  1597. lpszDirectory
  1598. ));
  1599. DWORD error;
  1600. LPINTERNET_THREAD_INFO lpThreadInfo;
  1601. DWORD nestingLevel = 0;
  1602. HINTERNET hMapped = NULL;
  1603. BOOL fDeref = TRUE;
  1604. if (!GlobalDataInitialized) {
  1605. error = ERROR_INTERNET_NOT_INITIALIZED;
  1606. goto done;
  1607. }
  1608. //
  1609. // get the thread info block
  1610. //
  1611. lpThreadInfo = InternetGetThreadInfo();
  1612. if (lpThreadInfo == NULL) {
  1613. INET_ASSERT(FALSE);
  1614. error = ERROR_INTERNET_INTERNAL_ERROR;
  1615. goto done;
  1616. }
  1617. _InternetIncNestingCount();
  1618. nestingLevel = 1;
  1619. //
  1620. // map the handle
  1621. //
  1622. error = MapHandleToAddress(hFtpSession, (LPVOID *)&hMapped, FALSE);
  1623. if ((error != ERROR_SUCCESS) && (hMapped == NULL)) {
  1624. goto quit;
  1625. }
  1626. //
  1627. // set the context and handle info and clear last error variables
  1628. //
  1629. _InternetSetObjectHandle(lpThreadInfo, hFtpSession, hMapped);
  1630. _InternetSetContext(lpThreadInfo,
  1631. ((INTERNET_HANDLE_OBJECT *)hMapped)->GetContext()
  1632. );
  1633. _InternetClearLastError(lpThreadInfo);
  1634. //
  1635. // quit now if the handle is invalid
  1636. //
  1637. if (error != ERROR_SUCCESS) {
  1638. goto quit;
  1639. }
  1640. //
  1641. // validate handle
  1642. //
  1643. BOOL isLocal;
  1644. BOOL isAsync;
  1645. error = RIsHandleLocal(hMapped,
  1646. &isLocal,
  1647. &isAsync,
  1648. TypeFtpConnectHandle
  1649. );
  1650. if (error != ERROR_SUCCESS) {
  1651. goto quit;
  1652. }
  1653. //
  1654. // perform sync work
  1655. //
  1656. if (!lpThreadInfo->IsAsyncWorkerThread
  1657. || (lpThreadInfo->NestedRequests > 1)) {
  1658. //
  1659. // validate parameters
  1660. //
  1661. if (IsBadStringPtr(lpszDirectory, INTERNET_MAX_PATH_LENGTH + 1)
  1662. || (*lpszDirectory == '\0')) {
  1663. error = ERROR_INVALID_PARAMETER;
  1664. goto quit;
  1665. }
  1666. // in offline mode modifications are disallowed
  1667. // someday we will do internet briefcase but not today
  1668. // BUGBUG there is hole in this API, there is no dwFlags
  1669. // so there is no way to know whether these operations are
  1670. // happening online or offline
  1671. if (((INTERNET_CONNECT_HANDLE_OBJECT *)hMapped)->GetInternetOpenFlags() &
  1672. INTERNET_FLAG_OFFLINE) {
  1673. error = ERROR_WRITE_PROTECT;
  1674. goto quit;
  1675. }
  1676. if (!lpThreadInfo->IsAsyncWorkerThread && isAsync) {
  1677. // MakeAsyncRequest
  1678. CFsm_FtpRemoveDirectory * pFsm;
  1679. pFsm = new CFsm_FtpRemoveDirectory(hFtpSession, lpszDirectory);
  1680. if (pFsm != NULL &&
  1681. pFsm->GetError() == ERROR_SUCCESS)
  1682. {
  1683. error = pFsm->QueueWorkItem();
  1684. if ( error == ERROR_IO_PENDING ) {
  1685. fDeref = FALSE;
  1686. }
  1687. }
  1688. else
  1689. {
  1690. error = ERROR_NOT_ENOUGH_MEMORY;
  1691. if ( pFsm )
  1692. {
  1693. error = pFsm->GetError();
  1694. delete pFsm;
  1695. pFsm = NULL;
  1696. }
  1697. }
  1698. //
  1699. // if we're here then ERROR_SUCCESS cannot have been returned from
  1700. // the above calls
  1701. //
  1702. INET_ASSERT(error != ERROR_SUCCESS);
  1703. DEBUG_PRINT(FTP,
  1704. INFO,
  1705. ("processing request asynchronously: error = %d\n",
  1706. error
  1707. ));
  1708. goto quit;
  1709. }
  1710. }
  1711. HINTERNET ftpHandle;
  1712. error = RGetLocalHandle(hMapped, &ftpHandle);
  1713. if (error == ERROR_SUCCESS) {
  1714. error = wFtpRemoveDirectory(ftpHandle,
  1715. lpszDirectory
  1716. );
  1717. }
  1718. quit:
  1719. _InternetDecNestingCount(nestingLevel);;
  1720. done:
  1721. BOOL success;
  1722. if (error != ERROR_SUCCESS) {
  1723. SetLastError(error);
  1724. success = FALSE;
  1725. DEBUG_ERROR(API, error);
  1726. } else {
  1727. success = TRUE;
  1728. }
  1729. if ((hMapped != NULL) && fDeref) {
  1730. DereferenceObject((LPVOID)hMapped);
  1731. }
  1732. DEBUG_LEAVE_API(success);
  1733. return success;
  1734. }
  1735. INTERNETAPI_(BOOL) FtpSetCurrentDirectoryA(
  1736. IN HINTERNET hFtpSession,
  1737. IN LPCSTR lpszDirectory
  1738. )
  1739. /*++
  1740. Routine Description:
  1741. Sets the current directory (for this session) at an FTP server
  1742. Arguments:
  1743. hFtpSession - identifies FTP server at which directory is to be set
  1744. lpszDirectory - name of directory to make current working directory
  1745. Return Value:
  1746. BOOL
  1747. Success - TRUE
  1748. Failure - FALSE. Use GetLastError() for more info
  1749. --*/
  1750. {
  1751. DEBUG_ENTER_API((DBG_API,
  1752. Bool,
  1753. "FtpSetCurrentDirectoryA",
  1754. "%#x, %q",
  1755. hFtpSession,
  1756. lpszDirectory
  1757. ));
  1758. DWORD error;
  1759. LPINTERNET_THREAD_INFO lpThreadInfo;
  1760. DWORD nestingLevel = 0;
  1761. HINTERNET hMapped = NULL;
  1762. BOOL fDeref = TRUE;
  1763. if (!GlobalDataInitialized) {
  1764. error = ERROR_INTERNET_NOT_INITIALIZED;
  1765. goto done;
  1766. }
  1767. //
  1768. // get the thread info block
  1769. //
  1770. lpThreadInfo = InternetGetThreadInfo();
  1771. if (lpThreadInfo == NULL) {
  1772. INET_ASSERT(FALSE);
  1773. error = ERROR_INTERNET_INTERNAL_ERROR;
  1774. goto done;
  1775. }
  1776. _InternetIncNestingCount();
  1777. nestingLevel = 1;
  1778. //
  1779. // map the handle
  1780. //
  1781. error = MapHandleToAddress(hFtpSession, (LPVOID *)&hMapped, FALSE);
  1782. if ((error != ERROR_SUCCESS) && (hMapped == NULL)) {
  1783. goto quit;
  1784. }
  1785. //
  1786. // set the context and handle info and clear last error variables
  1787. //
  1788. _InternetSetObjectHandle(lpThreadInfo, hFtpSession, hMapped);
  1789. _InternetSetContext(lpThreadInfo,
  1790. ((INTERNET_HANDLE_OBJECT *)hMapped)->GetContext()
  1791. );
  1792. _InternetClearLastError(lpThreadInfo);
  1793. //
  1794. // quit now if the handle is invalid
  1795. //
  1796. if (error != ERROR_SUCCESS) {
  1797. goto quit;
  1798. }
  1799. //
  1800. // validate handle
  1801. //
  1802. BOOL isLocal;
  1803. BOOL isAsync;
  1804. error = RIsHandleLocal(hMapped,
  1805. &isLocal,
  1806. &isAsync,
  1807. TypeFtpConnectHandle
  1808. );
  1809. if (error != ERROR_SUCCESS) {
  1810. goto quit;
  1811. }
  1812. //
  1813. // perform sync work
  1814. //
  1815. INTERNET_CONNECT_HANDLE_OBJECT * pMapped;
  1816. pMapped = (INTERNET_CONNECT_HANDLE_OBJECT *)hMapped;
  1817. if (!lpThreadInfo->IsAsyncWorkerThread
  1818. || (lpThreadInfo->NestedRequests > 1)) {
  1819. //
  1820. // validate parameters
  1821. //
  1822. if (IsBadStringPtr(lpszDirectory, INTERNET_MAX_PATH_LENGTH + 1)
  1823. || (*lpszDirectory == '\0')) {
  1824. error = ERROR_INVALID_PARAMETER;
  1825. goto quit;
  1826. }
  1827. //
  1828. // if we are not going to net at all (offline), spoof setting directory
  1829. // at the server
  1830. //
  1831. if (pMapped->GetInternetOpenFlags() & INTERNET_FLAG_OFFLINE) {
  1832. goto set_object_cwd;
  1833. }
  1834. //
  1835. // we have to hit the net. This will be an asynchronous operation if the
  1836. // app requested async
  1837. //
  1838. if (!lpThreadInfo->IsAsyncWorkerThread && isAsync) {
  1839. // MakeAsyncRequest
  1840. CFsm_FtpSetCurrentDirectory * pFsm;
  1841. pFsm = new CFsm_FtpSetCurrentDirectory(hFtpSession, lpszDirectory);
  1842. if (pFsm != NULL &&
  1843. pFsm->GetError() == ERROR_SUCCESS)
  1844. {
  1845. error = pFsm->QueueWorkItem();
  1846. if ( error == ERROR_IO_PENDING ) {
  1847. fDeref = FALSE;
  1848. }
  1849. }
  1850. else
  1851. {
  1852. error = ERROR_NOT_ENOUGH_MEMORY;
  1853. if ( pFsm )
  1854. {
  1855. error = pFsm->GetError();
  1856. delete pFsm;
  1857. pFsm = NULL;
  1858. }
  1859. }
  1860. //
  1861. // if we're here then ERROR_SUCCESS cannot have been returned from
  1862. // the above calls
  1863. //
  1864. INET_ASSERT(error != ERROR_SUCCESS);
  1865. DEBUG_PRINT(FTP,
  1866. INFO,
  1867. ("processing request asynchronously: error = %d\n",
  1868. error
  1869. ));
  1870. goto quit;
  1871. }
  1872. }
  1873. HINTERNET ftpHandle;
  1874. error = RGetLocalHandle(hMapped, &ftpHandle);
  1875. if (error == ERROR_SUCCESS) {
  1876. error = wFtpSetCurrentDirectory(ftpHandle, lpszDirectory);
  1877. }
  1878. set_object_cwd:
  1879. if (error == ERROR_SUCCESS) {
  1880. error = pMapped->SetCurrentWorkingDirectory((LPSTR)lpszDirectory);
  1881. }
  1882. quit:
  1883. _InternetDecNestingCount(nestingLevel);;
  1884. done:
  1885. BOOL success;
  1886. if (error != ERROR_SUCCESS) {
  1887. SetLastError(error);
  1888. success = FALSE;
  1889. DEBUG_ERROR(API, error);
  1890. } else {
  1891. success = TRUE;
  1892. }
  1893. if ((hMapped != NULL) && fDeref) {
  1894. DereferenceObject((LPVOID)hMapped);
  1895. }
  1896. DEBUG_LEAVE_API(success);
  1897. return success;
  1898. }
  1899. INTERNETAPI_(BOOL) FtpGetCurrentDirectoryA(
  1900. IN HINTERNET hFtpSession,
  1901. OUT LPSTR lpszCurrentDirectory,
  1902. IN OUT LPDWORD lpdwCurrentDirectory
  1903. )
  1904. /*++
  1905. Routine Description:
  1906. Gets the name of the current working directory for this session at the
  1907. FTP server identified by hFtpSession
  1908. Arguments:
  1909. hFtpSession - identifies FTP server from which to get directory
  1910. lpszCurrentDirectory - buffer where name of current directory will be written
  1911. lpdwCurrentDirectory - IN: size of the buffer
  1912. OUT: number of bytes returned
  1913. Return Value:
  1914. BOOL
  1915. TRUE - *lpdwCurrentDirectory contains number of characters returned
  1916. FALSE - Use GetLastError() to get more info. One of the following will
  1917. be returned:
  1918. ERROR_INVALID_PARAMETER
  1919. ERROR_INSUFFICIENT_BUFFER
  1920. *lpdwCurrentDirectory contains required buffer length
  1921. --*/
  1922. {
  1923. DEBUG_ENTER_API((DBG_API,
  1924. Bool,
  1925. "FtpGetCurrentDirectoryA",
  1926. "%#x, %#x, %#x [%d]",
  1927. hFtpSession,
  1928. lpszCurrentDirectory,
  1929. lpdwCurrentDirectory,
  1930. lpdwCurrentDirectory ? *lpdwCurrentDirectory : 0
  1931. ));
  1932. DWORD error;
  1933. LPINTERNET_THREAD_INFO lpThreadInfo;
  1934. DWORD nestingLevel = 0;
  1935. HINTERNET hMapped = NULL;
  1936. BOOL fDeref = TRUE;
  1937. if (!GlobalDataInitialized) {
  1938. error = ERROR_INTERNET_NOT_INITIALIZED;
  1939. goto done;
  1940. }
  1941. //
  1942. // get the thread info block
  1943. //
  1944. lpThreadInfo = InternetGetThreadInfo();
  1945. if (lpThreadInfo == NULL) {
  1946. INET_ASSERT(FALSE);
  1947. error = ERROR_INTERNET_INTERNAL_ERROR;
  1948. goto done;
  1949. }
  1950. _InternetIncNestingCount();
  1951. nestingLevel = 1;
  1952. //
  1953. // map the handle
  1954. //
  1955. error = MapHandleToAddress(hFtpSession, (LPVOID *)&hMapped, FALSE);
  1956. if ((error != ERROR_SUCCESS) && (hMapped == NULL)) {
  1957. goto quit;
  1958. }
  1959. //
  1960. // set the context and handle info and clear last error variables
  1961. //
  1962. _InternetSetObjectHandle(lpThreadInfo, hFtpSession, hMapped);
  1963. _InternetSetContext(lpThreadInfo,
  1964. ((INTERNET_HANDLE_OBJECT *)hMapped)->GetContext()
  1965. );
  1966. _InternetClearLastError(lpThreadInfo);
  1967. //
  1968. // quit now if the handle is invalid
  1969. //
  1970. if (error != ERROR_SUCCESS) {
  1971. goto quit;
  1972. }
  1973. //
  1974. // validate handle
  1975. //
  1976. BOOL isLocal;
  1977. BOOL isAsync;
  1978. error = RIsHandleLocal(hMapped,
  1979. &isLocal,
  1980. &isAsync,
  1981. TypeFtpConnectHandle
  1982. );
  1983. if (error != ERROR_SUCCESS) {
  1984. goto quit;
  1985. }
  1986. //
  1987. // perform sync work
  1988. //
  1989. if (!lpThreadInfo->IsAsyncWorkerThread
  1990. || (lpThreadInfo->NestedRequests > 1)) {
  1991. //
  1992. // validate parameters
  1993. //
  1994. //
  1995. // lpdwCurrentDirectory must be present (and writeable - we assume it is)
  1996. //
  1997. if (!ARGUMENT_PRESENT(lpdwCurrentDirectory)
  1998. //
  1999. // lpszCurrentDirectory may be not present, but if it is must be writeable
  2000. // by the number of bytes specified in *lpdwCurrentDirectory
  2001. //
  2002. || (ARGUMENT_PRESENT(lpszCurrentDirectory)
  2003. ? IsBadWritePtr(lpszCurrentDirectory, *lpdwCurrentDirectory) : FALSE)) {
  2004. error = ERROR_INVALID_PARAMETER;
  2005. goto quit;
  2006. }
  2007. }
  2008. //
  2009. // we get CWD from the cache only in disconnected state
  2010. //
  2011. if (FGetCWDFromCache(hMapped, lpszCurrentDirectory, lpdwCurrentDirectory)) {
  2012. error = ERROR_SUCCESS;
  2013. goto quit;
  2014. }
  2015. if (!lpThreadInfo->IsAsyncWorkerThread
  2016. && isAsync) {
  2017. // MakeAsyncRequest
  2018. CFsm_FtpGetCurrentDirectory * pFsm;
  2019. pFsm = new CFsm_FtpGetCurrentDirectory(hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
  2020. if (pFsm != NULL) {
  2021. error = pFsm->QueueWorkItem();
  2022. if ( error == ERROR_IO_PENDING ) {
  2023. fDeref = FALSE;
  2024. }
  2025. } else {
  2026. error = ERROR_NOT_ENOUGH_MEMORY;
  2027. }
  2028. //
  2029. // if we're here then ERROR_SUCCESS cannot have been returned from
  2030. // the above calls
  2031. //
  2032. INET_ASSERT(error != ERROR_SUCCESS);
  2033. DEBUG_PRINT(FTP,
  2034. INFO,
  2035. ("processing request asynchronously: error = %d\n",
  2036. error
  2037. ));
  2038. goto quit;
  2039. }
  2040. HINTERNET ftpHandle;
  2041. error = RGetLocalHandle(hMapped, &ftpHandle);
  2042. if (error == ERROR_SUCCESS) {
  2043. error = wFtpGetCurrentDirectory(
  2044. ftpHandle,
  2045. //
  2046. // if the caller supplied no buffer then the
  2047. // buffer length is 0
  2048. //
  2049. ARGUMENT_PRESENT(lpszCurrentDirectory)
  2050. ? *lpdwCurrentDirectory
  2051. : 0,
  2052. lpszCurrentDirectory,
  2053. lpdwCurrentDirectory
  2054. );
  2055. }
  2056. quit:
  2057. _InternetDecNestingCount(nestingLevel);;
  2058. done:
  2059. BOOL success;
  2060. if (error != ERROR_SUCCESS) {
  2061. SetLastError(error);
  2062. success = FALSE;
  2063. DEBUG_ERROR(API, error);
  2064. } else {
  2065. success = TRUE;
  2066. }
  2067. if ((hMapped != NULL) && fDeref) {
  2068. DereferenceObject((LPVOID)hMapped);
  2069. }
  2070. DEBUG_LEAVE_API(success);
  2071. return success;
  2072. }
  2073. INTERNETAPI_(BOOL) FtpCommandA(
  2074. IN HINTERNET hFtpSession,
  2075. IN BOOL fExpectResponse,
  2076. IN DWORD dwFlags,
  2077. IN LPCSTR lpszCommand,
  2078. IN DWORD_PTR dwContext,
  2079. OUT HINTERNET *phFtpCommand OPTIONAL
  2080. )
  2081. /*++
  2082. Routine Description:
  2083. Runs an arbitrary command at the identified FTP server
  2084. Arguments:
  2085. hFtpSession - identifies FTP server where this command is to be run
  2086. fExpectResponse - TRUE if we expect response data
  2087. dwTransferType - how to receive the data - as ASCII text, or as BINARY,
  2088. and open options
  2089. lpszCommand - string describing the command to run
  2090. dwContext - app-supplied context value for call-backs
  2091. phFtpCommand - pointer to an optional handle that will be created if
  2092. a valid data socket is opened, fExpectResponse must
  2093. be set to TRUE phFtpCommand to be filled
  2094. Return Value:
  2095. BOOL
  2096. Success - TRUE
  2097. Failure - FALSE. Use GetLastError() for more info
  2098. --*/
  2099. {
  2100. DEBUG_ENTER_API((DBG_API,
  2101. Bool,
  2102. "FtpCommandA",
  2103. "%#x, %B, %#x, %q, %#x, %#x",
  2104. hFtpSession,
  2105. fExpectResponse,
  2106. dwFlags,
  2107. lpszCommand,
  2108. dwContext,
  2109. phFtpCommand
  2110. ));
  2111. DWORD error;
  2112. HINTERNET hMapped = NULL;
  2113. HINTERNET hCommandMapped = NULL;
  2114. LPINTERNET_THREAD_INFO lpThreadInfo;
  2115. BOOL fDeref = TRUE;
  2116. if (!GlobalDataInitialized) {
  2117. error = ERROR_INTERNET_NOT_INITIALIZED;
  2118. goto quit;
  2119. }
  2120. //
  2121. // get the thread info block
  2122. //
  2123. lpThreadInfo = InternetGetThreadInfo();
  2124. if (lpThreadInfo == NULL) {
  2125. INET_ASSERT(FALSE);
  2126. error = ERROR_INTERNET_INTERNAL_ERROR;
  2127. goto quit;
  2128. }
  2129. //
  2130. // map the handle
  2131. //
  2132. error = MapHandleToAddress(hFtpSession, (LPVOID *)&hMapped, FALSE);
  2133. if ((error != ERROR_SUCCESS) && (hMapped == NULL)) {
  2134. goto quit;
  2135. }
  2136. INTERNET_CONNECT_HANDLE_OBJECT * pMapped;
  2137. pMapped = (INTERNET_CONNECT_HANDLE_OBJECT *)hMapped;
  2138. //
  2139. // this is the handle we are currently working on
  2140. //
  2141. _InternetSetObjectHandle(lpThreadInfo,
  2142. hFtpSession,
  2143. hMapped
  2144. );
  2145. _InternetSetContext(lpThreadInfo, dwContext);
  2146. //
  2147. // clear the per-thread object last error variables
  2148. //
  2149. InternetClearLastError();
  2150. BOOL isLocal;
  2151. BOOL isAsync;
  2152. //
  2153. // make RPC or local-worker function call
  2154. //
  2155. error = RIsHandleLocal(hMapped,
  2156. &isLocal,
  2157. &isAsync,
  2158. TypeFtpConnectHandle
  2159. );
  2160. if ( error != ERROR_SUCCESS ) {
  2161. goto quit;
  2162. }
  2163. //
  2164. // in offline mode we can't execute commands
  2165. //
  2166. if ((pMapped->GetInternetOpenFlags()|dwFlags) & INTERNET_FLAG_OFFLINE) {
  2167. error = ERROR_INTERNET_NO_DIRECT_ACCESS;
  2168. goto quit;
  2169. }
  2170. //
  2171. // validate parameters
  2172. //
  2173. if ( !lpThreadInfo->IsAsyncWorkerThread
  2174. || (lpThreadInfo->NestedRequests > 1) )
  2175. {
  2176. //
  2177. // BUGBUG - reasonable upper limit for command string length?
  2178. //
  2179. if (fExpectResponse && (phFtpCommand == NULL))
  2180. {
  2181. error = ERROR_INVALID_PARAMETER;
  2182. goto quit;
  2183. }
  2184. if (IsBadStringPtr(lpszCommand, 1024)
  2185. || (*lpszCommand == '\0')
  2186. || (fExpectResponse
  2187. && (((dwFlags & FTP_TRANSFER_TYPE_MASK) != FTP_TRANSFER_TYPE_ASCII)
  2188. && ((dwFlags & FTP_TRANSFER_TYPE_MASK) != FTP_TRANSFER_TYPE_BINARY)))
  2189. || ((dwFlags & ~ALLOWED_FTP_FLAGS) != 0))
  2190. {
  2191. error = ERROR_INVALID_PARAMETER;
  2192. goto quit;
  2193. }
  2194. }
  2195. if (!lpThreadInfo->IsAsyncWorkerThread
  2196. && isAsync
  2197. && (dwContext != INTERNET_NO_CALLBACK)) {
  2198. CFsm_FtpCommand * pFsm;
  2199. pFsm = new CFsm_FtpCommand(hFtpSession,
  2200. fExpectResponse,
  2201. dwFlags,
  2202. lpszCommand,
  2203. dwContext,
  2204. phFtpCommand
  2205. );
  2206. if (pFsm != NULL &&
  2207. pFsm->GetError() == ERROR_SUCCESS)
  2208. {
  2209. error = pFsm->QueueWorkItem();
  2210. if ( error == ERROR_IO_PENDING ) {
  2211. fDeref = FALSE;
  2212. }
  2213. }
  2214. else
  2215. {
  2216. error = ERROR_NOT_ENOUGH_MEMORY;
  2217. if ( pFsm )
  2218. {
  2219. error = pFsm->GetError();
  2220. delete pFsm;
  2221. pFsm = NULL;
  2222. }
  2223. }
  2224. //
  2225. // if we're here then ERROR_SUCCESS cannot have been returned from
  2226. // the above calls
  2227. //
  2228. INET_ASSERT(error != ERROR_SUCCESS);
  2229. DEBUG_PRINT(FTP,
  2230. INFO,
  2231. ("processing request asynchronously: error = %d\n",
  2232. error
  2233. ));
  2234. goto quit;
  2235. }
  2236. INET_ASSERT (error == ERROR_SUCCESS) ;
  2237. if ( fExpectResponse )
  2238. {
  2239. //
  2240. // create the handle object now. This can be used to cancel the async
  2241. // operation, or the sync operation if InternetCloseHandle() is called
  2242. // from a different thread
  2243. //
  2244. error = RMakeFtpFileObjectHandle(hMapped,
  2245. &hCommandMapped,
  2246. (CLOSE_HANDLE_FUNC)wFtpCloseFile,
  2247. dwContext
  2248. );
  2249. if (error != ERROR_SUCCESS) {
  2250. goto quit;
  2251. }
  2252. //
  2253. // this new handle will be used in callbacks
  2254. //
  2255. }
  2256. HINTERNET ftpHandle;
  2257. error = RGetLocalHandle(hMapped, &ftpHandle);
  2258. if (error == ERROR_SUCCESS)
  2259. {
  2260. //InternetSetContext(dwContext);
  2261. error = wFtpCommand(ftpHandle,
  2262. fExpectResponse,
  2263. dwFlags,
  2264. lpszCommand
  2265. );
  2266. if ( fExpectResponse )
  2267. {
  2268. //
  2269. // FTP can only have one active operation per session, so we just return
  2270. // this session handle as the find handle
  2271. //
  2272. ((FTP_FILE_HANDLE_OBJECT *)hCommandMapped)->SetFileHandle(
  2273. ftpHandle
  2274. );
  2275. *phFtpCommand = ((HANDLE_OBJECT *)hCommandMapped)->GetPseudoHandle();
  2276. }
  2277. }
  2278. quit:
  2279. BOOL success;
  2280. if ((hMapped != NULL) && fDeref) {
  2281. DereferenceObject((LPVOID)hMapped);
  2282. }
  2283. if (error != ERROR_SUCCESS) {
  2284. SetLastError(error);
  2285. success = FALSE;
  2286. DEBUG_ERROR(API, error);
  2287. } else {
  2288. success = TRUE;
  2289. }
  2290. DEBUG_LEAVE_API(success);
  2291. return success;
  2292. }
  2293. INTERNETAPI_(DWORD) FtpGetFileSize(
  2294. IN HINTERNET hFile,
  2295. OUT LPDWORD lpdwFileSizeHigh OPTIONAL
  2296. )
  2297. /*++
  2298. Routine Description:
  2299. Same as base Win32 GetFileSize() function. Returns size of file as reported
  2300. by server (if known). For the IIS FTP server, the file size is reported in
  2301. the response string when we open the file. For other (Unix) server, we need
  2302. to send a "SIZE <filename>" command
  2303. Arguments:
  2304. hFile - hInternet of FTP_FILE_HANDLE_OBJECT returned by
  2305. FtpOpenFile()
  2306. lpdwFileSizeHigh - optional pointer to returned high 32 bits of file size
  2307. Return Value:
  2308. DWORD
  2309. Success - low 32 bits of size of file associated with hFile. If
  2310. 0xFFFFFFFF is returned then GetLastError() must be used to
  2311. determine if an error occurred. If GetLastError() returns
  2312. ERROR_SUCCESS then the file size is 4GB-1
  2313. Failure - 0xFFFFFFFF. GetLastError() returns possible error codes:
  2314. ERROR_INVALID_HANDLE
  2315. The handle is not a valid HINTERNET
  2316. ERROR_INTERNET_INCORRECT_HANDLE_TYPE
  2317. The handle is a valid HINTERNET, but does not describe
  2318. a FTP FILE handle object
  2319. ERROR_INVALID_PARAMETER
  2320. lpdwFileSizeHigh is an invalid pointer
  2321. ERROR_INTERNET_OVERFLOW
  2322. The server reported that the file size was >0xFFFFFFFF,
  2323. but lpdwFileSizeHigh was NULL
  2324. --*/
  2325. {
  2326. DEBUG_ENTER_API((DBG_API,
  2327. Dword,
  2328. "FtpGetFileSize",
  2329. "%#x, %#x",
  2330. hFile,
  2331. lpdwFileSizeHigh
  2332. ));
  2333. DWORD error = ERROR_SUCCESS;
  2334. DWORD dwSizeLow = 0;
  2335. DWORD dwSizeHigh = 0;
  2336. LPINTERNET_THREAD_INFO lpThreadInfo;
  2337. DWORD nestingLevel = 0;
  2338. FTP_FILE_HANDLE_OBJECT * pFileMapped = NULL;
  2339. INTERNET_CONNECT_HANDLE_OBJECT * pConnectMapped = NULL;
  2340. HINTERNET hMapped;
  2341. BOOL fFile = TRUE;
  2342. BOOL isAsync;
  2343. BOOL isLocal;
  2344. BOOL fDeref = TRUE;
  2345. BOOL fDerefSession = FALSE;
  2346. if (!GlobalDataInitialized) {
  2347. error = ERROR_INTERNET_NOT_INITIALIZED;
  2348. goto quit;
  2349. }
  2350. lpThreadInfo = InternetGetThreadInfo();
  2351. if (lpThreadInfo == NULL) {
  2352. INET_ASSERT(FALSE);
  2353. error = ERROR_INTERNET_INTERNAL_ERROR;
  2354. goto quit;
  2355. }
  2356. //
  2357. // validate parameters
  2358. //
  2359. if (lpdwFileSizeHigh != NULL) {
  2360. if(IsBadWritePtr(lpdwFileSizeHigh, sizeof(*lpdwFileSizeHigh))) {
  2361. error = ERROR_INVALID_PARAMETER;
  2362. goto quit;
  2363. }
  2364. *lpdwFileSizeHigh = 0;
  2365. }
  2366. error = MapHandleToAddress(hFile, (LPVOID *)&pFileMapped, FALSE);
  2367. if ((error != ERROR_SUCCESS) && (pFileMapped == NULL)) {
  2368. goto quit;
  2369. }
  2370. hMapped = pFileMapped;
  2371. _InternetSetObjectHandle(lpThreadInfo, hFile, pFileMapped);
  2372. _InternetSetContext(lpThreadInfo,
  2373. ((FTP_FILE_HANDLE_OBJECT *)pFileMapped)->GetContext()
  2374. );
  2375. _InternetClearLastError(lpThreadInfo);
  2376. error = RIsHandleLocal(hMapped, // mapped file handle
  2377. &isLocal,
  2378. &isAsync,
  2379. TypeFtpFileHandle
  2380. );
  2381. if (error != ERROR_SUCCESS) {
  2382. #if 0
  2383. error = RIsHandleLocal((HINTERNET) hMapped,
  2384. &isLocal,
  2385. &isAsync,
  2386. TypeFtpConnectHandle
  2387. );
  2388. if ( error != ERROR_SUCESS ) {
  2389. goto quit;
  2390. }
  2391. //else ...
  2392. fFile = FALSE;
  2393. pConnectMapped = (INTERNET_CONNECT_HANDLE_OBJECT *) pFileMapped;
  2394. hMapped = (HINTERNET) pConnectMapped;
  2395. pFileMapped = NULL;
  2396. #else
  2397. goto quit;
  2398. #endif
  2399. }
  2400. //
  2401. // If we're reading from the cache we need to find the file size via the cache api.
  2402. //
  2403. //
  2404. // BUGBUG [arthurbi] - Need to test the cached code path, as it may be that we're
  2405. // not properly reading from the cached handle
  2406. //
  2407. if (fFile && pFileMapped->IsCacheReadInProgress())
  2408. {
  2409. CACHE_ENTRY_INFO ceiCacheInfo;
  2410. DWORD dwBuffSize = sizeof(ceiCacheInfo);
  2411. if (!pFileMapped->CacheGetUrlInfo(
  2412. &ceiCacheInfo,
  2413. &dwBuffSize
  2414. ))
  2415. {
  2416. error = GetLastError();
  2417. goto quit;
  2418. }
  2419. dwSizeLow = ceiCacheInfo.dwSizeLow;
  2420. dwSizeHigh = ceiCacheInfo.dwSizeHigh;
  2421. goto quit;
  2422. }
  2423. //
  2424. // if we already know the size of the file from a 150 response, return it
  2425. // immediately, else we need to send a "SIZE" request to the server to get
  2426. // it
  2427. //
  2428. LPFTP_SESSION_INFO lpSessionInfo;
  2429. HINTERNET hHandleMapped;
  2430. HINTERNET hFtpSession;
  2431. //
  2432. // find the FTP_SESSION_INFO and ensure it is set up to receive data
  2433. //
  2434. error = RGetLocalHandle(hMapped, &hFtpSession);
  2435. if (error == ERROR_SUCCESS) {
  2436. if (!FindFtpSession( hFtpSession, &lpSessionInfo)) {
  2437. error = ERROR_INVALID_HANDLE;
  2438. goto quit;
  2439. }
  2440. }
  2441. else
  2442. {
  2443. goto quit;
  2444. }
  2445. fDerefSession = TRUE;
  2446. #if 0
  2447. if (!fFile)
  2448. {
  2449. if (!lpThreadInfo->IsAsyncWorkerThread && isAsync)
  2450. {
  2451. CFsm_FtpGetFileSize * pFsm;
  2452. pFsm = new CFsm_FtpGetFileSize(hFile);
  2453. if (pFsm != NULL )
  2454. {
  2455. error = pFsm->QueueWorkItem();
  2456. if ( error == ERROR_IO_PENDING ) {
  2457. fDeref = FALSE;
  2458. }
  2459. }
  2460. else
  2461. {
  2462. error = ERROR_NOT_ENOUGH_MEMORY;
  2463. }
  2464. //
  2465. // if we're here then ERROR_SUCCESS cannot have been returned from
  2466. // the above calls
  2467. //
  2468. INET_ASSERT(error != ERROR_SUCCESS);
  2469. DEBUG_PRINT(FTP,
  2470. INFO,
  2471. ("processing request asynchronously: error = %d\n",
  2472. error
  2473. ));
  2474. goto quit;
  2475. }
  2476. error = wFtpGetFileSize(hMapped, lpSessionInfo, &dwSizeLow, &dwSizeHigh);
  2477. }
  2478. else
  2479. #endif
  2480. {
  2481. INET_ASSERT(fFile);
  2482. //
  2483. // if FFTP_KNOWN_FILE_SIZE is set then we already know the file size
  2484. //
  2485. if ( (lpSessionInfo->Flags & FFTP_KNOWN_FILE_SIZE) == 0 ) {
  2486. error = ERROR_INTERNET_ITEM_NOT_FOUND;
  2487. goto quit;
  2488. }
  2489. // set dwSizeLow here..
  2490. dwSizeLow = lpSessionInfo->dwFileSizeLow;
  2491. }
  2492. quit:
  2493. if ( fDerefSession ) {
  2494. DereferenceFtpSession(lpSessionInfo);
  2495. }
  2496. if (pFileMapped != NULL && fDeref) {
  2497. DereferenceObject((LPVOID)pFileMapped);
  2498. }
  2499. if ( error != ERROR_SUCCESS )
  2500. {
  2501. dwSizeLow = 0xffffffff;
  2502. }
  2503. SetLastError(error);
  2504. DEBUG_LEAVE_API(dwSizeLow);
  2505. return dwSizeLow;
  2506. }
  2507. INTERNETAPI_(BOOL) FtpGetSystemNameA(
  2508. IN HINTERNET hSession,
  2509. OUT LPSTR lpszBuffer,
  2510. IN OUT LPDWORD lpdwBufferLength
  2511. )
  2512. /*++
  2513. Routine Description:
  2514. Returns the results of a "SYST" command sent to the FTP server to identify
  2515. the FTP server/operating system type in use at the site. The server will
  2516. return a string of the form "215 Windows_NT Version 4.0". The FTP status
  2517. code substring "215 " will be stripped before the string is returned to the
  2518. caller
  2519. Arguments:
  2520. hSession - a HINTERNET returned by InternetConnect() describing
  2521. an FTP session
  2522. lpszBuffer - pointer to buffer where output string will be stored
  2523. lpdwBufferLength - IN: the size of lpszBuffer in BYTEs
  2524. OUT: if successful, the number of CHARACTERs comprising
  2525. the string in lpszBuffer minus 1 for the string
  2526. terminator. If ERROR_INSUFFICIENT_BUFFER is returned,
  2527. the number of BYTEs required to hold the string,
  2528. including the string termination character
  2529. Return Value:
  2530. BOOL
  2531. Success - TRUE
  2532. Failure - FALSE. Use GetLastError() to return the error information.
  2533. Possible error codes are:
  2534. ERROR_INVALID_HANDLE
  2535. The handle is not a valid HINTERNET
  2536. ERROR_INTERNET_INCORRECT_HANDLE_TYPE
  2537. The handle is a valid HINTERNET, but does not describe
  2538. an FTP connect handle object
  2539. ERROR_INVALID_PARAMETER
  2540. lpszBuffer or lpdwBufferLength are invalid pointers
  2541. ERROR_INSUFFICIENT_BUFFER
  2542. The buffer size given in *lpdwBufferLength is too small
  2543. to hold the resultant string. The required buffer length
  2544. is returned in *lpdwBufferLength
  2545. --*/
  2546. {
  2547. DEBUG_ENTER_API((DBG_API,
  2548. Bool,
  2549. "FtpGetSystemNameA",
  2550. "%#x, %#x, %#x [%d]",
  2551. hSession,
  2552. lpszBuffer,
  2553. lpdwBufferLength,
  2554. lpdwBufferLength ? *lpdwBufferLength : 0
  2555. ));
  2556. DWORD error;
  2557. INTERNET_CONNECT_HANDLE_OBJECT * phMapped = NULL;
  2558. if (!GlobalDataInitialized) {
  2559. error = ERROR_INTERNET_NOT_INITIALIZED;
  2560. goto quit;
  2561. }
  2562. //
  2563. // validate parameters
  2564. //
  2565. if ((lpdwBufferLength == NULL)
  2566. || IsBadWritePtr(lpdwBufferLength, sizeof(*lpdwBufferLength))
  2567. || IsBadWritePtr(lpszBuffer, *lpdwBufferLength)) {
  2568. error = ERROR_INVALID_PARAMETER;
  2569. goto quit;
  2570. }
  2571. error = MapHandleToAddress(hSession, (LPVOID *)&phMapped, FALSE);
  2572. if ((error != ERROR_SUCCESS) && (phMapped == NULL)) {
  2573. goto quit;
  2574. }
  2575. quit:
  2576. if (phMapped != NULL) {
  2577. DereferenceObject((LPVOID)phMapped);
  2578. }
  2579. BOOL success;
  2580. if (error == ERROR_SUCCESS) {
  2581. success = TRUE;
  2582. } else {
  2583. DEBUG_ERROR(API, error);
  2584. SetLastError(error);
  2585. success = FALSE;
  2586. }
  2587. DEBUG_LEAVE_API(success);
  2588. return success;
  2589. }
  2590. //
  2591. // Internet subordinate functions
  2592. //
  2593. BOOL
  2594. FtpFindNextFileA(
  2595. IN HINTERNET hFind,
  2596. OUT LPWIN32_FIND_DATA lpFindFileData
  2597. )
  2598. /*++
  2599. Routine Description:
  2600. Returns the directory entry in the list returned by FtpFindFirstFile()
  2601. Assumes: 1. We are being called from InternetFindNextFile() which has
  2602. already validated the parameters, set the thread variables,
  2603. and cleared the object last error info
  2604. Arguments:
  2605. hFind - find object handle, returned by FtpFindFirstFile()
  2606. lpFindFileData - Pointer to a buffer that will contain WIN32_FIND_DATA
  2607. information when this call succeeds.
  2608. Return Value:
  2609. BOOL
  2610. Success - TRUE
  2611. Failure - FALSE. Call GetLastError() for more info
  2612. --*/
  2613. {
  2614. DEBUG_ENTER((DBG_FTP,
  2615. Bool,
  2616. "FtpFindNextFileA",
  2617. "%#x, %#x",
  2618. hFind,
  2619. lpFindFileData
  2620. ));
  2621. INET_ASSERT(GlobalDataInitialized);
  2622. DWORD error, errorCache;
  2623. BOOL isLocal;
  2624. BOOL isAsync, fIsHtml = FALSE;
  2625. //
  2626. // make RPC or local-worker function call
  2627. //
  2628. error = RIsHandleLocal(hFind,
  2629. &isLocal,
  2630. &isAsync,
  2631. TypeFtpFindHandle
  2632. );
  2633. if (error != ERROR_SUCCESS) {
  2634. //
  2635. // if the handle is actually a HTML FTP find handle, then we allow the
  2636. // operation. Note: we can do this because FtpFindNextFile() is not
  2637. // exported, so a rogue app cannot call this function after opening the
  2638. // handle via InternetOpenUrl()
  2639. //
  2640. error = RIsHandleLocal(hFind,
  2641. &isLocal,
  2642. &isAsync,
  2643. TypeFtpFindHandleHtml
  2644. );
  2645. fIsHtml = (error == ERROR_SUCCESS);
  2646. }
  2647. FTP_FIND_HANDLE_OBJECT * pFind;
  2648. pFind = (FTP_FIND_HANDLE_OBJECT *)hFind;
  2649. if (error == ERROR_SUCCESS) {
  2650. if (pFind->IsCacheReadInProgress()) {
  2651. //we should never get here when the it is a findhtml type handle.
  2652. // internetopenurl should skim it off the top.
  2653. INET_ASSERT(!fIsHtml);
  2654. DWORD dwLen = sizeof(WIN32_FIND_DATA);
  2655. error = pFind->ReadCache((LPBYTE)lpFindFileData,
  2656. dwLen,
  2657. &dwLen);
  2658. if ((error == ERROR_SUCCESS) && !dwLen) {
  2659. error = ERROR_NO_MORE_FILES;
  2660. }
  2661. goto quit;
  2662. } else {
  2663. INET_ASSERT(!(pFind->GetInternetOpenFlags() & INTERNET_FLAG_OFFLINE));
  2664. }
  2665. HINTERNET ftpHandle;
  2666. error = RGetLocalHandle(hFind, &ftpHandle);
  2667. if (error == ERROR_SUCCESS) {
  2668. if (pFind->IsEmpty()) {
  2669. error = ERROR_NO_MORE_FILES;
  2670. } else {
  2671. error = wFtpFindNextFile(ftpHandle, lpFindFileData);
  2672. }
  2673. }
  2674. }
  2675. errorCache = error;
  2676. if (error == ERROR_SUCCESS) {
  2677. if (((INTERNET_CONNECT_HANDLE_OBJECT *)hFind)->IsCacheWriteInProgress()) {
  2678. // write here only if it is a native find handle
  2679. // otherwise, internetreadurl will take care
  2680. if (!fIsHtml) {
  2681. errorCache = ((INTERNET_CONNECT_HANDLE_OBJECT *)hFind)->WriteCache(
  2682. (LPBYTE)lpFindFileData,
  2683. sizeof(WIN32_FIND_DATA)
  2684. );
  2685. }
  2686. }
  2687. }
  2688. if (errorCache != ERROR_SUCCESS) {
  2689. if (!fIsHtml) {
  2690. InbLocalEndCacheWrite(hFind, NULL, (errorCache == ERROR_NO_MORE_FILES));
  2691. }
  2692. }
  2693. quit:
  2694. BOOL success;
  2695. if (error != ERROR_SUCCESS) {
  2696. success = FALSE;
  2697. DEBUG_ERROR(API, error);
  2698. } else {
  2699. success = TRUE;
  2700. }
  2701. DEBUG_LEAVE(success);
  2702. SetLastError(error);
  2703. return success;
  2704. }
  2705. BOOL
  2706. FtpReadFile(
  2707. IN HINTERNET hFile,
  2708. IN LPVOID lpBuffer,
  2709. IN DWORD dwNumberOfBytesToRead,
  2710. OUT LPDWORD lpdwNumberOfBytesRead
  2711. )
  2712. /*++
  2713. Routine Description:
  2714. Reads number of bytes from file at FTP server
  2715. Assumes: 1. We are being called from InternetReadFile() which has
  2716. already validated the parameters, handled the zero byte
  2717. read case, set the thread variables, and cleared the object
  2718. last error info
  2719. Arguments:
  2720. hFile - file object handle, returned by FtpOpenFile()
  2721. lpBuffer - pointer to user's buffer
  2722. dwNumberOfBytesToRead - size of user's buffer
  2723. lpdwNumberOfBytesRead - number of bytes copied to user's buffer on output
  2724. Return Value:
  2725. BOOL
  2726. Success - TRUE
  2727. Failure - FALSE. Call GetLastError() for more info
  2728. --*/
  2729. {
  2730. DEBUG_ENTER((DBG_FTP,
  2731. Bool,
  2732. "FtpReadFile",
  2733. "%#x, %#x, %d, %#x",
  2734. hFile,
  2735. lpBuffer,
  2736. dwNumberOfBytesToRead,
  2737. lpdwNumberOfBytesRead
  2738. ));
  2739. INET_ASSERT(GlobalDataInitialized);
  2740. DWORD error, errorCache;
  2741. BOOL isLocal;
  2742. BOOL isAsync;
  2743. FTP_FILE_HANDLE_OBJECT * pFile = (FTP_FILE_HANDLE_OBJECT *)hFile;
  2744. //
  2745. // make RPC or local-worker function call
  2746. //
  2747. error = RIsHandleLocal(hFile,
  2748. &isLocal,
  2749. &isAsync,
  2750. TypeFtpFileHandle
  2751. );
  2752. if (error == ERROR_SUCCESS) {
  2753. if (pFile->IsCacheReadInProgress()) {
  2754. error = pFile->ReadCache((LPBYTE)lpBuffer,
  2755. dwNumberOfBytesToRead,
  2756. lpdwNumberOfBytesRead
  2757. );
  2758. if (!*lpdwNumberOfBytesRead || (error != ERROR_SUCCESS)) {
  2759. //
  2760. // don't do anything here so we don't barf when someone
  2761. // does an extraneous read. The cache stream gets closed
  2762. // when the handle is closed. bug#9086
  2763. // ((FTP_FILE_HANDLE_OBJECT *)hFile)->EndCacheRetrieval();
  2764. //
  2765. }
  2766. //
  2767. // quit whether we succeed or we fail
  2768. //
  2769. goto quit;
  2770. } else {
  2771. INET_ASSERT(!((pFile->GetInternetOpenFlags()|pFile->GetCacheFlags())
  2772. & INTERNET_FLAG_OFFLINE));
  2773. }
  2774. HINTERNET ftpHandle;
  2775. error = RGetLocalHandle(hFile, &ftpHandle);
  2776. if (error == ERROR_SUCCESS) {
  2777. error = wFtpReadFile(ftpHandle,
  2778. lpBuffer,
  2779. dwNumberOfBytesToRead,
  2780. lpdwNumberOfBytesRead
  2781. );
  2782. }
  2783. }
  2784. if (error == ERROR_SUCCESS) {
  2785. if (pFile->IsCacheWriteInProgress()) {
  2786. if (!*lpdwNumberOfBytesRead) {
  2787. DEBUG_PRINT(CACHE,
  2788. INFO,
  2789. ("Cache write complete\r\n"
  2790. ));
  2791. errorCache = InbLocalEndCacheWrite(hFile, NULL, TRUE);
  2792. INET_ASSERT(error == ERROR_SUCCESS);
  2793. goto quit;
  2794. }
  2795. INET_ASSERT(pFile->IsCacheReadInProgress() == FALSE);
  2796. if (pFile->WriteCache((LPBYTE)lpBuffer,
  2797. *lpdwNumberOfBytesRead
  2798. ) != ERROR_SUCCESS) {
  2799. DEBUG_PRINT(CACHE,
  2800. ERROR,
  2801. ("Error in Cache write\n"
  2802. ));
  2803. errorCache = InbLocalEndCacheWrite(hFile, NULL, FALSE);
  2804. INET_ASSERT(error == ERROR_SUCCESS);
  2805. }
  2806. }
  2807. }
  2808. quit:
  2809. BOOL success;
  2810. if (error != ERROR_SUCCESS) {
  2811. SetLastError(error);
  2812. success = FALSE;
  2813. DEBUG_ERROR(API, error);
  2814. } else {
  2815. success = TRUE;
  2816. }
  2817. DEBUG_LEAVE(success);
  2818. return success;
  2819. }
  2820. BOOL
  2821. FtpWriteFile(
  2822. IN HINTERNET hFile,
  2823. IN LPVOID lpBuffer,
  2824. IN DWORD dwNumberOfBytesToWrite,
  2825. OUT LPDWORD lpdwNumberOfBytesWritten
  2826. )
  2827. /*++
  2828. Routine Description:
  2829. Writes a number of bytes from the user's buffer to an open file on an FTP
  2830. server
  2831. Assumes: 1. We are being called from InternetWriteFile() which has
  2832. already validated the parameters, handled the zero byte
  2833. read case, set the thread variables, and cleared the object
  2834. last error info
  2835. Arguments:
  2836. hFile - file object handle, returned by FtpOpenFile()
  2837. lpBuffer - pointer to user's buffer
  2838. dwNumberOfBytesToWrite - number of bytes to write from user's buffer
  2839. lpdwNumberOfBytesWritten - number of bytes actually written
  2840. Return Value:
  2841. BOOL
  2842. Success - TRUE
  2843. Failure - FALSE. Call GetLastError() for more info
  2844. --*/
  2845. {
  2846. DEBUG_ENTER((DBG_FTP,
  2847. Bool,
  2848. "FtpWriteFile",
  2849. "%#x, %#x, %d, %#x",
  2850. hFile,
  2851. lpBuffer,
  2852. dwNumberOfBytesToWrite,
  2853. lpdwNumberOfBytesWritten
  2854. ));
  2855. INET_ASSERT(GlobalDataInitialized);
  2856. DWORD error;
  2857. BOOL isLocal;
  2858. BOOL isAsync;
  2859. //
  2860. // make RPC or local-worker function call
  2861. //
  2862. error = RIsHandleLocal(hFile,
  2863. &isLocal,
  2864. &isAsync,
  2865. TypeFtpFileHandle
  2866. );
  2867. if (error == ERROR_SUCCESS) {
  2868. HINTERNET ftpHandle;
  2869. error = RGetLocalHandle(hFile, &ftpHandle);
  2870. if (error == ERROR_SUCCESS) {
  2871. error = wFtpWriteFile(ftpHandle,
  2872. lpBuffer,
  2873. dwNumberOfBytesToWrite,
  2874. lpdwNumberOfBytesWritten
  2875. );
  2876. if (error == ERROR_SUCCESS) {
  2877. // expire the url if it exists and it's
  2878. // parent directory
  2879. if( !((FTP_FILE_HANDLE_OBJECT *)hFile)->IsForcedExpirySet()){
  2880. ((FTP_FILE_HANDLE_OBJECT *)hFile)->ExpireDependents();
  2881. ((FTP_FILE_HANDLE_OBJECT *)hFile)->SetForcedExpiry(TRUE);
  2882. ((FTP_FILE_HANDLE_OBJECT *)hFile)->ExpireUrl();
  2883. }
  2884. }
  2885. }
  2886. }
  2887. BOOL success;
  2888. if (error != ERROR_SUCCESS) {
  2889. SetLastError(error);
  2890. success = FALSE;
  2891. DEBUG_ERROR(API, error);
  2892. } else {
  2893. success = TRUE;
  2894. }
  2895. DEBUG_LEAVE(success);
  2896. return success;
  2897. }
  2898. DWORD
  2899. pFtpGetUrlString(
  2900. IN INTERNET_SCHEME SchemeType,
  2901. IN LPSTR lpszTargetName,
  2902. IN LPSTR lpszCWD,
  2903. IN LPSTR lpszObjectName,
  2904. IN LPSTR lpszExtension,
  2905. IN DWORD dwPort,
  2906. OUT LPSTR *lplpUrlName,
  2907. OUT LPDWORD lpdwUrlLen
  2908. )
  2909. /*++
  2910. Routine Description:
  2911. This routine returns a LocaAlloc'ed buffer containing an FTP URL constructed
  2912. from the TargetHost, CWD, and the ObjectName. The caller is responsible
  2913. for freeing the memory.
  2914. Arguments:
  2915. SchemeType - protocol scheme (INTERNET_SCHEME_FTP)
  2916. lpszTargetName - name of server
  2917. lpszCWD - current directory at server
  2918. lpszObjectName - name of file. If NULL or empty then the URL is for a
  2919. directory
  2920. lpszExtension - file extension. NOT USED
  2921. dwPort - port at server
  2922. lplpUrlName - pointer to returned URL
  2923. lpdwUrlLen - pointer to returned URL length
  2924. Return Value:
  2925. DWORD
  2926. Success - ERROR_SUCCESS
  2927. Failure - ERROR_NOT_ENOUGH_MEMORY
  2928. --*/
  2929. {
  2930. DWORD dwError, dwLen, dwT, dwTargetNameLen=0, dwObjectNameLen=0;
  2931. char cBuff[INTERNET_MAX_URL_LENGTH], cBuff1[INTERNET_MAX_URL_LENGTH];//????
  2932. URL_COMPONENTS sUrlComp;
  2933. DWORD dwSav, i, ccFirst, ccTmp;
  2934. INET_ASSERT(lpszTargetName);
  2935. INET_ASSERT(lpszObjectName || lpszCWD);
  2936. //
  2937. // NULL or empty object name == directory
  2938. //
  2939. if ((lpszObjectName != NULL) && (*lpszObjectName == '\0')) {
  2940. lpszObjectName = NULL;
  2941. }
  2942. memcpy(cBuff1, "/", sizeof("/"));
  2943. ccFirst = 2;
  2944. // add the current directory only if the path is a relative one
  2945. if (lpszCWD && !(lpszObjectName && (*lpszObjectName == '/')) ) {
  2946. if (*lpszCWD == '/') {
  2947. ++lpszCWD;
  2948. }
  2949. ccTmp = lstrlen(lpszCWD);
  2950. if (ccTmp > sizeof(cBuff1) - 2)
  2951. {
  2952. dwError = ERROR_INVALID_PARAMETER;
  2953. goto Cleanup;
  2954. }
  2955. memcpy(cBuff1 + 1, lpszCWD, ccTmp + 1);
  2956. ccFirst += ccTmp;
  2957. INET_ASSERT(lpszCWD[ccTmp - 1] == '/');
  2958. }
  2959. if (lpszObjectName) {
  2960. if (*lpszObjectName == '/') {
  2961. lpszObjectName++;
  2962. }
  2963. ccTmp = lstrlen(lpszObjectName);
  2964. if (ccTmp > sizeof(cBuff1) - ccFirst)
  2965. {
  2966. dwError = ERROR_INVALID_PARAMETER;
  2967. goto Cleanup;
  2968. }
  2969. memcpy(cBuff1 + ccFirst - 1, lpszObjectName, ccTmp + 1);
  2970. }
  2971. memset(&sUrlComp, 0, sizeof(URL_COMPONENTS));
  2972. sUrlComp.dwStructSize = sizeof(URL_COMPONENTS);
  2973. sUrlComp.nScheme = INTERNET_SCHEME_FTP;
  2974. sUrlComp.lpszHostName = lpszTargetName;
  2975. sUrlComp.lpszUrlPath = cBuff1;
  2976. sUrlComp.nPort = (INTERNET_PORT)dwPort;
  2977. dwSav = sizeof(cBuff);
  2978. if(!InternetCreateUrl(&sUrlComp, 0, cBuff, &dwSav)){
  2979. dwError = GetLastError();
  2980. goto Cleanup;
  2981. }
  2982. // BUGBUG, this is because InternetCreateUrl is not returning
  2983. // the correct size
  2984. dwSav = strlen(cBuff)+5;
  2985. for(i=0;i<2;++i) {
  2986. *lplpUrlName = (LPSTR)ALLOCATE_MEMORY(LPTR, dwSav);
  2987. if (*lplpUrlName) {
  2988. if(!InternetCanonicalizeUrl(cBuff, *lplpUrlName, &dwSav, ICU_NO_ENCODE)){
  2989. FREE_MEMORY(*lplpUrlName);
  2990. // general paranoia
  2991. *lplpUrlName = NULL;
  2992. dwError = GetLastError();
  2993. if ((i == 1) || (dwError != ERROR_INSUFFICIENT_BUFFER)) {
  2994. goto Cleanup;
  2995. }
  2996. }
  2997. else {
  2998. dwError = ERROR_SUCCESS;
  2999. *lpdwUrlLen = dwSav;
  3000. break;
  3001. }
  3002. }
  3003. else {
  3004. SetLastError(dwError = ERROR_NOT_ENOUGH_MEMORY);
  3005. goto Cleanup;
  3006. }
  3007. }
  3008. Cleanup:
  3009. if (dwError != ERROR_SUCCESS) {
  3010. INET_ASSERT(!*lplpUrlName);
  3011. *lpdwUrlLen = 0;
  3012. }
  3013. return (dwError);
  3014. }
  3015. PRIVATE
  3016. BOOL
  3017. FBeginCacheReadProcessing(
  3018. IN HINTERNET hFtp,
  3019. IN LPCSTR lpszFileName,
  3020. IN DWORD dwAccess,
  3021. IN DWORD dwFlags,
  3022. IN DWORD_PTR dwContext,
  3023. IN BOOL fIsHtmlFind
  3024. )
  3025. /*++
  3026. Routine Description:
  3027. Sets up to read FTP data from the cache
  3028. Arguments:
  3029. hFtp A mapped handle to an ftp data transfer object (FtpOpenFile/FtpFindFirstFile)
  3030. lpszFileName ftp file to look for in the cache
  3031. dwAccess Accesstype eg: GENERIC_READ
  3032. dwFlags caching flags
  3033. dwContext async context
  3034. Returns:
  3035. TRUE of started cache reading, FALSE otherwise
  3036. Comments:
  3037. --*/
  3038. {
  3039. DEBUG_ENTER((DBG_FTP,
  3040. Bool,
  3041. "FBeginCacheReadProcessing",
  3042. "%#x, %q, %d, %08x, %x, %B",
  3043. hFtp,
  3044. lpszFileName,
  3045. dwAccess,
  3046. dwFlags,
  3047. dwContext,
  3048. fIsHtmlFind
  3049. ));
  3050. DWORD dwError = ERROR_SUCCESS;
  3051. URLGEN_FUNC fn = pFtpGetUrlString;
  3052. LPCACHE_ENTRY_INFO lpCEI = NULL;
  3053. FTP_FILE_HANDLE_OBJECT *pFtp = (FTP_FILE_HANDLE_OBJECT *)hFtp;
  3054. if (((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->IsCacheReadInProgress()) {
  3055. //
  3056. // BUGBUG - return FALSE surely? If there is a cache read in progress
  3057. // then by default, its for another request?
  3058. //
  3059. DEBUG_LEAVE(TRUE);
  3060. return (TRUE);
  3061. }
  3062. //
  3063. // if the object name is not set then all cache methods fail
  3064. //
  3065. //
  3066. // BUGBUG - do this only when we know we are reading from cache?
  3067. //
  3068. ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->SetObjectName((LPSTR)lpszFileName,
  3069. NULL,
  3070. &fn
  3071. );
  3072. if (dwAccess & GENERIC_WRITE) {
  3073. DEBUG_LEAVE(FALSE);
  3074. return (FALSE);
  3075. }
  3076. if (!(dwFlags & INTERNET_FLAG_NO_CACHE_WRITE)) {
  3077. //
  3078. // set the cache flags like RELOAD etc.
  3079. //
  3080. ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->SetCacheFlags(dwFlags);
  3081. } else {
  3082. //
  3083. // set flags to disable both read and write
  3084. //
  3085. ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->SetCacheFlags(
  3086. INTERNET_FLAG_NO_CACHE_WRITE
  3087. | INTERNET_FLAG_RELOAD);
  3088. }
  3089. if (!FFtpCanReadFromCache(hFtp)) {
  3090. DEBUG_LEAVE(FALSE);
  3091. return (FALSE);
  3092. }
  3093. DEBUG_PRINT(CACHE,
  3094. INFO,
  3095. ("Checking in the cache\n"
  3096. ));
  3097. dwError = ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->BeginCacheRetrieval(&lpCEI);
  3098. if (dwError == ERROR_SUCCESS)
  3099. {
  3100. //
  3101. // found it in the cache
  3102. //
  3103. DEBUG_PRINT(FTP,
  3104. INFO,
  3105. ("Found in the cache\n"
  3106. ));
  3107. BOOL bGetFromCache = FALSE;
  3108. if (IsOffline()
  3109. || (((((INTERNET_HANDLE_OBJECT *)hFtp)->GetInternetOpenFlags() | dwFlags)
  3110. & INTERNET_FLAG_OFFLINE) && !(dwFlags & INTERNET_FLAG_RELOAD))) {
  3111. bGetFromCache = TRUE;
  3112. DEBUG_PRINT(CACHE,
  3113. INFO,
  3114. ("Offline, loading from cache:\n \
  3115. dwFlags & INTERNET_FLAG_RELOAD = %s \n \
  3116. dwFlags & INTERNET_FLAG_OFFLIME = %s \n \
  3117. InternetOpenFlags & INTERNET_FLAG_OFFLIME = %s \n",
  3118. (dwFlags & INTERNET_FLAG_RELOAD) ? "TRUE" : "FALSE",
  3119. (dwFlags & INTERNET_FLAG_OFFLINE) ? "TRUE" : "FALSE",
  3120. (((INTERNET_HANDLE_OBJECT *)hFtp)->GetInternetOpenFlags()
  3121. & INTERNET_FLAG_OFFLINE) ? "TRUE" : "FALSE"
  3122. ));
  3123. } else {
  3124. if (!FIsFtpExpired(hFtp, lpCEI))
  3125. {
  3126. bGetFromCache = TRUE;
  3127. }
  3128. }
  3129. if (! (( fIsHtmlFind && lpCEI->lpszFileExtension) ||
  3130. (!fIsHtmlFind && !lpCEI->lpszFileExtension) ) )
  3131. {
  3132. DEBUG_PRINT(CACHE,
  3133. INFO,
  3134. ("Mismatched HTML-type, expiring\n"
  3135. ));
  3136. bGetFromCache = FALSE;
  3137. }
  3138. if (bGetFromCache) {
  3139. dwError = ERROR_SUCCESS;
  3140. ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->SetFromCache();
  3141. } else {
  3142. DEBUG_PRINT(CACHE,
  3143. INFO,
  3144. ("Expired\n"
  3145. ));
  3146. dwError = ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->EndCacheRetrieval();
  3147. INET_ASSERT(dwError == ERROR_SUCCESS);
  3148. dwError = ERROR_FILE_NOT_FOUND;
  3149. }
  3150. }
  3151. //
  3152. // cleanup
  3153. //
  3154. if (lpCEI != NULL) {
  3155. lpCEI = (LPCACHE_ENTRY_INFO)FREE_MEMORY(lpCEI);
  3156. INET_ASSERT(lpCEI == NULL);
  3157. }
  3158. DEBUG_LEAVE(dwError == ERROR_SUCCESS);
  3159. return (dwError == ERROR_SUCCESS);
  3160. }
  3161. PRIVATE
  3162. BOOL
  3163. FFtpCanReadFromCache(
  3164. HINTERNET hFtp
  3165. )
  3166. /*++
  3167. Routine Description:
  3168. This routine checks whether a cache read should even be started.
  3169. Arguments:
  3170. hFtp a mapped handle to an ftp download (FtpOpenFile/FtpFindFirstFile)
  3171. Returns:
  3172. Windows Error Code.
  3173. Comments:
  3174. --*/
  3175. {
  3176. DWORD dwFlags;
  3177. dwFlags = ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->GetCacheFlags();
  3178. //
  3179. // in offline or disconnected states client always reads
  3180. //
  3181. if (IsOffline()
  3182. || (((INTERNET_HANDLE_OBJECT *)hFtp)->GetInternetOpenFlags() | dwFlags)
  3183. & INTERNET_FLAG_OFFLINE) {
  3184. return (TRUE);
  3185. }
  3186. //
  3187. // if we are asked to reload data, it is not OK to read
  3188. //
  3189. if (dwFlags & (INTERNET_FLAG_RELOAD | INTERNET_FLAG_RESYNCHRONIZE)) {
  3190. DEBUG_PRINT(CACHE,
  3191. INFO,
  3192. ("no cache option\n"
  3193. ));
  3194. return (FALSE);
  3195. }
  3196. return (TRUE);
  3197. }
  3198. PRIVATE
  3199. BOOL
  3200. FBeginCacheWriteProcessing(
  3201. IN HINTERNET hFtp,
  3202. IN LPCSTR lpszFileName,
  3203. IN DWORD dwAccess,
  3204. IN DWORD dwFlags,
  3205. IN DWORD_PTR dwContext,
  3206. BOOL fIsHtmlFind
  3207. )
  3208. /*++
  3209. Routine Description:
  3210. Sets up to start writing FTP data to the cache
  3211. Arguments:
  3212. hFtp A mapped handle to an ftp data transfer object (FtpOpenFile/FtpFindFirstFile)
  3213. lpszFileName ftp file to look for in the cache
  3214. dwAccess Accesstype eg: GENERIC_WRITE
  3215. dwFlags caching flags
  3216. dwContext async context
  3217. Returns:
  3218. TRUE if started cache writing, FALSE otherwise
  3219. Comments:
  3220. --*/
  3221. {
  3222. DWORD dwError = ERROR_INVALID_FUNCTION;
  3223. URLGEN_FUNC fn = pFtpGetUrlString;
  3224. char cExt[DEFAULT_MAX_EXTENSION_LENGTH + 1];
  3225. DWORD dwBuffLen;
  3226. LPSTR lpszFileExtension;
  3227. if (dwAccess & GENERIC_WRITE) {
  3228. return (FALSE);
  3229. }
  3230. if (!((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->IsCacheReadInProgress()) {
  3231. //
  3232. // we are not reading from the cache
  3233. // Let us ask a routine whether we should cache this
  3234. // stuff or not
  3235. //
  3236. if (FFtpCanWriteToCache(hFtp)) {
  3237. //
  3238. // if the object name is not set then all cache methods fail
  3239. //
  3240. ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->
  3241. SetObjectName((LPSTR)lpszFileName,
  3242. NULL,
  3243. &fn
  3244. );
  3245. //
  3246. // set the cache flags
  3247. //
  3248. ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->SetCacheFlags(dwFlags);
  3249. //
  3250. // he says we can cache it.
  3251. //
  3252. DEBUG_PRINT(CACHE,
  3253. INFO,
  3254. ("Starting cache write\n"
  3255. ));
  3256. if (!fIsHtmlFind) {
  3257. dwBuffLen = sizeof(cExt);
  3258. lpszFileExtension = GetFileExtensionFromUrl(((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->GetURL(), &dwBuffLen);
  3259. if (lpszFileExtension != NULL) {
  3260. memcpy(cExt, lpszFileExtension, dwBuffLen);
  3261. cExt[dwBuffLen] = '\0';
  3262. lpszFileExtension = cExt;
  3263. }
  3264. }
  3265. else {
  3266. //allways generate htm extension
  3267. strcpy(cExt, "htm");
  3268. lpszFileExtension = cExt;
  3269. }
  3270. dwError = ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->BeginCacheWrite(0, lpszFileExtension);
  3271. if (dwError != ERROR_SUCCESS) {
  3272. DEBUG_PRINT(CACHE,
  3273. ERROR,
  3274. ("Error in BeginCacheWrite %ld\n",
  3275. dwError
  3276. ));
  3277. }
  3278. }
  3279. }
  3280. return (dwError == ERROR_SUCCESS);
  3281. }
  3282. PRIVATE
  3283. BOOL
  3284. FFtpCanWriteToCache(
  3285. HINTERNET hFtp
  3286. )
  3287. /*++
  3288. Routine Description:
  3289. This routine checks whether a cache write should even be started.
  3290. Arguments:
  3291. hFtp a mapped handle to an ftp download (FtpOpenFile/FtpFindFirstFile)
  3292. Returns:
  3293. TRUE if successful, FALSE otherwise
  3294. Comments:
  3295. --*/
  3296. {
  3297. if (((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->GetCacheFlags() & INTERNET_FLAG_DONT_CACHE) {
  3298. return (FALSE);
  3299. }
  3300. return (TRUE);
  3301. }
  3302. DWORD
  3303. InbLocalEndCacheWrite(
  3304. IN HINTERNET hFtp,
  3305. LPSTR lpszFileExtension,
  3306. IN BOOL fNormal
  3307. )
  3308. /*++
  3309. Routine Description:
  3310. This routine checks terminates cache writing if it was in progress and
  3311. commits the entry to the cache
  3312. Arguments:
  3313. hFtp a mapped handle to an ftp download (FtpOpenFile/FtpFindFirstFile)
  3314. fNormal TRUE if the termination is normal, in which case the collected data
  3315. is entered in the cache, otherwise it is discarded
  3316. Returns:
  3317. Windows error code
  3318. Comments:
  3319. --*/
  3320. {
  3321. FILETIME ftLastModTime, ftExpiryTime, ftPostCheck;
  3322. DWORD dwEntryType;
  3323. if (((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->IsCacheWriteInProgress()) {
  3324. ftLastModTime.dwLowDateTime =
  3325. ftLastModTime.dwHighDateTime = 0;
  3326. ftExpiryTime.dwLowDateTime =
  3327. ftExpiryTime.dwHighDateTime = 0;
  3328. ftPostCheck.dwLowDateTime =
  3329. ftPostCheck.dwHighDateTime = 0;
  3330. dwEntryType = (!fNormal)?0xffffffff:
  3331. ((((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->
  3332. GetCacheFlags() & INTERNET_FLAG_MAKE_PERSISTENT)
  3333. ? STICKY_CACHE_ENTRY:0
  3334. );
  3335. DEBUG_PRINT(CACHE,
  3336. INFO,
  3337. ("Cache write EntryType = %x \
  3338. IsPerUserItem = %d \
  3339. <hFtp = 0x%x> \
  3340. <hFtp->GetParent() = 0x%x> \
  3341. <hFtp->IsPerUserItem() = %d\n",
  3342. dwEntryType,
  3343. ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->IsPerUserItem(),
  3344. hFtp,
  3345. ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->GetParent(),
  3346. ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->IsPerUserItem()
  3347. ));
  3348. return (((INTERNET_CONNECT_HANDLE_OBJECT *)hFtp)->
  3349. EndCacheWrite( &ftExpiryTime,
  3350. &ftLastModTime,
  3351. &ftPostCheck,
  3352. dwEntryType,
  3353. 0,
  3354. NULL,
  3355. lpszFileExtension));
  3356. }
  3357. return (ERROR_SUCCESS);
  3358. }
  3359. PRIVATE
  3360. BOOL
  3361. FGetCWDFromCache(
  3362. HINTERNET hFtpSession,
  3363. LPSTR lpBuff,
  3364. LPDWORD lpdwBuffSize
  3365. )
  3366. /*++
  3367. Routine Description:
  3368. This routine returns the current working directory for an FTP session
  3369. Arguments:
  3370. hFtpSession a mapped handle to an ftp download (FtpOpenFile/FtpFindFirstFile)
  3371. LPSTR buffer to return the string in
  3372. lpdwBuffSize IN size of lpBuff, OUT size passed back
  3373. Returns:
  3374. TRUE if successful, FALSE otherwise
  3375. Comments:
  3376. --*/
  3377. {
  3378. if ((((INTERNET_CONNECT_HANDLE_OBJECT *)hFtpSession)->GetInternetOpenFlags()
  3379. & INTERNET_FLAG_OFFLINE)) {
  3380. ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtpSession)->GetCurrentWorkingDirectory(lpBuff, lpdwBuffSize);
  3381. return (TRUE);
  3382. }
  3383. return (FALSE);
  3384. }
  3385. PRIVATE
  3386. BOOL
  3387. FIsFtpExpired(
  3388. HINTERNET hFtp,
  3389. LPCACHE_ENTRY_INFO lpCEI
  3390. )
  3391. /*++
  3392. Routine Description:
  3393. This routine checks whether an ftp item in the cache has expired
  3394. Arguments:
  3395. hFtp a mapped handle to an ftp download (FtpOpenFile/FtpFindFirstFile)
  3396. lpCEI cache entry info for the item
  3397. Returns:
  3398. TRUE if successful, FALSE otherwise
  3399. Comments:
  3400. Even though this is a trivial routine and can be nuked, it is good
  3401. place holder for doing special things to check FTP expiry
  3402. --*/
  3403. {
  3404. BOOL fExpired = FALSE;
  3405. if (CheckExpired( hFtp,
  3406. &fExpired,
  3407. lpCEI,
  3408. dwdwFtpDefaultExpiryDelta) != ERROR_SUCCESS) {
  3409. fExpired = TRUE;
  3410. }
  3411. return (fExpired);
  3412. }
  3413. VOID
  3414. LocalSetObjectName(
  3415. HINTERNET hFtpMapped,
  3416. LPSTR lpszFileName
  3417. )
  3418. {
  3419. URLGEN_FUNC fn = pFtpGetUrlString;
  3420. ((INTERNET_CONNECT_HANDLE_OBJECT *)hFtpMapped)->
  3421. SetObjectName((LPSTR)lpszFileName,
  3422. NULL,
  3423. &fn
  3424. );
  3425. }
  3426. /*++
  3427. Routine Description:
  3428. This routine checks whether a string given to FtpFindFirstFile
  3429. is a directory or a file
  3430. Arguments:
  3431. lpszSearchFile string passed in to either FtpFindFirstFile
  3432. Returns:
  3433. TRUE if successful, FALSE otherwise
  3434. Comments:
  3435. This routine should not be used anywhere other thatn FtpFindFirstFile
  3436. functions
  3437. --*/
  3438. BOOL IsSearchFileDirectory(
  3439. LPCSTR lpszSearchFile
  3440. )
  3441. {
  3442. DWORD dwLen;
  3443. if (!lpszSearchFile) {
  3444. return (TRUE);
  3445. }
  3446. dwLen = lstrlen(lpszSearchFile);
  3447. if (!dwLen) {
  3448. return (TRUE);
  3449. }
  3450. return (lpszSearchFile[dwLen-1] == '/');
  3451. }