ftpapir.cxx
Upload User: caisha3
Upload Date: 2013-09-21
Package Size: 208739k
Code Size: 66k
Category:

Windows Develop

Development Platform:

Visual C++

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