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.

2629 lines
67 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. ftpapir.cxx
  5. Abstract:
  6. Contains the remote-side FTP API worker functions. In each case, the API
  7. proper validates the arguments. The worker functions contained herein just
  8. perform the requested operation with the supplied arguments.
  9. These functions are the remote side of the RPC interface. If the DLL is
  10. the abstract0 version (no RPC) then the A forms of the functions simply
  11. call the w functions
  12. Contents:
  13. wFtpFindFirstFile
  14. wFtpDeleteFile
  15. wFtpRenameFile
  16. wFtpOpenFile
  17. wFtpCreateDirectory
  18. wFtpRemoveDirectory
  19. wFtpSetCurrentDirectory
  20. wFtpGetCurrentDirectory
  21. wFtpCommand
  22. wFtpFindNextFile
  23. wFtpFindClose
  24. wFtpConnect
  25. wFtpMakeConnection
  26. wFtpDisconnect
  27. wFtpReadFile
  28. wFtpWriteFile
  29. wFtpQueryDataAvailable
  30. wFtpCloseFile
  31. wFtpFindServerType
  32. wFtpGetFileSize
  33. Author:
  34. Heath Hunnicutt [t-heathh] 13-Jul-1994
  35. Environment:
  36. Win32(s) user-level DLL
  37. Revision History:
  38. 09-Mar-1995 rfirth
  39. Created new file/worker functions from functions contained in
  40. findfile.c, ftphelp.c
  41. --*/
  42. #include <wininetp.h>
  43. #include "ftpapih.h"
  44. //
  45. // private macros
  46. //
  47. #define CASE_OF(constant) case constant: return # constant
  48. //
  49. // private debug functions
  50. //
  51. #if INET_DEBUG
  52. PRIVATE
  53. DEBUG_FUNCTION
  54. LPSTR
  55. InternetMapFtpServerType(
  56. IN FTP_SERVER_TYPE ServerType
  57. );
  58. #else
  59. #define InternetMapFtpServerType(x) (VOID)(x)
  60. #endif // INET_DEBUG
  61. //
  62. // external functions
  63. //
  64. extern
  65. DWORD
  66. InbLocalEndCacheWrite(
  67. IN HINTERNET hFtpFile,
  68. IN LPSTR lpszFileExtension,
  69. IN BOOL fNormal
  70. );
  71. //
  72. // functions
  73. //
  74. DWORD
  75. wFtpFindFirstFile(
  76. IN HINTERNET hFtpSession,
  77. IN LPCSTR lpszFilespec,
  78. OUT LPWIN32_FIND_DATA lpFindFileData OPTIONAL,
  79. OUT LPHINTERNET lphInternet
  80. )
  81. /*++
  82. Routine Description:
  83. Download the remote site's directory listing and parse it into
  84. WIN32_FIND_DATA structures that we can pass back to the app.
  85. If the FTP session is currently involved in a data transfer, such as
  86. a FtpOpenFile()....FtpCloseFile() series of calls, this function will
  87. fail.
  88. Arguments:
  89. hFtpSession - Handle to an FTP session, as returned from FtpOpen()
  90. lpszFilespec - Pointer to a string containing a file specification
  91. to find. May be empty, but not NULL
  92. lpFindFileData - Pointer to a buffer that will contain WIN32_FIND_DATA
  93. information when this call succeeds.
  94. If this parameter is not supplied, then any find data
  95. will be returned via InternetFindNextFile()
  96. lphInternet - place to return open find handle
  97. Return Value:
  98. DWORD
  99. Success - ERROR_SUCCESS
  100. *lphInternet contains new find handle
  101. Failure - ERROR_INVALID_HANDLE
  102. The session handle is not recognized
  103. ERROR_FTP_TRANSFER_IN_PROGRESS
  104. The data connection is already in use
  105. ERROR_NO_MORE_FILES
  106. The end of the directory listing has been reached
  107. ERROR_INTERNET_EXTENDED_ERROR
  108. Call InternetGetLastResponseInfo() for the text
  109. ERROR_INTERNET_INTERNAL_ERROR
  110. Something bad happened
  111. --*/
  112. {
  113. DEBUG_ENTER((DBG_FTP,
  114. Dword,
  115. "wFtpFindFirstFile",
  116. "%#x, %q, %#x, %#x",
  117. hFtpSession,
  118. lpszFilespec,
  119. lpFindFileData,
  120. lphInternet
  121. ));
  122. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  123. LPSTR lpBuffer = NULL;
  124. DWORD error;
  125. if (lpThreadInfo == NULL) {
  126. INET_ASSERT(FALSE);
  127. error = ERROR_INTERNET_INTERNAL_ERROR;
  128. goto quit;
  129. }
  130. LPFTP_SESSION_INFO lpSessionInfo;
  131. if (!FindFtpSession(hFtpSession, &lpSessionInfo)) {
  132. error = ERROR_INVALID_HANDLE;
  133. goto quit;
  134. }
  135. //
  136. // acquire the session lock while we check and optionally set the active
  137. // find flag
  138. //
  139. AcquireFtpSessionLock(lpSessionInfo);
  140. if (!(lpSessionInfo->Flags & FFTP_FIND_ACTIVE)) {
  141. lpSessionInfo->Flags |= FFTP_FIND_ACTIVE;
  142. error = ERROR_SUCCESS;
  143. } else {
  144. error = ERROR_FTP_TRANSFER_IN_PROGRESS;
  145. }
  146. ReleaseFtpSessionLock(lpSessionInfo);
  147. //
  148. // if we already have a directory listing on this connection, then we can
  149. // not allow another one, until the current listing is cleared out by the
  150. // app calling InternetCloseHandle()
  151. //
  152. if (error != ERROR_SUCCESS) {
  153. goto deref_exit;
  154. }
  155. //
  156. // the filespec may have a path component. We assume that any wild-cards
  157. // will only be in the filename part. We use the path part in the directory
  158. // request and the filename part when parsing the directory output
  159. //
  160. char pathBuf[INTERNET_MAX_PATH_LENGTH + 1];
  161. LPSTR lpszPathPart;
  162. LPSTR lpszFilePart;
  163. BOOL isWild;
  164. DWORD dwFilePartLength;
  165. lpszFilePart = (LPSTR)lpszFilespec;
  166. lpszPathPart = NULL;
  167. dwFilePartLength = lstrlen(lpszFilePart);
  168. if (*lpszFilePart != '\0') {
  169. LPSTR pathSeparator;
  170. pathSeparator = _memrchr(lpszFilePart, '\\', dwFilePartLength);
  171. if (pathSeparator == NULL) {
  172. pathSeparator = _memrchr(lpszFilePart, '/', dwFilePartLength);
  173. }
  174. if (pathSeparator != NULL) {
  175. int len = (int) (pathSeparator - lpszFilePart) + 1;
  176. if (len < sizeof(pathBuf)) {
  177. memcpy(pathBuf, lpszFilePart, len);
  178. pathBuf[len] = '\0';
  179. lpszPathPart = pathBuf;
  180. lpszFilePart = pathSeparator + 1;
  181. DEBUG_PRINT(FTP,
  182. INFO,
  183. ("lpszPathPart = %q, lpszFilePart = %q\n",
  184. lpszPathPart,
  185. lpszFilePart
  186. ));
  187. }
  188. }
  189. //
  190. // determine whether the caller is asking for a fuzzy file match, or
  191. // (typically) the request is for the contents of a directory
  192. //
  193. isWild = IsFilespecWild(lpszFilePart);
  194. } else {
  195. //
  196. // empty string - not asking for wildcard search
  197. //
  198. isWild = FALSE;
  199. }
  200. //
  201. // and ask the FTP server for the directory listing
  202. //
  203. FTP_RESPONSE_CODE rcResponse;
  204. error = Command(lpSessionInfo,
  205. TRUE,
  206. FTP_TRANSFER_TYPE_ASCII,
  207. &rcResponse,
  208. ((lpszPathPart == NULL) && (isWild || (*lpszFilePart == '\0')))
  209. ? "LIST"
  210. : "LIST %s",
  211. (lpszPathPart == NULL)
  212. ? lpszFilePart
  213. : isWild
  214. ? lpszPathPart
  215. : lpszFilespec
  216. );
  217. //
  218. // quit early if we failed to send the command, or the server didn't
  219. // understand it
  220. //
  221. if (error != ERROR_SUCCESS) {
  222. goto cleanup;
  223. }
  224. //
  225. // presumably, the server has sent us a directory listing. Receive it
  226. //
  227. DWORD bufferLength;
  228. DWORD bufferLeft;
  229. DWORD bytesReceived;
  230. BOOL eof;
  231. bufferLength = 0;
  232. bufferLeft = 0;
  233. bytesReceived = 0;
  234. error = lpSessionInfo->socketData->Receive((LPVOID *)&lpBuffer,
  235. &bufferLength,
  236. &bufferLeft,
  237. &bytesReceived,
  238. 0,
  239. SF_EXPAND
  240. | SF_COMPRESS
  241. | SF_RECEIVE_ALL
  242. | SF_INDICATE,
  243. &eof
  244. );
  245. //
  246. // we are done with the data connection
  247. //
  248. lpSessionInfo->socketData->Close();
  249. //
  250. // quit now if we had an error while receiving
  251. //
  252. if (error != ERROR_SUCCESS) {
  253. goto cleanup;
  254. }
  255. //
  256. // if the previous response was preliminary then get the final response from
  257. // the FTP server
  258. //
  259. if (rcResponse.Major != FTP_RESPONSE_COMPLETE) {
  260. error = GetReply(lpSessionInfo, &rcResponse);
  261. if (error != ERROR_SUCCESS) {
  262. goto cleanup;
  263. }
  264. //
  265. // check response for failure
  266. //
  267. if (rcResponse.Major != FTP_RESPONSE_COMPLETE) {
  268. //
  269. // <-- Return "command failed" error code
  270. //
  271. if(rcResponse.Status == FTP_RESPONSE_ACTION_NOT_TAKEN)
  272. {
  273. error = ERROR_NO_MORE_FILES;
  274. }
  275. else
  276. {
  277. error = ERROR_INTERNET_EXTENDED_ERROR;
  278. }
  279. goto cleanup;
  280. }
  281. }
  282. if (bytesReceived == 0) {
  283. DEBUG_PRINT(WORKER,
  284. ERROR,
  285. ("ReceiveData() returns 0 bytes\n"
  286. ));
  287. error = ERROR_NO_MORE_FILES;
  288. goto cleanup;
  289. }
  290. //
  291. // trap bad servers which return a not-found message in the data stream. We
  292. // only do this if we are not performing a wild-card search (because the
  293. // wild-card match will fail to match anything if the target file or path
  294. // cannot be found)
  295. //
  296. LPSTR lpszSearch;
  297. DWORD dwSearch;
  298. lpszSearch = (lpszPathPart == NULL) ? lpszFilePart : (LPSTR)lpszFilespec;
  299. dwSearch = lstrlen(lpszSearch);
  300. if (!isWild && (bytesReceived > dwSearch)) {
  301. if (!_strnicmp(lpBuffer, lpszSearch, dwSearch)
  302. && (lpBuffer[dwSearch] == ':')) {
  303. static char testChars[] = {'\r', '\n', '\0'};
  304. LPSTR lpStartOfString = lpBuffer + dwSearch + 1;
  305. LPSTR lpEndOfString;
  306. for (int i = 0; i < ARRAY_ELEMENTS(testChars); ++i) {
  307. lpEndOfString = strchr(lpStartOfString, testChars[i]);
  308. if (lpEndOfString != NULL) {
  309. break;
  310. }
  311. }
  312. //
  313. // we should have found at least one of the target characters
  314. //
  315. INET_ASSERT(lpEndOfString != NULL);
  316. if (lpEndOfString != NULL) {
  317. int lengthToTest = (int) (lpEndOfString - lpStartOfString);
  318. //
  319. // BUGBUG - internationalization?
  320. //
  321. if (strnistr(lpStartOfString, "not found", lengthToTest)
  322. || strnistr(lpStartOfString, "cannot find", lengthToTest)) {
  323. error = ERROR_NO_MORE_FILES;
  324. goto cleanup;
  325. }
  326. } else {
  327. error = ERROR_INTERNET_INTERNAL_ERROR;
  328. goto cleanup;
  329. }
  330. }
  331. }
  332. INET_ASSERT(lpBuffer != NULL);
  333. INET_ASSERT((int)bytesReceived > 0);
  334. error = ParseDirList(lpBuffer,
  335. bytesReceived,
  336. isWild ? (LPSTR)lpszFilePart : NULL,
  337. &lpSessionInfo->FindFileList
  338. );
  339. //
  340. // ParseDirList() may have failed
  341. //
  342. if (error != ERROR_SUCCESS) {
  343. goto cleanup;
  344. }
  345. //
  346. // if there's nothing in the list then no files matching the caller's
  347. // specification were found
  348. //
  349. if (IsListEmpty(&lpSessionInfo->FindFileList)) {
  350. error = ERROR_NO_MORE_FILES;
  351. } else {
  352. //
  353. // if the caller supplied an output buffer then return the first entry
  354. // and remove it from the list
  355. //
  356. if (ARGUMENT_PRESENT(lpFindFileData)) {
  357. PLIST_ENTRY pEntry;
  358. pEntry = RemoveHeadList(&lpSessionInfo->FindFileList);
  359. CopyMemory(lpFindFileData,
  360. (LPWIN32_FIND_DATA)(pEntry + 1),
  361. sizeof(*lpFindFileData)
  362. );
  363. FREE_MEMORY(pEntry);
  364. }
  365. //
  366. // FTP can only have one active operation per session, so we just return
  367. // this session handle as the find handle
  368. //
  369. *lphInternet = hFtpSession;
  370. error = ERROR_SUCCESS;
  371. }
  372. cleanup:
  373. if (lpSessionInfo->socketData->IsValid()) {
  374. lpSessionInfo->socketData->SetLinger(TRUE, 0);
  375. lpSessionInfo->socketData->Close();
  376. }
  377. if (lpBuffer != NULL) {
  378. (void)FREE_MEMORY((HLOCAL)lpBuffer);
  379. }
  380. //
  381. // if we failed then reset the active find flag. We set it, so we know it
  382. // is safe to reset without acquiring the session lock
  383. //
  384. if (error != ERROR_SUCCESS) {
  385. lpSessionInfo->Flags &= ~FFTP_FIND_ACTIVE;
  386. }
  387. deref_exit:
  388. DereferenceFtpSession(lpSessionInfo);
  389. quit:
  390. DEBUG_LEAVE(error);
  391. return error;
  392. }
  393. DWORD
  394. wFtpDeleteFile(
  395. IN HINTERNET hFtpSession,
  396. IN LPCSTR lpszFileName
  397. )
  398. /*++
  399. Routine Description:
  400. Deletes a file at an FTP server
  401. Arguments:
  402. hFtpSession - identifies the FTP server
  403. lpszFileName - name of file to delete
  404. Return Value:
  405. DWORD
  406. Success - ERROR_SUCCESS
  407. Failure -
  408. --*/
  409. {
  410. DEBUG_ENTER((DBG_FTP,
  411. Dword,
  412. "wFtpDeleteFile",
  413. "%#x, %q",
  414. hFtpSession,
  415. lpszFileName
  416. ));
  417. LPFTP_SESSION_INFO lpSessionInfo;
  418. DWORD error;
  419. if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
  420. FTP_RESPONSE_CODE rcResponse;
  421. error = Command(lpSessionInfo,
  422. FALSE,
  423. FTP_TRANSFER_TYPE_UNKNOWN,
  424. &rcResponse,
  425. "DELE %s",
  426. lpszFileName
  427. );
  428. if ((error == ERROR_SUCCESS)
  429. && (rcResponse.Major != FTP_RESPONSE_COMPLETE)) {
  430. error = ERROR_INTERNET_EXTENDED_ERROR;
  431. }
  432. DereferenceFtpSession(lpSessionInfo);
  433. } else {
  434. error = ERROR_INVALID_HANDLE;
  435. }
  436. DEBUG_LEAVE(error);
  437. return error;
  438. }
  439. DWORD
  440. wFtpRenameFile(
  441. IN HINTERNET hFtpSession,
  442. IN LPCSTR lpszExisting,
  443. IN LPCSTR lpszNew
  444. )
  445. /*++
  446. Routine Description:
  447. Renames a file at an FTP server
  448. Arguments:
  449. hFtpSession - identifies FTP server
  450. lpszExisting - current file name
  451. lpszNew - new file name
  452. Return Value:
  453. DWORD
  454. Success - ERROR_SUCCESS
  455. Failure -
  456. --*/
  457. {
  458. DEBUG_ENTER((DBG_FTP,
  459. Dword,
  460. "wFtpRenameFile",
  461. "%#x, %q, %q",
  462. hFtpSession,
  463. lpszExisting,
  464. lpszNew
  465. ));
  466. LPFTP_SESSION_INFO lpSessionInfo;
  467. DWORD error;
  468. if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
  469. FTP_RESPONSE_CODE rcResponse;
  470. error = Command(lpSessionInfo,
  471. FALSE,
  472. FTP_TRANSFER_TYPE_UNKNOWN,
  473. &rcResponse,
  474. "RNFR %s",
  475. lpszExisting
  476. );
  477. if ((error == ERROR_SUCCESS)
  478. && (rcResponse.Major != FTP_RESPONSE_CONTINUE)) {
  479. error = ERROR_INTERNET_EXTENDED_ERROR;
  480. }
  481. if (error == ERROR_SUCCESS) {
  482. error = Command(lpSessionInfo,
  483. FALSE,
  484. FTP_TRANSFER_TYPE_UNKNOWN,
  485. &rcResponse,
  486. "RNTO %s",
  487. lpszNew
  488. );
  489. if ((error == ERROR_SUCCESS)
  490. && (rcResponse.Major != FTP_RESPONSE_COMPLETE)) {
  491. error = ERROR_INTERNET_EXTENDED_ERROR;
  492. }
  493. }
  494. DereferenceFtpSession(lpSessionInfo);
  495. } else {
  496. error = ERROR_INVALID_HANDLE;
  497. }
  498. DEBUG_LEAVE(error);
  499. return error;
  500. }
  501. DWORD
  502. wFtpOpenFile(
  503. IN HINTERNET hFtpSession,
  504. IN LPCSTR lpszFileName,
  505. IN DWORD dwAccess,
  506. IN DWORD dwFlags,
  507. OUT LPHINTERNET lphInternet
  508. )
  509. /*++
  510. Routine Description:
  511. Initiates the connection to read or write a file at the FTP server
  512. Arguments:
  513. hFtpSession - identifies FTP server
  514. lpszFileName - name of file to open
  515. dwAccess - access mode - GENERIC_READ or GENERIC_WRITE
  516. dwFlags - flags controlling how to transfer the data
  517. lphInternet - where to return the open file handle
  518. Return Value:
  519. DWORD
  520. Success - ERROR_SUCCESS
  521. Failure -
  522. --*/
  523. {
  524. DEBUG_ENTER((DBG_FTP,
  525. Dword,
  526. "wFtpOpenFile",
  527. "%#x, %q, %#x, %#x, %#x",
  528. hFtpSession,
  529. lpszFileName,
  530. dwAccess,
  531. dwFlags,
  532. lphInternet
  533. ));
  534. LPFTP_SESSION_INFO lpSessionInfo;
  535. DWORD error;
  536. if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
  537. //
  538. // control session must be established
  539. //
  540. if (! lpSessionInfo->socketControl->IsValid()) {
  541. error = ERROR_FTP_DROPPED;
  542. } else if ((lpSessionInfo->socketData->IsValid())
  543. || (lpSessionInfo->Flags & FFTP_FILE_ACTIVE)) {
  544. //
  545. // there is a (file) transfer in progress if the socket is valid,
  546. // or we are awaiting a call to InternetCloseHandle() before we can
  547. // open another file (FFTP_FILE_ACTIVE is set. This stops another
  548. // thread from closing our socket handle)
  549. //
  550. error = ERROR_FTP_TRANSFER_IN_PROGRESS;
  551. } else {
  552. FTP_RESPONSE_CODE rcResponse;
  553. INET_ASSERT(!lpSessionInfo->socketData->IsValid());
  554. //
  555. // Clear the session's "known size bit" before we download the next file,
  556. // this is to make sure we don't read an extranous size value off it.
  557. //
  558. lpSessionInfo->Flags &= ~(FFTP_KNOWN_FILE_SIZE);
  559. //
  560. // send the connection set-up commands, and issue either the send
  561. // or the receive command
  562. //
  563. // Either "RETR filename" or "STOR filename"
  564. //
  565. error = NegotiateDataConnection(lpSessionInfo,
  566. dwFlags,
  567. &rcResponse,
  568. (dwAccess & GENERIC_READ)
  569. ? "RETR %s"
  570. : "STOR %s",
  571. lpszFileName
  572. );
  573. if (error == ERROR_SUCCESS) {
  574. //
  575. // Check response for failure
  576. //
  577. if ((rcResponse.Major != FTP_RESPONSE_PRELIMINARY)
  578. && (rcResponse.Major != FTP_RESPONSE_COMPLETE)) {
  579. ICSocket * socketData;
  580. //
  581. // BUGBUG - RLF - don't know if this is what's intended
  582. // here, but the code just used to check
  583. // socketData != INVALID_SOCKET. Since socketData
  584. // was getting set to INVALID_SOCKET at the top
  585. // of this routine, this branch would never be
  586. // taken
  587. //
  588. socketData = lpSessionInfo->socketData;
  589. if (socketData->IsValid()) {
  590. ResetSocket(socketData);
  591. }
  592. error = ERROR_INTERNET_EXTENDED_ERROR;
  593. } else {
  594. lpSessionInfo->dwTransferAccess = dwAccess;
  595. //
  596. // Some FTP servers will send us back both the preliminary
  597. // response and the complete response so quickly that we
  598. // will never see the preliminary.
  599. //
  600. // In order for FtpCloseFile() to know that the completion
  601. // response has been received, we store the response
  602. // structure in the Session Info.
  603. //
  604. // The response structure only needs to be stored between
  605. // API calls in this situation, it is not generally
  606. // referred to.
  607. //
  608. SetSessionLastResponseCode(lpSessionInfo, &rcResponse);
  609. //
  610. // set the abort flag if the file was opened for read - this
  611. // lets the server know it can clean up the session if we
  612. // close early
  613. //
  614. if (dwAccess & GENERIC_READ) {
  615. lpSessionInfo->Flags |= FFTP_ABORT_TRANSFER;
  616. }
  617. //
  618. // FTP can only have one active operation per session, so
  619. // we just return this session handle as the find handle
  620. //
  621. *lphInternet = hFtpSession;
  622. //
  623. // this session has an active file operation
  624. //
  625. lpSessionInfo->Flags |= FFTP_FILE_ACTIVE;
  626. //
  627. // N.B. error == ERROR_SUCCESS from above test after call
  628. // to NegotiateDataConnection
  629. //
  630. INET_ASSERT(error == ERROR_SUCCESS);
  631. }
  632. }
  633. }
  634. DereferenceFtpSession(lpSessionInfo);
  635. } else {
  636. error = ERROR_INVALID_HANDLE;
  637. }
  638. DEBUG_LEAVE(error);
  639. return error;
  640. }
  641. DWORD
  642. wFtpCreateDirectory(
  643. IN HINTERNET hFtpSession,
  644. IN LPCSTR lpszDirectory
  645. )
  646. /*++
  647. Routine Description:
  648. Creates a directory at the FTP server
  649. Arguments:
  650. hFtpSession - identifies the FTP server
  651. lpszDirectory - directory to create
  652. Return Value:
  653. DWORD
  654. Success - ERROR_SUCCESS
  655. Failure -
  656. --*/
  657. {
  658. DEBUG_ENTER((DBG_FTP,
  659. Dword,
  660. "wFtpCreateDirectory",
  661. "%#x, %q",
  662. hFtpSession,
  663. lpszDirectory
  664. ));
  665. LPFTP_SESSION_INFO lpSessionInfo;
  666. DWORD error;
  667. if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
  668. FTP_RESPONSE_CODE rcResponse;
  669. error = Command(lpSessionInfo,
  670. FALSE,
  671. FTP_TRANSFER_TYPE_UNKNOWN,
  672. &rcResponse,
  673. "MKD %s",
  674. lpszDirectory
  675. );
  676. if ((error == ERROR_SUCCESS)
  677. && (rcResponse.Major != FTP_RESPONSE_COMPLETE)) {
  678. error = ERROR_INTERNET_EXTENDED_ERROR;
  679. }
  680. DereferenceFtpSession(lpSessionInfo);
  681. } else {
  682. error = ERROR_INVALID_HANDLE;
  683. }
  684. DEBUG_LEAVE(error);
  685. return error;
  686. }
  687. DWORD
  688. wFtpRemoveDirectory(
  689. IN HINTERNET hFtpSession,
  690. IN LPCSTR lpszDirectory
  691. )
  692. /*++
  693. Routine Description:
  694. Removes the named directory at the FTP server
  695. Arguments:
  696. hFtpSession - identifies the FTP server
  697. lpszDirectory - directory to remove
  698. Return Value:
  699. DWORD
  700. Success - ERROR_SUCCESS
  701. Failure -
  702. --*/
  703. {
  704. DEBUG_ENTER((DBG_FTP,
  705. Dword,
  706. "wFtpRemoveDirectory",
  707. "%#x, %q",
  708. hFtpSession,
  709. lpszDirectory
  710. ));
  711. LPFTP_SESSION_INFO lpSessionInfo;
  712. DWORD error;
  713. if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
  714. FTP_RESPONSE_CODE rcResponse;
  715. error = Command(lpSessionInfo,
  716. FALSE,
  717. FTP_TRANSFER_TYPE_UNKNOWN,
  718. &rcResponse,
  719. "RMD %s",
  720. lpszDirectory
  721. );
  722. if ((error == ERROR_SUCCESS)
  723. && (rcResponse.Major != FTP_RESPONSE_COMPLETE)) {
  724. error = ERROR_INTERNET_EXTENDED_ERROR;
  725. }
  726. DereferenceFtpSession(lpSessionInfo);
  727. } else {
  728. error = ERROR_INVALID_HANDLE;
  729. }
  730. DEBUG_LEAVE(error);
  731. return error;
  732. }
  733. DWORD
  734. wFtpSetCurrentDirectory(
  735. IN HINTERNET hFtpSession,
  736. IN LPCSTR lpszDirectory
  737. )
  738. /*++
  739. Routine Description:
  740. Sets the current directory for this FTP server session
  741. Arguments:
  742. hFtpSession - identifies the FTP server/session
  743. lpszDirectory - name of directory to set
  744. Return Value:
  745. DWORD
  746. Success - ERROR_SUCCESS
  747. Failure -
  748. --*/
  749. {
  750. DEBUG_ENTER((DBG_FTP,
  751. Dword,
  752. "wFtpSetCurrentDirectory",
  753. "%#x, %q",
  754. hFtpSession,
  755. lpszDirectory
  756. ));
  757. LPFTP_SESSION_INFO lpSessionInfo;
  758. DWORD error;
  759. if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
  760. FTP_RESPONSE_CODE rcResponse;
  761. error = Command(lpSessionInfo,
  762. FALSE,
  763. FTP_TRANSFER_TYPE_UNKNOWN,
  764. &rcResponse,
  765. "CWD %s",
  766. lpszDirectory
  767. );
  768. if ((error == ERROR_SUCCESS)
  769. && (rcResponse.Major != FTP_RESPONSE_COMPLETE)) {
  770. error = ERROR_INTERNET_EXTENDED_ERROR;
  771. }
  772. DereferenceFtpSession(lpSessionInfo);
  773. } else {
  774. error = ERROR_INVALID_HANDLE;
  775. }
  776. DEBUG_LEAVE(error);
  777. return error;
  778. }
  779. DWORD
  780. wFtpGetCurrentDirectory(
  781. IN HINTERNET hFtpSession,
  782. IN DWORD cchCurrentDirectory,
  783. OUT LPSTR lpszCurrentDirectory,
  784. OUT LPDWORD lpdwBytesReturned
  785. )
  786. /*++
  787. Routine Description:
  788. Gets the current working directory at the FTP server for this session
  789. Arguments:
  790. hFtpSession - identifies FTP server
  791. cchCurrentDirectory - number of characters in lpszCurrentDirectory
  792. lpszCurrentDirectory - buffer where current directory string is written
  793. lpdwBytesReturned - number of characters in output string NOT including
  794. terminating NUL
  795. Return Value:
  796. DWORD
  797. Success - ERROR_SUCCESS
  798. Failure - ERROR_INVALID_HANDLE
  799. ERROR_INSUFFICIENT_BUFFER
  800. The buffer in lpszCurrentDirectory is not large enough to
  801. hold the directory string. *lpdwBytesReturned will have
  802. the required size
  803. --*/
  804. {
  805. DEBUG_ENTER((DBG_FTP,
  806. Dword,
  807. "wFtpGetCurrentDirectory",
  808. "%#x, %d, %#x, %#x",
  809. hFtpSession,
  810. cchCurrentDirectory,
  811. lpszCurrentDirectory,
  812. lpdwBytesReturned
  813. ));
  814. LPFTP_SESSION_INFO lpSessionInfo;
  815. DWORD cchCopied;
  816. DWORD error;
  817. if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
  818. FTP_RESPONSE_CODE rcResponse;
  819. error = Command(lpSessionInfo,
  820. FALSE,
  821. FTP_TRANSFER_TYPE_UNKNOWN,
  822. &rcResponse,
  823. "PWD"
  824. );
  825. if ((error == ERROR_SUCCESS)
  826. && (rcResponse.Major != FTP_RESPONSE_COMPLETE)) {
  827. error = ERROR_INTERNET_EXTENDED_ERROR;
  828. }
  829. if (error == ERROR_SUCCESS) {
  830. LPSTR pchResponse;
  831. //
  832. // parse the returned directory name out of the response text
  833. //
  834. pchResponse = InternetLockErrorText();
  835. if (pchResponse != NULL) {
  836. pchResponse = strstr(pchResponse, "257 ");
  837. if (pchResponse != NULL) {
  838. pchResponse = strchr(pchResponse, '\"');
  839. if (pchResponse != NULL) {
  840. int idx;
  841. ++pchResponse;
  842. for (idx = 0, cchCopied = 0; pchResponse[idx] != '\0'; idx++) {
  843. if (pchResponse[idx] == '\"') {
  844. if (pchResponse[idx + 1] == '\"') {
  845. continue;
  846. }
  847. break;
  848. }
  849. if (cchCopied < cchCurrentDirectory) {
  850. lpszCurrentDirectory[cchCopied] = pchResponse[idx];
  851. }
  852. cchCopied++;
  853. }
  854. if (cchCopied < cchCurrentDirectory) {
  855. lpszCurrentDirectory[cchCopied] = '\0';
  856. error = ERROR_SUCCESS;
  857. } else {
  858. error = ERROR_INSUFFICIENT_BUFFER;
  859. ++cchCopied;
  860. }
  861. } else {
  862. error = ERROR_INTERNET_EXTENDED_ERROR;
  863. }
  864. }
  865. //InternetUnlockErrorText();
  866. }
  867. }
  868. DereferenceFtpSession(lpSessionInfo);
  869. } else {
  870. error = ERROR_INVALID_HANDLE;
  871. }
  872. if ((error == ERROR_SUCCESS) || (error == ERROR_INSUFFICIENT_BUFFER)) {
  873. *lpdwBytesReturned = cchCopied;
  874. }
  875. DEBUG_LEAVE(error);
  876. return error;
  877. }
  878. DWORD
  879. wFtpCommand(
  880. IN HINTERNET hFtpSession,
  881. IN BOOL fExpectResponse,
  882. IN DWORD dwFlags,
  883. IN LPCSTR lpszCommand
  884. )
  885. /*++
  886. Routine Description:
  887. Runs arbitrary command at an FTP server. Direct connect over Internet
  888. Arguments:
  889. hFtpSession - identifies the FTP server
  890. fExpectResponse - TRUE if we expect a response from the server
  891. dwFlags - type of response - ASCII text or BINARY data
  892. lpszCommand - pointer to string describing command to run
  893. Return Value:
  894. DWORD
  895. Success - ERROR_SUCCESS
  896. Failure -
  897. --*/
  898. {
  899. DEBUG_ENTER((DBG_FTP,
  900. Dword,
  901. "wFtpCommand",
  902. "%#x, %#x, %#x, %q",
  903. hFtpSession,
  904. fExpectResponse,
  905. dwFlags,
  906. lpszCommand
  907. ));
  908. LPFTP_SESSION_INFO lpSessionInfo;
  909. DWORD error;
  910. //
  911. // Look up the given handle.
  912. //
  913. if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
  914. FTP_RESPONSE_CODE rcResponse;
  915. //
  916. // Issue the command.
  917. //
  918. error = Command(lpSessionInfo,
  919. fExpectResponse,
  920. dwFlags,
  921. &rcResponse,
  922. lpszCommand
  923. );
  924. if (fExpectResponse && (error == ERROR_SUCCESS)) {
  925. INET_ASSERT(lpSessionInfo->socketData->IsValid());
  926. lpSessionInfo->dwTransferAccess |= (GENERIC_READ|GENERIC_WRITE);
  927. }
  928. #if DBG
  929. else {
  930. INET_ASSERT(! lpSessionInfo->socketData->IsValid());
  931. }
  932. if (error == ERROR_SUCCESS) {
  933. INET_ASSERT(lpSessionInfo->socketControl->IsValid());
  934. }
  935. #endif
  936. DereferenceFtpSession(lpSessionInfo);
  937. } else {
  938. error = ERROR_INVALID_HANDLE;
  939. }
  940. DEBUG_LEAVE(error);
  941. return error;
  942. }
  943. //
  944. // Internet subordinate functions
  945. //
  946. DWORD
  947. wFtpFindNextFile(
  948. IN HINTERNET hFtpSession,
  949. OUT LPWIN32_FIND_DATA lpFindFileData
  950. )
  951. /*++
  952. Routine Description:
  953. Returns the next file found from a call to FtpFindFirstFile().
  954. Arguments:
  955. hFtpSession - Handle to an FTP session, as returned from FtpConnect()
  956. lpFindFileData - Pointer to a buffer that will contain WIN32_FIND_DATA
  957. information when this call succeeds.
  958. Return Value:
  959. DWORD
  960. Success - ERROR_SUCCESS
  961. Failure - ERROR_NO_MORE_FILES
  962. The end of the file list has been reached.
  963. ERROR_INVALID_HANDLE
  964. Can't find session that knows about hFind
  965. --*/
  966. {
  967. DEBUG_ENTER((DBG_FTP,
  968. Dword,
  969. "wFtpFindNextFile",
  970. "%#x, %#x",
  971. hFtpSession,
  972. lpFindFileData
  973. ));
  974. LPFTP_SESSION_INFO lpSessionInfo;
  975. DWORD error;
  976. if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
  977. //
  978. // ISSUE this code is cut & paste from find first - they should both call a
  979. // fn instead
  980. //
  981. if (!IsListEmpty(&lpSessionInfo->FindFileList)) {
  982. PLIST_ENTRY pEntry;
  983. //
  984. // Enumerate the first entry and advance pointers
  985. //
  986. pEntry = RemoveHeadList(&lpSessionInfo->FindFileList);
  987. INET_ASSERT(pEntry != NULL);
  988. CopyMemory(lpFindFileData,
  989. (LPWIN32_FIND_DATA)(pEntry + 1),
  990. sizeof(WIN32_FIND_DATA)
  991. );
  992. FREE_MEMORY(pEntry);
  993. error = ERROR_SUCCESS;
  994. } else {
  995. error = ERROR_NO_MORE_FILES;
  996. }
  997. DereferenceFtpSession(lpSessionInfo);
  998. } else {
  999. error = ERROR_INVALID_HANDLE;
  1000. }
  1001. DEBUG_LEAVE(error);
  1002. return error;
  1003. }
  1004. DWORD
  1005. wFtpFindClose(
  1006. IN HINTERNET hFtpSession
  1007. )
  1008. /*++
  1009. Routine Description:
  1010. Frees the WIN32_FIND_DATA structures in the directory list for this session
  1011. Arguments:
  1012. hFtpSession - handle of an FTP session, created by InternetConnect
  1013. Return Value:
  1014. DWORD
  1015. Success - ERROR_SUCCESS
  1016. Failure - ERROR_INVALID_HANDLE
  1017. --*/
  1018. {
  1019. DEBUG_ENTER((DBG_FTP,
  1020. Dword,
  1021. "wFtpFindClose",
  1022. "%#x",
  1023. hFtpSession
  1024. ));
  1025. LPFTP_SESSION_INFO lpSessionInfo;
  1026. DWORD error;
  1027. if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
  1028. ClearFindList(&lpSessionInfo->FindFileList);
  1029. //
  1030. // this session no longer has an active directory listing
  1031. //
  1032. lpSessionInfo->Flags &= ~FFTP_FIND_ACTIVE;
  1033. DereferenceFtpSession(lpSessionInfo);
  1034. error = ERROR_SUCCESS;
  1035. } else {
  1036. error = ERROR_INVALID_HANDLE;
  1037. }
  1038. DEBUG_LEAVE(error);
  1039. return error;
  1040. }
  1041. DWORD
  1042. wFtpConnect(
  1043. IN LPCSTR lpszServerName,
  1044. IN INTERNET_PORT nServerPort,
  1045. IN LPCSTR lpszUsername,
  1046. IN LPCSTR lpszPassword,
  1047. IN DWORD dwService,
  1048. IN DWORD dwFlags,
  1049. OUT LPHINTERNET lphInternet
  1050. )
  1051. /*++
  1052. Routine Description:
  1053. Creates a new FTP session object
  1054. Arguments:
  1055. lpszServerName - pointer to string identifying FTP server
  1056. nServerPort - port number to connect to
  1057. lpszUsername - pointer to string identifying user name to log on as
  1058. lpszPassword - pointer to string identifying password to use with user name
  1059. dwService - service type parameter (unused)
  1060. dwFlags - session flags. Currently only INTERNET_FLAG_PASSIVE
  1061. is defined
  1062. lphInternet - returned handle of created FTP session
  1063. Return Value:
  1064. DWORD
  1065. Success - ERROR_SUCCESS
  1066. Failure - ERROR_NOT_ENOUGH_MEMORY
  1067. Ran out of memory while creating the session object
  1068. ERROR_INTERNET_OUT_OF_HANDLES
  1069. Ran out of handles while creating the session object
  1070. ERROR_INTERNET_SHUTDOWN
  1071. The DLL is being unloaded
  1072. --*/
  1073. {
  1074. INET_ASSERT(lpszUsername != NULL);
  1075. INET_ASSERT(lpszPassword != NULL);
  1076. DEBUG_ENTER((DBG_FTP,
  1077. Dword,
  1078. "wFtpConnect",
  1079. "%q, %d, %q, %q, %d, %#x, %#x",
  1080. lpszServerName,
  1081. nServerPort,
  1082. lpszUsername,
  1083. lpszPassword,
  1084. dwService,
  1085. dwFlags,
  1086. lphInternet
  1087. ));
  1088. DWORD error;
  1089. LPFTP_SESSION_INFO sessionInfo;
  1090. UNREFERENCED_PARAMETER(lpszUsername);
  1091. UNREFERENCED_PARAMETER(lpszPassword);
  1092. UNREFERENCED_PARAMETER(dwService);
  1093. //
  1094. // create a new FTP session object
  1095. //
  1096. error = CreateFtpSession((LPSTR)lpszServerName,
  1097. nServerPort,
  1098. //
  1099. // if INTERNET_FLAG_PASSIVE then create a passive
  1100. // session object
  1101. //
  1102. (dwFlags & INTERNET_FLAG_PASSIVE)
  1103. ? FFTP_PASSIVE_MODE
  1104. : 0,
  1105. &sessionInfo
  1106. );
  1107. if (error == ERROR_SUCCESS) {
  1108. //
  1109. // return the FTP_SESSION_INFO handle
  1110. //
  1111. *lphInternet = sessionInfo->Handle;
  1112. }
  1113. DEBUG_LEAVE(error);
  1114. return error;
  1115. }
  1116. DWORD
  1117. wFtpMakeConnection(
  1118. IN HINTERNET hFtpSession,
  1119. IN LPCSTR lpszUsername,
  1120. IN LPCSTR lpszPassword
  1121. )
  1122. /*++
  1123. Routine Description:
  1124. Connect with and log into an FTP server.
  1125. This function is cancellable
  1126. Arguments:
  1127. hFtpSession - handle of an FTP session, created by InternetConnect
  1128. pszUsername - pointer to string identifying user name to log on as
  1129. pszPassword - pointer to string identifying password to use with user name
  1130. Return Value:
  1131. DWORD
  1132. Success - ERROR_SUCCESS
  1133. Failure - ERROR_INTERNET_INCORRECT_USER_NAME
  1134. The server didn't like the user name
  1135. ERROR_INTERNET_INCORRECT_PASSWORD
  1136. The server didn't like the password
  1137. ERROR_INTERNET_LOGIN_FAILURE
  1138. The server rejected the login request
  1139. ERROR_FTP_DROPPED
  1140. The connection has been closed
  1141. ERROR_FTP_TRANSFER_IN_PROGRESS
  1142. There is already a transfer in progress on this connection
  1143. ERROR_INTERNET_NAME_NOT_RESOLVED
  1144. Couldn't resolve the server name
  1145. WSA error
  1146. Couldn't connect to the server, or problems while
  1147. communicating with it
  1148. --*/
  1149. {
  1150. DEBUG_ENTER((DBG_FTP,
  1151. Dword,
  1152. "wFtpMakeConnection",
  1153. "%#x, %q, %q",
  1154. hFtpSession,
  1155. lpszUsername,
  1156. lpszPassword
  1157. ));
  1158. LPFTP_SESSION_INFO sessionInfo;
  1159. DWORD error;
  1160. if (FindFtpSession(hFtpSession, &sessionInfo)) {
  1161. //
  1162. // resolve the FTP server's host name and connect to the server
  1163. //
  1164. error = FtpOpenServer(sessionInfo);
  1165. if (error == ERROR_SUCCESS) {
  1166. FTP_RESPONSE_CODE rcResponse;
  1167. //
  1168. // set send and receive timeouts on the control channel socket.
  1169. // Ignore any errors
  1170. //
  1171. sessionInfo->socketControl->SetTimeout(
  1172. SEND_TIMEOUT,
  1173. GetTimeoutValue(INTERNET_OPTION_CONTROL_SEND_TIMEOUT)
  1174. );
  1175. sessionInfo->socketControl->SetTimeout(
  1176. RECEIVE_TIMEOUT,
  1177. GetTimeoutValue(INTERNET_OPTION_CONTROL_RECEIVE_TIMEOUT)
  1178. );
  1179. //
  1180. // check greeting and store in per-thread response text buffer
  1181. //
  1182. error = GetReply(sessionInfo, &rcResponse);
  1183. if (error == ERROR_SUCCESS) {
  1184. //
  1185. // check that the server sent us an affirmative response
  1186. //
  1187. if (rcResponse.Major == FTP_RESPONSE_COMPLETE) {
  1188. //
  1189. // send the user name
  1190. //
  1191. error = Command(sessionInfo,
  1192. FALSE,
  1193. FTP_TRANSFER_TYPE_UNKNOWN,
  1194. &rcResponse,
  1195. "USER %s",
  1196. lpszUsername
  1197. );
  1198. //
  1199. // BUGBUG - is it possible to get success from Command(),
  1200. // but an error from the server - e.g. 332, need
  1201. // account for login?
  1202. //
  1203. if (error == ERROR_SUCCESS) {
  1204. //
  1205. // send the password if required
  1206. //
  1207. if (rcResponse.Major == FTP_RESPONSE_CONTINUE) {
  1208. error = Command(sessionInfo,
  1209. FALSE,
  1210. FTP_TRANSFER_TYPE_UNKNOWN,
  1211. &rcResponse,
  1212. "PASS %s",
  1213. lpszPassword
  1214. );
  1215. //
  1216. // if we failed to send the password, or the password
  1217. // was rejected, or we are attempting to log on as
  1218. // "anonymous" and it turns out that the server does
  1219. // not allow anonymous logon, then return a password
  1220. // error. The caller can still check the response
  1221. // from the server
  1222. //
  1223. if (((error == ERROR_SUCCESS)
  1224. && (rcResponse.Major != FTP_RESPONSE_COMPLETE))
  1225. || (error == ERROR_INTERNET_EXTENDED_ERROR)) {
  1226. if (stricmp(lpszUsername, "anonymous") == 0) {
  1227. error = ERROR_INTERNET_LOGIN_FAILURE;
  1228. } else {
  1229. error = ERROR_INTERNET_INCORRECT_PASSWORD;
  1230. }
  1231. }
  1232. } else if (rcResponse.Major != FTP_RESPONSE_COMPLETE) {
  1233. error = ERROR_INTERNET_INCORRECT_USER_NAME;
  1234. }
  1235. //
  1236. // get the server type
  1237. //
  1238. //if (error == ERROR_SUCCESS) {
  1239. // error = wFtpFindServerType(hFtpSession);
  1240. //}
  1241. }
  1242. } else {
  1243. error = ERROR_INTERNET_LOGIN_FAILURE;
  1244. }
  1245. }
  1246. }
  1247. //
  1248. // success or fail: unlock the session object
  1249. //
  1250. DereferenceFtpSession(sessionInfo);
  1251. //
  1252. // if we failed to login then let wFtpDisconnect() clean up - it will
  1253. // also send a "QUIT" to the server (if we have a control connection)
  1254. // which will ensure a clean exit
  1255. //
  1256. if (error != ERROR_SUCCESS) {
  1257. //
  1258. // if we experience an error during disconnect, we will just ignore
  1259. // it and return the error generated during our failed login attempt
  1260. //
  1261. (void)wFtpDisconnect(hFtpSession, CF_EXPEDITED_CLOSE);
  1262. }
  1263. } else {
  1264. error = ERROR_INVALID_HANDLE;
  1265. }
  1266. DEBUG_LEAVE(error);
  1267. return error;
  1268. }
  1269. DWORD
  1270. wFtpDisconnect(
  1271. IN HINTERNET hFtpSession,
  1272. IN DWORD dwFlags
  1273. )
  1274. /*++
  1275. Routine Description:
  1276. Closes the connection, issues the quit command, etc.,
  1277. Arguments:
  1278. hFtpSession - FTP session created by wFtpConnect
  1279. dwFlags - controlling operation. Can be:
  1280. CF_EXPEDITED_CLOSE - Don't send QUIT to the server, just
  1281. close the control connection
  1282. Return Value:
  1283. DWORD
  1284. Success - ERROR_SUCCESS
  1285. Failure - ERROR_INVALID_HANDLE
  1286. --*/
  1287. {
  1288. DEBUG_ENTER((DBG_FTP,
  1289. Dword,
  1290. "wFtpDisconnect",
  1291. "%#x, %#x",
  1292. hFtpSession,
  1293. dwFlags
  1294. ));
  1295. LPFTP_SESSION_INFO lpSessionInfo;
  1296. DWORD error;
  1297. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  1298. if (lpThreadInfo == NULL) {
  1299. INET_ASSERT(FALSE);
  1300. error = ERROR_INTERNET_INTERNAL_ERROR;
  1301. goto quit;
  1302. }
  1303. if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
  1304. ICSocket * socketControl;
  1305. ICSocket * socketData;
  1306. socketControl = lpSessionInfo->socketControl;
  1307. socketData = lpSessionInfo->socketData;
  1308. //
  1309. // kill any active data transfer
  1310. //
  1311. if (socketData->IsValid()) {
  1312. //
  1313. // set the non-blocking state depending on whether we are called in
  1314. // an app thread context, or in the async scheduler thread context
  1315. //
  1316. //socketData->SetNonBlockingMode(lpThreadInfo->IsAsyncWorkerThread);
  1317. if (dwFlags & CF_EXPEDITED_CLOSE) {
  1318. error = socketData->Close();
  1319. } else {
  1320. error = wFtpCloseFile(hFtpSession);
  1321. if (error != ERROR_SUCCESS) {
  1322. DEBUG_PRINT(WORKER,
  1323. ERROR,
  1324. ("wFtpCloseFile() returns %d\n",
  1325. error
  1326. ));
  1327. }
  1328. }
  1329. }
  1330. INET_ASSERT(!lpSessionInfo->socketData->IsValid());
  1331. //
  1332. // perform graceful close to the server if we have a control connection
  1333. //
  1334. if (socketControl->IsValid()) {
  1335. //
  1336. // set the non-blocking state depending on whether we are called in
  1337. // an app thread context, or in the async scheduler thread context
  1338. //
  1339. //socketControl->SetNonBlockingMode(lpThreadInfo->IsAsyncWorkerThread);
  1340. if (!(dwFlags & CF_EXPEDITED_CLOSE)) {
  1341. FTP_RESPONSE_CODE rcResponse;
  1342. Command(lpSessionInfo,
  1343. FALSE,
  1344. FTP_TRANSFER_TYPE_UNKNOWN,
  1345. &rcResponse,
  1346. "QUIT"
  1347. );
  1348. }
  1349. lpSessionInfo->socketControl->Disconnect(SF_INDICATE);
  1350. }
  1351. //
  1352. // finally kill the FTP_SESSION_INFO structure
  1353. //
  1354. TerminateFtpSession(lpSessionInfo);
  1355. error = ERROR_SUCCESS;
  1356. } else {
  1357. error = ERROR_INVALID_HANDLE;
  1358. }
  1359. quit:
  1360. DEBUG_LEAVE(error);
  1361. return error;
  1362. }
  1363. DWORD
  1364. wFtpReadFile(
  1365. IN HINTERNET hFtpSession,
  1366. IN LPVOID lpBuffer,
  1367. IN DWORD dwNumberOfBytesToRead,
  1368. OUT LPDWORD lpdwNumberOfBytesRead
  1369. )
  1370. /*++
  1371. Routine Description:
  1372. Reads data from the FTP server. We use the data channel
  1373. Arguments:
  1374. hFtpSession - handle identifying FTP session
  1375. lpBuffer - pointer to buffer for received data
  1376. dwNumberOfBytesToRead - size of lpBuffer in bytes
  1377. lpdwNumberOfBytesRead - returned number of bytes received
  1378. Return Value:
  1379. DWORD
  1380. Success - ERROR_SUCCESS
  1381. Failure - ERROR_INVALID_HANDLE
  1382. Couldn't find hFtpSession
  1383. ERROR_ACCESS_DENIED
  1384. This session doesn't have read access (?)
  1385. ERROR_FTP_DROPPED
  1386. The data channel has been closed
  1387. --*/
  1388. {
  1389. DEBUG_ENTER((DBG_FTP,
  1390. Dword,
  1391. "wFtpReadFile",
  1392. "%#x, %#x, %d, %#x",
  1393. hFtpSession,
  1394. lpBuffer,
  1395. dwNumberOfBytesToRead,
  1396. lpdwNumberOfBytesRead
  1397. ));
  1398. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  1399. DWORD error;
  1400. if (lpThreadInfo == NULL) {
  1401. INET_ASSERT(FALSE);
  1402. error = ERROR_INTERNET_INTERNAL_ERROR;
  1403. goto quit;
  1404. }
  1405. LPFTP_SESSION_INFO lpSessionInfo;
  1406. ICSocket * socketData;
  1407. BOOL eof;
  1408. DWORD bytesReceived;
  1409. //
  1410. // initialize variables in case we quit early (i.e. via goto)
  1411. //
  1412. bytesReceived = 0;
  1413. //
  1414. // find the FTP_SESSION_INFO and ensure it is set up to receive data
  1415. //
  1416. if (!FindFtpSession(hFtpSession, &lpSessionInfo)) {
  1417. error = ERROR_INVALID_HANDLE;
  1418. goto quit;
  1419. }
  1420. //
  1421. // if FFTP_EOF is set then we already reached the end the file
  1422. //
  1423. if (lpSessionInfo->Flags & FFTP_EOF) {
  1424. error = ERROR_SUCCESS;
  1425. goto unlock_and_quit;
  1426. }
  1427. //
  1428. // get the data socket. If it has become INVALID_SOCKET then the server
  1429. // closed the connection
  1430. //
  1431. socketData = lpSessionInfo->socketData;
  1432. if (!socketData->IsValid()) {
  1433. error = ERROR_FTP_DROPPED;
  1434. goto unlock_and_quit;
  1435. }
  1436. if (!(lpSessionInfo->dwTransferAccess & GENERIC_READ)) {
  1437. error = ERROR_ACCESS_DENIED;
  1438. goto unlock_and_quit;
  1439. }
  1440. //
  1441. // read until we fill the users buffer, get an error, or get to EOF
  1442. //
  1443. DWORD bufferRemaining;
  1444. bufferRemaining = dwNumberOfBytesToRead;
  1445. error = socketData->Receive(
  1446. &lpBuffer,
  1447. &dwNumberOfBytesToRead, // lpdwBufferLength
  1448. &bufferRemaining, // lpdwBufferRemaining
  1449. &bytesReceived, // lpdwBytesReceived
  1450. 0, // dwExtraSpace
  1451. SF_RECEIVE_ALL
  1452. | SF_INDICATE,
  1453. &eof
  1454. );
  1455. if (error == ERROR_SUCCESS) {
  1456. //
  1457. // if we got to EOF then the server will have closed the data
  1458. // connection. We need to close the socket at our end. If this is
  1459. // a passive connection then we initiate session termination
  1460. //
  1461. if (eof) {
  1462. (void)socketData->Close();
  1463. INET_ASSERT(lpSessionInfo->socketData == socketData);
  1464. //
  1465. // reset the abort flag - we no longer have to send and ABOR command
  1466. // when we close the handle
  1467. //
  1468. lpSessionInfo->Flags &= ~FFTP_ABORT_TRANSFER;
  1469. //
  1470. // set EOF in the FTP_SESSION_INFO flags so we know next time
  1471. // we call this function that the session is not dropped, but
  1472. // that we already reached the end of the data
  1473. //
  1474. lpSessionInfo->Flags |= FFTP_EOF;
  1475. }
  1476. }
  1477. //
  1478. // BUGBUG - in error case we should probably close the socket, set
  1479. // INVALID_SOCKET in the FTP_SESSION_INFO, etc.
  1480. //
  1481. unlock_and_quit:
  1482. //
  1483. // update the output parameters if we succeeded
  1484. //
  1485. if (error == ERROR_SUCCESS) {
  1486. *lpdwNumberOfBytesRead = bytesReceived;
  1487. }
  1488. DereferenceFtpSession(lpSessionInfo);
  1489. quit:
  1490. DEBUG_LEAVE(error);
  1491. return error;
  1492. }
  1493. DWORD
  1494. wFtpWriteFile(
  1495. IN HINTERNET hFtpSession,
  1496. IN LPVOID lpBuffer,
  1497. IN DWORD dwNumberOfBytesToWrite,
  1498. OUT LPDWORD lpdwNumberOfBytesWritten
  1499. )
  1500. /*++
  1501. Routine Description:
  1502. Writes data to the FTP server. We use the data channel
  1503. Arguments:
  1504. hFtpSession - handle identifying FTP session
  1505. lpBuffer - pointer to buffer containing data to write
  1506. dwNumberOfBytesToWrite - size of lpBuffer in bytes
  1507. lpdwNumberOfBytesWritten - returned number of bytes sent
  1508. Return Value:
  1509. DWORD
  1510. Success - ERROR_SUCCESS
  1511. Failure - ERROR_INVALID_HANDLE
  1512. Couldn't find hFtpSession
  1513. ERROR_ACCESS_DENIED
  1514. This session doesn't have write access (?)
  1515. ERROR_FTP_DROPPED
  1516. The data channel has been closed
  1517. --*/
  1518. {
  1519. DEBUG_ENTER((DBG_FTP,
  1520. Dword,
  1521. "wFtpWriteFile",
  1522. "%#x, %#x, %d, %#x",
  1523. hFtpSession,
  1524. lpBuffer,
  1525. dwNumberOfBytesToWrite,
  1526. lpdwNumberOfBytesWritten
  1527. ));
  1528. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  1529. DWORD error;
  1530. if (lpThreadInfo == NULL) {
  1531. INET_ASSERT(FALSE);
  1532. error = ERROR_INTERNET_INTERNAL_ERROR;
  1533. goto quit;
  1534. }
  1535. LPFTP_SESSION_INFO lpSessionInfo;
  1536. ICSocket * socketData;
  1537. int nSent;
  1538. //
  1539. // find the FTP_SESSION_INFO and ensure it is set up to send data
  1540. //
  1541. if (!FindFtpSession(hFtpSession, &lpSessionInfo)) {
  1542. error = ERROR_INVALID_HANDLE;
  1543. goto quit;
  1544. }
  1545. socketData = lpSessionInfo->socketData;
  1546. if (! socketData->IsValid()) {
  1547. error = ERROR_FTP_DROPPED;
  1548. goto unlock_and_quit;
  1549. }
  1550. if (!(lpSessionInfo->dwTransferAccess & GENERIC_WRITE)) {
  1551. error = ERROR_ACCESS_DENIED;
  1552. goto unlock_and_quit;
  1553. }
  1554. error = socketData->Send(lpBuffer, dwNumberOfBytesToWrite, SF_INDICATE);
  1555. if (error == ERROR_SUCCESS) {
  1556. *lpdwNumberOfBytesWritten = dwNumberOfBytesToWrite;
  1557. } else {
  1558. //
  1559. // we had a failure. We should check the control socket for any error
  1560. // info from the server
  1561. //
  1562. FTP_RESPONSE_CODE response = {0};
  1563. response.Major = FTP_RESPONSE_PERMANENT_FAILURE;
  1564. //
  1565. SetSessionLastResponseCode(lpSessionInfo, &response);
  1566. }
  1567. unlock_and_quit:
  1568. DereferenceFtpSession(lpSessionInfo);
  1569. quit:
  1570. DEBUG_LEAVE(error);
  1571. return error;
  1572. }
  1573. DWORD
  1574. wFtpQueryDataAvailable(
  1575. IN HINTERNET hFtpSession,
  1576. OUT LPDWORD lpdwNumberOfBytesAvailable
  1577. )
  1578. /*++
  1579. Routine Description:
  1580. Determines amount of data available to be received on a data (file) socket
  1581. Arguments:
  1582. hFtpSession - identifies FTP session
  1583. lpdwNumberOfBytesAvailable - returned number of bytes available
  1584. Return Value:
  1585. DWORD
  1586. Success - ERROR_SUCCESS
  1587. Failure - ERROR_INVALID_HANDLE
  1588. --*/
  1589. {
  1590. DEBUG_ENTER((DBG_FTP,
  1591. Dword,
  1592. "wFtpQueryDataAvailable",
  1593. "%#x, %#x",
  1594. hFtpSession,
  1595. lpdwNumberOfBytesAvailable
  1596. ));
  1597. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  1598. DWORD error;
  1599. HINTERNET_HANDLE_TYPE handleType;
  1600. error = RGetHandleType(lpThreadInfo->hObjectMapped, &handleType);
  1601. if (error != ERROR_SUCCESS) {
  1602. return (error);
  1603. }
  1604. *lpdwNumberOfBytesAvailable = 0;
  1605. if (lpThreadInfo == NULL) {
  1606. INET_ASSERT(FALSE);
  1607. error = ERROR_INTERNET_INTERNAL_ERROR;
  1608. goto quit;
  1609. }
  1610. LPFTP_SESSION_INFO lpSessionInfo;
  1611. if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
  1612. //
  1613. // if we are currently performing a directory list then return the size
  1614. // of a dir list entry
  1615. //
  1616. if (lpSessionInfo->Flags & FFTP_FIND_ACTIVE) {
  1617. *lpdwNumberOfBytesAvailable = !IsListEmpty(&lpSessionInfo->FindFileList)
  1618. ? sizeof(WIN32_FIND_DATA) : 0;
  1619. } else {
  1620. //
  1621. // otherwise, if we are receiving data, find out how much
  1622. //
  1623. ICSocket * socketData;
  1624. socketData = lpSessionInfo->socketData;
  1625. if (socketData->IsValid()) {
  1626. error = socketData->DataAvailable(lpdwNumberOfBytesAvailable);
  1627. } else {
  1628. //
  1629. // there is no data connection
  1630. //
  1631. *lpdwNumberOfBytesAvailable = 0;
  1632. error = ERROR_SUCCESS;
  1633. }
  1634. }
  1635. DereferenceFtpSession(lpSessionInfo);
  1636. } else {
  1637. error = ERROR_INVALID_HANDLE;
  1638. }
  1639. quit:
  1640. if ((error == ERROR_SUCCESS) && (*lpdwNumberOfBytesAvailable == 0)) {
  1641. InbLocalEndCacheWrite(lpThreadInfo->hObjectMapped,
  1642. ((handleType==TypeFtpFindHandleHtml)
  1643. ?"htm":NULL),
  1644. TRUE);
  1645. }
  1646. DEBUG_LEAVE(error);
  1647. return error;
  1648. }
  1649. DWORD
  1650. wFtpCloseFile(
  1651. IN HINTERNET hFtpSession
  1652. )
  1653. /*++
  1654. Routine Description:
  1655. Terminates the connection used for file transfer. The connection may already
  1656. be closed (by the server during a READ, or by the client during a WRITE) in
  1657. which case we just need to receive the confirmation (226) on the control
  1658. socket. If the connection is still open, or the abort flag is set for this
  1659. connection, then this is an abnormal termination, and we need to send an
  1660. ABORt command
  1661. Arguments:
  1662. hFtpSession - Identifies the session on which to terminate file transfer
  1663. Return Value:
  1664. DWORD
  1665. Success - ERROR_SUCCESS
  1666. Failure - ERROR_INVALID_HANDLE
  1667. Couldn't find the FTP_SESSION_INFO corresponding to
  1668. hFtpSession
  1669. --*/
  1670. {
  1671. DEBUG_ENTER((DBG_FTP,
  1672. Dword,
  1673. "wFtpCloseFile",
  1674. "%#x",
  1675. hFtpSession
  1676. ));
  1677. LPFTP_SESSION_INFO lpSessionInfo;
  1678. DWORD error;
  1679. if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
  1680. BOOL getResponse;
  1681. ICSocket * socketData;
  1682. FTP_RESPONSE_CODE rcResponse;
  1683. socketData = lpSessionInfo->socketData;
  1684. if (socketData->IsValid()) {
  1685. //
  1686. // if we are performing a read/write operation and the transfer
  1687. // isn't complete then abort the connection
  1688. //
  1689. if (lpSessionInfo->Flags & FFTP_ABORT_TRANSFER) {
  1690. AbortTransfer(lpSessionInfo);
  1691. ResetSocket(lpSessionInfo->socketData);
  1692. } else {
  1693. //
  1694. // in all other cases - completed READ, complete or incomplete
  1695. // WRITE - just close the socket
  1696. //
  1697. lpSessionInfo->socketData->Close();
  1698. }
  1699. } else if (lpSessionInfo->Flags & FFTP_ABORT_TRANSFER) {
  1700. //
  1701. // we have no data socket, but the abort transfer flag is set. We
  1702. // are probably closing a file we opened for read without having
  1703. // read any data. In this case we send an abort anyway
  1704. //
  1705. AbortTransfer(lpSessionInfo);
  1706. }
  1707. //
  1708. // get the server response - we expect either 226 to a good transfer,
  1709. // or 426 for an aborted transfer...
  1710. //
  1711. GetSessionLastResponseCode(lpSessionInfo, &rcResponse);
  1712. if (rcResponse.Major == FTP_RESPONSE_PRELIMINARY) {
  1713. error = GetReply(lpSessionInfo, &rcResponse);
  1714. SetLastError(ERROR_SUCCESS); // a-thkesa;. added from Win CE fix of BUG WinSE: 23985
  1715. if ((error == ERROR_SUCCESS)
  1716. && (rcResponse.Major != FTP_RESPONSE_COMPLETE)) {
  1717. error = ERROR_INTERNET_EXTENDED_ERROR;
  1718. SetLastError(error); // a-thkesa;. added from Win CE fix of BUG WinSE: 23985
  1719. }
  1720. } else if (rcResponse.Major == FTP_RESPONSE_PERMANENT_FAILURE){
  1721. error = ERROR_SUCCESS;
  1722. } else {
  1723. if(rcResponse.Major != FTP_RESPONSE_COMPLETE)
  1724. error = GetReply(lpSessionInfo, &rcResponse);
  1725. else
  1726. error = ERROR_SUCCESS;
  1727. }
  1728. //
  1729. // reset the ABORT, FILE_ACTIVE and EOF flags
  1730. //
  1731. lpSessionInfo->Flags &= ~(FFTP_ABORT_TRANSFER
  1732. | FFTP_EOF
  1733. | FFTP_FILE_ACTIVE
  1734. );
  1735. DereferenceFtpSession(lpSessionInfo);
  1736. } else {
  1737. error = ERROR_INVALID_HANDLE;
  1738. }
  1739. DEBUG_LEAVE(error);
  1740. return error;
  1741. }
  1742. DWORD
  1743. wFtpFindServerType(
  1744. IN HINTERNET hFtpSession
  1745. )
  1746. /*++
  1747. Routine Description:
  1748. Determines the type of server we are talking to (NT or Unix)
  1749. Arguments:
  1750. hFtpSession - identifies FTP_SESSION_INFO. The structure ServerType field
  1751. will be updated with the discovered info
  1752. Return Value:
  1753. DWORD
  1754. Success - ERROR_SUCCESS
  1755. Failure - ERROR_INVALID_HANDLE
  1756. Couldn't find the FTP_SESSION_INFO corresponding to
  1757. hFtpSession
  1758. --*/
  1759. {
  1760. DEBUG_ENTER((DBG_FTP,
  1761. Dword,
  1762. "wFtpFindServerType",
  1763. "%#x",
  1764. hFtpSession
  1765. ));
  1766. LPFTP_SESSION_INFO lpSessionInfo;
  1767. DWORD error;
  1768. if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
  1769. FTP_RESPONSE_CODE rcResponse;
  1770. error = Command(lpSessionInfo,
  1771. FALSE,
  1772. FTP_TRANSFER_TYPE_UNKNOWN,
  1773. &rcResponse,
  1774. "SYST"
  1775. );
  1776. if (error == ERROR_SUCCESS) {
  1777. LPSTR lpszResponse = InternetLockErrorText();
  1778. if (lpszResponse != NULL) {
  1779. FTP_SERVER_TYPE serverType = FTP_SERVER_TYPE_UNKNOWN;
  1780. //
  1781. // "215 " must be first token in response text
  1782. //
  1783. lpszResponse = strstr(lpszResponse, "215 ");
  1784. if (lpszResponse != NULL) {
  1785. //
  1786. // check for existence of "Windows_NT" or "Unix" (case
  1787. // insensitive comparison)
  1788. //
  1789. //
  1790. // BUGBUG - find out from MuraliK/TerryK the values these
  1791. // ids can have
  1792. //
  1793. static struct {
  1794. LPCSTR lpszSystemName;
  1795. FTP_SERVER_TYPE ServerType;
  1796. } FtpServerTypes[] = {
  1797. "Windows_NT", FTP_SERVER_TYPE_NT,
  1798. "Unix", FTP_SERVER_TYPE_UNIX
  1799. };
  1800. DWORD textLength = strlen(lpszResponse);
  1801. for (int i = 0; i < ARRAY_ELEMENTS(FtpServerTypes); ++i) {
  1802. if (strnistr(lpszResponse,
  1803. (LPSTR)FtpServerTypes[i].lpszSystemName,
  1804. textLength
  1805. ) != NULL) {
  1806. serverType = FtpServerTypes[i].ServerType;
  1807. DEBUG_PRINT(FTP,
  1808. INFO,
  1809. ("serverType = %s (%d)\n",
  1810. InternetMapFtpServerType(serverType),
  1811. serverType
  1812. ));
  1813. break;
  1814. }
  1815. }
  1816. }
  1817. lpSessionInfo->ServerType = serverType;
  1818. //InternetUnlockErrorText();
  1819. }
  1820. }
  1821. DereferenceFtpSession(lpSessionInfo);
  1822. } else {
  1823. error = ERROR_INVALID_HANDLE;
  1824. }
  1825. DEBUG_LEAVE(error);
  1826. return error;
  1827. }
  1828. #if 0
  1829. //
  1830. // We don't use this today, because FtpGetFileSize does not support
  1831. // issuing backround commands through the FTP Control socket while
  1832. // the user is doing an FTP download (with FtpOpenFile)
  1833. //
  1834. DWORD
  1835. wFtpGetFileSize(
  1836. IN HINTERNET hMappedFtpSession,
  1837. IN LPFTP_SESSION_INFO lpSessionInfo,
  1838. OUT LPDWORD lpdwFileSizeLow,
  1839. OUT LPDWORD lpdwFileSizeHigh
  1840. )
  1841. /*++
  1842. Routine Description:
  1843. Finds size of a file at server
  1844. Arguments:
  1845. hFtpSession - identifies mapped FTP handle obj
  1846. lpSessionInfo - LPFTP_SESSION_INFO structure ptr.
  1847. lpdwFileSizeLow - pointer to low dword of file size
  1848. lpdwFileSizeHigh - optional output pointer to high dword of file size
  1849. Return Value:
  1850. DWORD
  1851. Success - ERROR_SUCCESS
  1852. Failure - ERROR_INVALID_HANDLE
  1853. Couldn't find the FTP_SESSION_INFO corresponding to
  1854. hFtpSession
  1855. --*/
  1856. {
  1857. DEBUG_ENTER((DBG_FTP,
  1858. Dword,
  1859. "wFtpGetFileSize",
  1860. "%#x, %#x, %#x, %#x",
  1861. hMappedFtpSession,
  1862. lpSessionInfo,
  1863. lpdwFileSizeLow,
  1864. lpdwFileSizeHigh
  1865. ));
  1866. DWORD error = ERROR_INTERNET_INTERNAL_ERROR;
  1867. FTP_FILE_HANDLE_OBJECT * pFileMapped = (FTP_FILE_HANDLE_OBJECT *) hMappedFtpSession;
  1868. *lpdwFileSizeLow = 0;
  1869. *lpdwFileSizeHigh = 0;
  1870. if (lpSessionInfo) {
  1871. FTP_RESPONSE_CODE rcResponse;
  1872. error = Command(lpSessionInfo,
  1873. FALSE,
  1874. FTP_TRANSFER_TYPE_UNKNOWN,
  1875. &rcResponse,
  1876. "SIZE %s",
  1877. pFileMapped->GetFileName()
  1878. );
  1879. if (error == ERROR_SUCCESS) {
  1880. LPSTR lpszResponse = InternetLockErrorText();
  1881. if (lpszResponse != NULL) {
  1882. FTP_SERVER_TYPE serverType = FTP_SERVER_TYPE_UNKNOWN;
  1883. //
  1884. // "213 " must be first token in response text of file size
  1885. //
  1886. lpszResponse = strstr(lpszResponse, "213 ");
  1887. if (lpszResponse != NULL) {
  1888. *lpdwFileSizeLow = atoi(lpszResponse);
  1889. error = ERROR_SUCCESS;
  1890. }
  1891. }
  1892. }
  1893. } else {
  1894. error = ERROR_INVALID_HANDLE;
  1895. }
  1896. DEBUG_LEAVE(error);
  1897. return error;
  1898. }
  1899. #endif
  1900. //
  1901. // private debug functions
  1902. //
  1903. #if INET_DEBUG
  1904. PRIVATE
  1905. DEBUG_FUNCTION
  1906. LPSTR
  1907. InternetMapFtpServerType(
  1908. IN FTP_SERVER_TYPE ServerType
  1909. )
  1910. {
  1911. switch (ServerType) {
  1912. CASE_OF(FTP_SERVER_TYPE_UNKNOWN);
  1913. CASE_OF(FTP_SERVER_TYPE_NT);
  1914. CASE_OF(FTP_SERVER_TYPE_UNIX);
  1915. }
  1916. return "?";
  1917. }
  1918. #endif // INET_DEBUG