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
2629 lines
67 KiB
/*++
|
|
|
|
Copyright (c) 1994 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ftpapir.cxx
|
|
|
|
Abstract:
|
|
|
|
Contains the remote-side FTP API worker functions. In each case, the API
|
|
proper validates the arguments. The worker functions contained herein just
|
|
perform the requested operation with the supplied arguments.
|
|
|
|
These functions are the remote side of the RPC interface. If the DLL is
|
|
the abstract0 version (no RPC) then the A forms of the functions simply
|
|
call the w functions
|
|
|
|
Contents:
|
|
wFtpFindFirstFile
|
|
wFtpDeleteFile
|
|
wFtpRenameFile
|
|
wFtpOpenFile
|
|
wFtpCreateDirectory
|
|
wFtpRemoveDirectory
|
|
wFtpSetCurrentDirectory
|
|
wFtpGetCurrentDirectory
|
|
wFtpCommand
|
|
wFtpFindNextFile
|
|
wFtpFindClose
|
|
wFtpConnect
|
|
wFtpMakeConnection
|
|
wFtpDisconnect
|
|
wFtpReadFile
|
|
wFtpWriteFile
|
|
wFtpQueryDataAvailable
|
|
wFtpCloseFile
|
|
wFtpFindServerType
|
|
wFtpGetFileSize
|
|
|
|
Author:
|
|
|
|
Heath Hunnicutt [t-heathh] 13-Jul-1994
|
|
|
|
Environment:
|
|
|
|
Win32(s) user-level DLL
|
|
|
|
Revision History:
|
|
|
|
09-Mar-1995 rfirth
|
|
Created new file/worker functions from functions contained in
|
|
findfile.c, ftphelp.c
|
|
|
|
--*/
|
|
|
|
#include <wininetp.h>
|
|
#include "ftpapih.h"
|
|
|
|
//
|
|
// private macros
|
|
//
|
|
|
|
#define CASE_OF(constant) case constant: return # constant
|
|
|
|
//
|
|
// private debug functions
|
|
//
|
|
|
|
#if INET_DEBUG
|
|
|
|
PRIVATE
|
|
DEBUG_FUNCTION
|
|
LPSTR
|
|
InternetMapFtpServerType(
|
|
IN FTP_SERVER_TYPE ServerType
|
|
);
|
|
|
|
#else
|
|
|
|
#define InternetMapFtpServerType(x) (VOID)(x)
|
|
|
|
#endif // INET_DEBUG
|
|
|
|
//
|
|
// external functions
|
|
//
|
|
|
|
extern
|
|
DWORD
|
|
InbLocalEndCacheWrite(
|
|
IN HINTERNET hFtpFile,
|
|
IN LPSTR lpszFileExtension,
|
|
IN BOOL fNormal
|
|
);
|
|
|
|
//
|
|
// functions
|
|
//
|
|
|
|
|
|
DWORD
|
|
wFtpFindFirstFile(
|
|
IN HINTERNET hFtpSession,
|
|
IN LPCSTR lpszFilespec,
|
|
OUT LPWIN32_FIND_DATA lpFindFileData OPTIONAL,
|
|
OUT LPHINTERNET lphInternet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Download the remote site's directory listing and parse it into
|
|
WIN32_FIND_DATA structures that we can pass back to the app.
|
|
|
|
If the FTP session is currently involved in a data transfer, such as
|
|
a FtpOpenFile()....FtpCloseFile() series of calls, this function will
|
|
fail.
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - Handle to an FTP session, as returned from FtpOpen()
|
|
|
|
lpszFilespec - Pointer to a string containing a file specification
|
|
to find. May be empty, but not NULL
|
|
|
|
lpFindFileData - Pointer to a buffer that will contain WIN32_FIND_DATA
|
|
information when this call succeeds.
|
|
If this parameter is not supplied, then any find data
|
|
will be returned via InternetFindNextFile()
|
|
|
|
lphInternet - place to return open find handle
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
*lphInternet contains new find handle
|
|
|
|
Failure - ERROR_INVALID_HANDLE
|
|
The session handle is not recognized
|
|
|
|
ERROR_FTP_TRANSFER_IN_PROGRESS
|
|
The data connection is already in use
|
|
|
|
ERROR_NO_MORE_FILES
|
|
The end of the directory listing has been reached
|
|
|
|
ERROR_INTERNET_EXTENDED_ERROR
|
|
Call InternetGetLastResponseInfo() for the text
|
|
|
|
ERROR_INTERNET_INTERNAL_ERROR
|
|
Something bad happened
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpFindFirstFile",
|
|
"%#x, %q, %#x, %#x",
|
|
hFtpSession,
|
|
lpszFilespec,
|
|
lpFindFileData,
|
|
lphInternet
|
|
));
|
|
|
|
LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
|
|
LPSTR lpBuffer = NULL;
|
|
DWORD error;
|
|
|
|
if (lpThreadInfo == NULL) {
|
|
|
|
INET_ASSERT(FALSE);
|
|
|
|
error = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto quit;
|
|
}
|
|
|
|
LPFTP_SESSION_INFO lpSessionInfo;
|
|
|
|
if (!FindFtpSession(hFtpSession, &lpSessionInfo)) {
|
|
error = ERROR_INVALID_HANDLE;
|
|
goto quit;
|
|
}
|
|
|
|
//
|
|
// acquire the session lock while we check and optionally set the active
|
|
// find flag
|
|
//
|
|
|
|
AcquireFtpSessionLock(lpSessionInfo);
|
|
|
|
if (!(lpSessionInfo->Flags & FFTP_FIND_ACTIVE)) {
|
|
lpSessionInfo->Flags |= FFTP_FIND_ACTIVE;
|
|
error = ERROR_SUCCESS;
|
|
} else {
|
|
error = ERROR_FTP_TRANSFER_IN_PROGRESS;
|
|
}
|
|
|
|
ReleaseFtpSessionLock(lpSessionInfo);
|
|
|
|
//
|
|
// if we already have a directory listing on this connection, then we can
|
|
// not allow another one, until the current listing is cleared out by the
|
|
// app calling InternetCloseHandle()
|
|
//
|
|
|
|
if (error != ERROR_SUCCESS) {
|
|
goto deref_exit;
|
|
}
|
|
|
|
//
|
|
// the filespec may have a path component. We assume that any wild-cards
|
|
// will only be in the filename part. We use the path part in the directory
|
|
// request and the filename part when parsing the directory output
|
|
//
|
|
|
|
char pathBuf[INTERNET_MAX_PATH_LENGTH + 1];
|
|
LPSTR lpszPathPart;
|
|
LPSTR lpszFilePart;
|
|
BOOL isWild;
|
|
DWORD dwFilePartLength;
|
|
|
|
lpszFilePart = (LPSTR)lpszFilespec;
|
|
lpszPathPart = NULL;
|
|
dwFilePartLength = lstrlen(lpszFilePart);
|
|
|
|
if (*lpszFilePart != '\0') {
|
|
|
|
LPSTR pathSeparator;
|
|
|
|
pathSeparator = _memrchr(lpszFilePart, '\\', dwFilePartLength);
|
|
if (pathSeparator == NULL) {
|
|
pathSeparator = _memrchr(lpszFilePart, '/', dwFilePartLength);
|
|
}
|
|
if (pathSeparator != NULL) {
|
|
|
|
int len = (int) (pathSeparator - lpszFilePart) + 1;
|
|
|
|
if (len < sizeof(pathBuf)) {
|
|
memcpy(pathBuf, lpszFilePart, len);
|
|
pathBuf[len] = '\0';
|
|
lpszPathPart = pathBuf;
|
|
lpszFilePart = pathSeparator + 1;
|
|
|
|
DEBUG_PRINT(FTP,
|
|
INFO,
|
|
("lpszPathPart = %q, lpszFilePart = %q\n",
|
|
lpszPathPart,
|
|
lpszFilePart
|
|
));
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// determine whether the caller is asking for a fuzzy file match, or
|
|
// (typically) the request is for the contents of a directory
|
|
//
|
|
|
|
isWild = IsFilespecWild(lpszFilePart);
|
|
} else {
|
|
|
|
//
|
|
// empty string - not asking for wildcard search
|
|
//
|
|
|
|
isWild = FALSE;
|
|
}
|
|
|
|
//
|
|
// and ask the FTP server for the directory listing
|
|
//
|
|
|
|
FTP_RESPONSE_CODE rcResponse;
|
|
|
|
error = Command(lpSessionInfo,
|
|
TRUE,
|
|
FTP_TRANSFER_TYPE_ASCII,
|
|
&rcResponse,
|
|
((lpszPathPart == NULL) && (isWild || (*lpszFilePart == '\0')))
|
|
? "LIST"
|
|
: "LIST %s",
|
|
(lpszPathPart == NULL)
|
|
? lpszFilePart
|
|
: isWild
|
|
? lpszPathPart
|
|
: lpszFilespec
|
|
);
|
|
|
|
//
|
|
// quit early if we failed to send the command, or the server didn't
|
|
// understand it
|
|
//
|
|
|
|
if (error != ERROR_SUCCESS) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// presumably, the server has sent us a directory listing. Receive it
|
|
//
|
|
|
|
DWORD bufferLength;
|
|
DWORD bufferLeft;
|
|
DWORD bytesReceived;
|
|
BOOL eof;
|
|
|
|
bufferLength = 0;
|
|
bufferLeft = 0;
|
|
bytesReceived = 0;
|
|
|
|
error = lpSessionInfo->socketData->Receive((LPVOID *)&lpBuffer,
|
|
&bufferLength,
|
|
&bufferLeft,
|
|
&bytesReceived,
|
|
0,
|
|
SF_EXPAND
|
|
| SF_COMPRESS
|
|
| SF_RECEIVE_ALL
|
|
| SF_INDICATE,
|
|
&eof
|
|
);
|
|
|
|
//
|
|
// we are done with the data connection
|
|
//
|
|
|
|
lpSessionInfo->socketData->Close();
|
|
|
|
//
|
|
// quit now if we had an error while receiving
|
|
//
|
|
|
|
if (error != ERROR_SUCCESS) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// if the previous response was preliminary then get the final response from
|
|
// the FTP server
|
|
//
|
|
|
|
if (rcResponse.Major != FTP_RESPONSE_COMPLETE) {
|
|
error = GetReply(lpSessionInfo, &rcResponse);
|
|
if (error != ERROR_SUCCESS) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// check response for failure
|
|
//
|
|
|
|
if (rcResponse.Major != FTP_RESPONSE_COMPLETE) {
|
|
|
|
//
|
|
// <-- Return "command failed" error code
|
|
//
|
|
if(rcResponse.Status == FTP_RESPONSE_ACTION_NOT_TAKEN)
|
|
{
|
|
error = ERROR_NO_MORE_FILES;
|
|
}
|
|
else
|
|
{
|
|
error = ERROR_INTERNET_EXTENDED_ERROR;
|
|
}
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (bytesReceived == 0) {
|
|
|
|
DEBUG_PRINT(WORKER,
|
|
ERROR,
|
|
("ReceiveData() returns 0 bytes\n"
|
|
));
|
|
|
|
error = ERROR_NO_MORE_FILES;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// trap bad servers which return a not-found message in the data stream. We
|
|
// only do this if we are not performing a wild-card search (because the
|
|
// wild-card match will fail to match anything if the target file or path
|
|
// cannot be found)
|
|
//
|
|
|
|
LPSTR lpszSearch;
|
|
DWORD dwSearch;
|
|
|
|
lpszSearch = (lpszPathPart == NULL) ? lpszFilePart : (LPSTR)lpszFilespec;
|
|
dwSearch = lstrlen(lpszSearch);
|
|
|
|
if (!isWild && (bytesReceived > dwSearch)) {
|
|
if (!_strnicmp(lpBuffer, lpszSearch, dwSearch)
|
|
&& (lpBuffer[dwSearch] == ':')) {
|
|
|
|
static char testChars[] = {'\r', '\n', '\0'};
|
|
LPSTR lpStartOfString = lpBuffer + dwSearch + 1;
|
|
LPSTR lpEndOfString;
|
|
|
|
for (int i = 0; i < ARRAY_ELEMENTS(testChars); ++i) {
|
|
lpEndOfString = strchr(lpStartOfString, testChars[i]);
|
|
if (lpEndOfString != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// we should have found at least one of the target characters
|
|
//
|
|
|
|
INET_ASSERT(lpEndOfString != NULL);
|
|
|
|
if (lpEndOfString != NULL) {
|
|
|
|
int lengthToTest = (int) (lpEndOfString - lpStartOfString);
|
|
|
|
//
|
|
// BUGBUG - internationalization?
|
|
//
|
|
|
|
if (strnistr(lpStartOfString, "not found", lengthToTest)
|
|
|| strnistr(lpStartOfString, "cannot find", lengthToTest)) {
|
|
error = ERROR_NO_MORE_FILES;
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
error = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
INET_ASSERT(lpBuffer != NULL);
|
|
INET_ASSERT((int)bytesReceived > 0);
|
|
|
|
error = ParseDirList(lpBuffer,
|
|
bytesReceived,
|
|
isWild ? (LPSTR)lpszFilePart : NULL,
|
|
&lpSessionInfo->FindFileList
|
|
);
|
|
|
|
//
|
|
// ParseDirList() may have failed
|
|
//
|
|
|
|
if (error != ERROR_SUCCESS) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// if there's nothing in the list then no files matching the caller's
|
|
// specification were found
|
|
//
|
|
|
|
if (IsListEmpty(&lpSessionInfo->FindFileList)) {
|
|
error = ERROR_NO_MORE_FILES;
|
|
} else {
|
|
|
|
//
|
|
// if the caller supplied an output buffer then return the first entry
|
|
// and remove it from the list
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(lpFindFileData)) {
|
|
|
|
PLIST_ENTRY pEntry;
|
|
|
|
pEntry = RemoveHeadList(&lpSessionInfo->FindFileList);
|
|
CopyMemory(lpFindFileData,
|
|
(LPWIN32_FIND_DATA)(pEntry + 1),
|
|
sizeof(*lpFindFileData)
|
|
);
|
|
FREE_MEMORY(pEntry);
|
|
}
|
|
|
|
//
|
|
// FTP can only have one active operation per session, so we just return
|
|
// this session handle as the find handle
|
|
//
|
|
|
|
*lphInternet = hFtpSession;
|
|
error = ERROR_SUCCESS;
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if (lpSessionInfo->socketData->IsValid()) {
|
|
lpSessionInfo->socketData->SetLinger(TRUE, 0);
|
|
lpSessionInfo->socketData->Close();
|
|
}
|
|
|
|
if (lpBuffer != NULL) {
|
|
(void)FREE_MEMORY((HLOCAL)lpBuffer);
|
|
}
|
|
|
|
//
|
|
// if we failed then reset the active find flag. We set it, so we know it
|
|
// is safe to reset without acquiring the session lock
|
|
//
|
|
|
|
if (error != ERROR_SUCCESS) {
|
|
lpSessionInfo->Flags &= ~FFTP_FIND_ACTIVE;
|
|
}
|
|
|
|
deref_exit:
|
|
|
|
DereferenceFtpSession(lpSessionInfo);
|
|
|
|
quit:
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
wFtpDeleteFile(
|
|
IN HINTERNET hFtpSession,
|
|
IN LPCSTR lpszFileName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deletes a file at an FTP server
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - identifies the FTP server
|
|
|
|
lpszFileName - name of file to delete
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure -
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpDeleteFile",
|
|
"%#x, %q",
|
|
hFtpSession,
|
|
lpszFileName
|
|
));
|
|
|
|
LPFTP_SESSION_INFO lpSessionInfo;
|
|
DWORD error;
|
|
|
|
if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
|
|
|
|
FTP_RESPONSE_CODE rcResponse;
|
|
|
|
error = Command(lpSessionInfo,
|
|
FALSE,
|
|
FTP_TRANSFER_TYPE_UNKNOWN,
|
|
&rcResponse,
|
|
"DELE %s",
|
|
lpszFileName
|
|
);
|
|
|
|
if ((error == ERROR_SUCCESS)
|
|
&& (rcResponse.Major != FTP_RESPONSE_COMPLETE)) {
|
|
error = ERROR_INTERNET_EXTENDED_ERROR;
|
|
}
|
|
DereferenceFtpSession(lpSessionInfo);
|
|
} else {
|
|
error = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
wFtpRenameFile(
|
|
IN HINTERNET hFtpSession,
|
|
IN LPCSTR lpszExisting,
|
|
IN LPCSTR lpszNew
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Renames a file at an FTP server
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - identifies FTP server
|
|
|
|
lpszExisting - current file name
|
|
|
|
lpszNew - new file name
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure -
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpRenameFile",
|
|
"%#x, %q, %q",
|
|
hFtpSession,
|
|
lpszExisting,
|
|
lpszNew
|
|
));
|
|
|
|
LPFTP_SESSION_INFO lpSessionInfo;
|
|
DWORD error;
|
|
|
|
if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
|
|
|
|
FTP_RESPONSE_CODE rcResponse;
|
|
|
|
error = Command(lpSessionInfo,
|
|
FALSE,
|
|
FTP_TRANSFER_TYPE_UNKNOWN,
|
|
&rcResponse,
|
|
"RNFR %s",
|
|
lpszExisting
|
|
);
|
|
|
|
if ((error == ERROR_SUCCESS)
|
|
&& (rcResponse.Major != FTP_RESPONSE_CONTINUE)) {
|
|
error = ERROR_INTERNET_EXTENDED_ERROR;
|
|
}
|
|
if (error == ERROR_SUCCESS) {
|
|
error = Command(lpSessionInfo,
|
|
FALSE,
|
|
FTP_TRANSFER_TYPE_UNKNOWN,
|
|
&rcResponse,
|
|
"RNTO %s",
|
|
lpszNew
|
|
);
|
|
if ((error == ERROR_SUCCESS)
|
|
&& (rcResponse.Major != FTP_RESPONSE_COMPLETE)) {
|
|
error = ERROR_INTERNET_EXTENDED_ERROR;
|
|
}
|
|
}
|
|
DereferenceFtpSession(lpSessionInfo);
|
|
} else {
|
|
error = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
wFtpOpenFile(
|
|
IN HINTERNET hFtpSession,
|
|
IN LPCSTR lpszFileName,
|
|
IN DWORD dwAccess,
|
|
IN DWORD dwFlags,
|
|
OUT LPHINTERNET lphInternet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initiates the connection to read or write a file at the FTP server
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - identifies FTP server
|
|
|
|
lpszFileName - name of file to open
|
|
|
|
dwAccess - access mode - GENERIC_READ or GENERIC_WRITE
|
|
|
|
dwFlags - flags controlling how to transfer the data
|
|
|
|
lphInternet - where to return the open file handle
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure -
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpOpenFile",
|
|
"%#x, %q, %#x, %#x, %#x",
|
|
hFtpSession,
|
|
lpszFileName,
|
|
dwAccess,
|
|
dwFlags,
|
|
lphInternet
|
|
));
|
|
|
|
LPFTP_SESSION_INFO lpSessionInfo;
|
|
DWORD error;
|
|
|
|
if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
|
|
|
|
//
|
|
// control session must be established
|
|
//
|
|
|
|
if (! lpSessionInfo->socketControl->IsValid()) {
|
|
error = ERROR_FTP_DROPPED;
|
|
} else if ((lpSessionInfo->socketData->IsValid())
|
|
|| (lpSessionInfo->Flags & FFTP_FILE_ACTIVE)) {
|
|
|
|
//
|
|
// there is a (file) transfer in progress if the socket is valid,
|
|
// or we are awaiting a call to InternetCloseHandle() before we can
|
|
// open another file (FFTP_FILE_ACTIVE is set. This stops another
|
|
// thread from closing our socket handle)
|
|
//
|
|
|
|
error = ERROR_FTP_TRANSFER_IN_PROGRESS;
|
|
} else {
|
|
|
|
FTP_RESPONSE_CODE rcResponse;
|
|
|
|
INET_ASSERT(!lpSessionInfo->socketData->IsValid());
|
|
|
|
//
|
|
// Clear the session's "known size bit" before we download the next file,
|
|
// this is to make sure we don't read an extranous size value off it.
|
|
//
|
|
|
|
lpSessionInfo->Flags &= ~(FFTP_KNOWN_FILE_SIZE);
|
|
|
|
//
|
|
// send the connection set-up commands, and issue either the send
|
|
// or the receive command
|
|
//
|
|
// Either "RETR filename" or "STOR filename"
|
|
//
|
|
|
|
error = NegotiateDataConnection(lpSessionInfo,
|
|
dwFlags,
|
|
&rcResponse,
|
|
(dwAccess & GENERIC_READ)
|
|
? "RETR %s"
|
|
: "STOR %s",
|
|
lpszFileName
|
|
);
|
|
|
|
if (error == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// Check response for failure
|
|
//
|
|
|
|
if ((rcResponse.Major != FTP_RESPONSE_PRELIMINARY)
|
|
&& (rcResponse.Major != FTP_RESPONSE_COMPLETE)) {
|
|
|
|
ICSocket * socketData;
|
|
|
|
//
|
|
// BUGBUG - RLF - don't know if this is what's intended
|
|
// here, but the code just used to check
|
|
// socketData != INVALID_SOCKET. Since socketData
|
|
// was getting set to INVALID_SOCKET at the top
|
|
// of this routine, this branch would never be
|
|
// taken
|
|
//
|
|
|
|
socketData = lpSessionInfo->socketData;
|
|
if (socketData->IsValid()) {
|
|
ResetSocket(socketData);
|
|
}
|
|
error = ERROR_INTERNET_EXTENDED_ERROR;
|
|
} else {
|
|
|
|
lpSessionInfo->dwTransferAccess = dwAccess;
|
|
|
|
//
|
|
// Some FTP servers will send us back both the preliminary
|
|
// response and the complete response so quickly that we
|
|
// will never see the preliminary.
|
|
//
|
|
// In order for FtpCloseFile() to know that the completion
|
|
// response has been received, we store the response
|
|
// structure in the Session Info.
|
|
//
|
|
// The response structure only needs to be stored between
|
|
// API calls in this situation, it is not generally
|
|
// referred to.
|
|
//
|
|
|
|
SetSessionLastResponseCode(lpSessionInfo, &rcResponse);
|
|
|
|
//
|
|
// set the abort flag if the file was opened for read - this
|
|
// lets the server know it can clean up the session if we
|
|
// close early
|
|
//
|
|
|
|
if (dwAccess & GENERIC_READ) {
|
|
lpSessionInfo->Flags |= FFTP_ABORT_TRANSFER;
|
|
}
|
|
|
|
//
|
|
// FTP can only have one active operation per session, so
|
|
// we just return this session handle as the find handle
|
|
//
|
|
|
|
*lphInternet = hFtpSession;
|
|
|
|
//
|
|
// this session has an active file operation
|
|
//
|
|
|
|
lpSessionInfo->Flags |= FFTP_FILE_ACTIVE;
|
|
|
|
//
|
|
// N.B. error == ERROR_SUCCESS from above test after call
|
|
// to NegotiateDataConnection
|
|
//
|
|
|
|
INET_ASSERT(error == ERROR_SUCCESS);
|
|
}
|
|
}
|
|
}
|
|
DereferenceFtpSession(lpSessionInfo);
|
|
} else {
|
|
error = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
wFtpCreateDirectory(
|
|
IN HINTERNET hFtpSession,
|
|
IN LPCSTR lpszDirectory
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a directory at the FTP server
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - identifies the FTP server
|
|
|
|
lpszDirectory - directory to create
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure -
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpCreateDirectory",
|
|
"%#x, %q",
|
|
hFtpSession,
|
|
lpszDirectory
|
|
));
|
|
|
|
LPFTP_SESSION_INFO lpSessionInfo;
|
|
DWORD error;
|
|
|
|
if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
|
|
|
|
FTP_RESPONSE_CODE rcResponse;
|
|
|
|
error = Command(lpSessionInfo,
|
|
FALSE,
|
|
FTP_TRANSFER_TYPE_UNKNOWN,
|
|
&rcResponse,
|
|
"MKD %s",
|
|
lpszDirectory
|
|
);
|
|
if ((error == ERROR_SUCCESS)
|
|
&& (rcResponse.Major != FTP_RESPONSE_COMPLETE)) {
|
|
error = ERROR_INTERNET_EXTENDED_ERROR;
|
|
}
|
|
DereferenceFtpSession(lpSessionInfo);
|
|
} else {
|
|
error = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
wFtpRemoveDirectory(
|
|
IN HINTERNET hFtpSession,
|
|
IN LPCSTR lpszDirectory
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes the named directory at the FTP server
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - identifies the FTP server
|
|
|
|
lpszDirectory - directory to remove
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure -
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpRemoveDirectory",
|
|
"%#x, %q",
|
|
hFtpSession,
|
|
lpszDirectory
|
|
));
|
|
|
|
LPFTP_SESSION_INFO lpSessionInfo;
|
|
DWORD error;
|
|
|
|
if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
|
|
|
|
FTP_RESPONSE_CODE rcResponse;
|
|
|
|
error = Command(lpSessionInfo,
|
|
FALSE,
|
|
FTP_TRANSFER_TYPE_UNKNOWN,
|
|
&rcResponse,
|
|
"RMD %s",
|
|
lpszDirectory
|
|
);
|
|
if ((error == ERROR_SUCCESS)
|
|
&& (rcResponse.Major != FTP_RESPONSE_COMPLETE)) {
|
|
error = ERROR_INTERNET_EXTENDED_ERROR;
|
|
}
|
|
DereferenceFtpSession(lpSessionInfo);
|
|
} else {
|
|
error = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
wFtpSetCurrentDirectory(
|
|
IN HINTERNET hFtpSession,
|
|
IN LPCSTR lpszDirectory
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the current directory for this FTP server session
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - identifies the FTP server/session
|
|
|
|
lpszDirectory - name of directory to set
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure -
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpSetCurrentDirectory",
|
|
"%#x, %q",
|
|
hFtpSession,
|
|
lpszDirectory
|
|
));
|
|
|
|
LPFTP_SESSION_INFO lpSessionInfo;
|
|
DWORD error;
|
|
|
|
if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
|
|
|
|
FTP_RESPONSE_CODE rcResponse;
|
|
|
|
error = Command(lpSessionInfo,
|
|
FALSE,
|
|
FTP_TRANSFER_TYPE_UNKNOWN,
|
|
&rcResponse,
|
|
"CWD %s",
|
|
lpszDirectory
|
|
);
|
|
if ((error == ERROR_SUCCESS)
|
|
&& (rcResponse.Major != FTP_RESPONSE_COMPLETE)) {
|
|
error = ERROR_INTERNET_EXTENDED_ERROR;
|
|
}
|
|
DereferenceFtpSession(lpSessionInfo);
|
|
} else {
|
|
error = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
wFtpGetCurrentDirectory(
|
|
IN HINTERNET hFtpSession,
|
|
IN DWORD cchCurrentDirectory,
|
|
OUT LPSTR lpszCurrentDirectory,
|
|
OUT LPDWORD lpdwBytesReturned
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Gets the current working directory at the FTP server for this session
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - identifies FTP server
|
|
|
|
cchCurrentDirectory - number of characters in lpszCurrentDirectory
|
|
|
|
lpszCurrentDirectory - buffer where current directory string is written
|
|
|
|
lpdwBytesReturned - number of characters in output string NOT including
|
|
terminating NUL
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INVALID_HANDLE
|
|
|
|
ERROR_INSUFFICIENT_BUFFER
|
|
The buffer in lpszCurrentDirectory is not large enough to
|
|
hold the directory string. *lpdwBytesReturned will have
|
|
the required size
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpGetCurrentDirectory",
|
|
"%#x, %d, %#x, %#x",
|
|
hFtpSession,
|
|
cchCurrentDirectory,
|
|
lpszCurrentDirectory,
|
|
lpdwBytesReturned
|
|
));
|
|
|
|
LPFTP_SESSION_INFO lpSessionInfo;
|
|
DWORD cchCopied;
|
|
DWORD error;
|
|
|
|
if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
|
|
|
|
FTP_RESPONSE_CODE rcResponse;
|
|
|
|
error = Command(lpSessionInfo,
|
|
FALSE,
|
|
FTP_TRANSFER_TYPE_UNKNOWN,
|
|
&rcResponse,
|
|
"PWD"
|
|
);
|
|
if ((error == ERROR_SUCCESS)
|
|
&& (rcResponse.Major != FTP_RESPONSE_COMPLETE)) {
|
|
error = ERROR_INTERNET_EXTENDED_ERROR;
|
|
}
|
|
if (error == ERROR_SUCCESS) {
|
|
|
|
LPSTR pchResponse;
|
|
|
|
//
|
|
// parse the returned directory name out of the response text
|
|
//
|
|
|
|
pchResponse = InternetLockErrorText();
|
|
if (pchResponse != NULL) {
|
|
pchResponse = strstr(pchResponse, "257 ");
|
|
if (pchResponse != NULL) {
|
|
pchResponse = strchr(pchResponse, '\"');
|
|
if (pchResponse != NULL) {
|
|
|
|
int idx;
|
|
|
|
++pchResponse;
|
|
for (idx = 0, cchCopied = 0; pchResponse[idx] != '\0'; idx++) {
|
|
if (pchResponse[idx] == '\"') {
|
|
if (pchResponse[idx + 1] == '\"') {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (cchCopied < cchCurrentDirectory) {
|
|
lpszCurrentDirectory[cchCopied] = pchResponse[idx];
|
|
}
|
|
cchCopied++;
|
|
}
|
|
if (cchCopied < cchCurrentDirectory) {
|
|
lpszCurrentDirectory[cchCopied] = '\0';
|
|
error = ERROR_SUCCESS;
|
|
} else {
|
|
error = ERROR_INSUFFICIENT_BUFFER;
|
|
++cchCopied;
|
|
}
|
|
} else {
|
|
error = ERROR_INTERNET_EXTENDED_ERROR;
|
|
}
|
|
}
|
|
//InternetUnlockErrorText();
|
|
}
|
|
}
|
|
DereferenceFtpSession(lpSessionInfo);
|
|
} else {
|
|
error = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
if ((error == ERROR_SUCCESS) || (error == ERROR_INSUFFICIENT_BUFFER)) {
|
|
*lpdwBytesReturned = cchCopied;
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
wFtpCommand(
|
|
IN HINTERNET hFtpSession,
|
|
IN BOOL fExpectResponse,
|
|
IN DWORD dwFlags,
|
|
IN LPCSTR lpszCommand
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Runs arbitrary command at an FTP server. Direct connect over Internet
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - identifies the FTP server
|
|
|
|
fExpectResponse - TRUE if we expect a response from the server
|
|
|
|
dwFlags - type of response - ASCII text or BINARY data
|
|
|
|
lpszCommand - pointer to string describing command to run
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure -
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpCommand",
|
|
"%#x, %#x, %#x, %q",
|
|
hFtpSession,
|
|
fExpectResponse,
|
|
dwFlags,
|
|
lpszCommand
|
|
));
|
|
|
|
LPFTP_SESSION_INFO lpSessionInfo;
|
|
DWORD error;
|
|
|
|
//
|
|
// Look up the given handle.
|
|
//
|
|
|
|
if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
|
|
|
|
FTP_RESPONSE_CODE rcResponse;
|
|
|
|
//
|
|
// Issue the command.
|
|
//
|
|
|
|
error = Command(lpSessionInfo,
|
|
fExpectResponse,
|
|
dwFlags,
|
|
&rcResponse,
|
|
lpszCommand
|
|
);
|
|
if (fExpectResponse && (error == ERROR_SUCCESS)) {
|
|
|
|
INET_ASSERT(lpSessionInfo->socketData->IsValid());
|
|
|
|
lpSessionInfo->dwTransferAccess |= (GENERIC_READ|GENERIC_WRITE);
|
|
|
|
}
|
|
#if DBG
|
|
else {
|
|
|
|
INET_ASSERT(! lpSessionInfo->socketData->IsValid());
|
|
|
|
}
|
|
|
|
if (error == ERROR_SUCCESS) {
|
|
|
|
INET_ASSERT(lpSessionInfo->socketControl->IsValid());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
DereferenceFtpSession(lpSessionInfo);
|
|
} else {
|
|
error = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
//
|
|
// Internet subordinate functions
|
|
//
|
|
|
|
DWORD
|
|
wFtpFindNextFile(
|
|
IN HINTERNET hFtpSession,
|
|
OUT LPWIN32_FIND_DATA lpFindFileData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the next file found from a call to FtpFindFirstFile().
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - Handle to an FTP session, as returned from FtpConnect()
|
|
|
|
lpFindFileData - Pointer to a buffer that will contain WIN32_FIND_DATA
|
|
information when this call succeeds.
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_NO_MORE_FILES
|
|
The end of the file list has been reached.
|
|
|
|
ERROR_INVALID_HANDLE
|
|
Can't find session that knows about hFind
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpFindNextFile",
|
|
"%#x, %#x",
|
|
hFtpSession,
|
|
lpFindFileData
|
|
));
|
|
|
|
LPFTP_SESSION_INFO lpSessionInfo;
|
|
DWORD error;
|
|
|
|
if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
|
|
|
|
//
|
|
// ISSUE this code is cut & paste from find first - they should both call a
|
|
// fn instead
|
|
//
|
|
|
|
if (!IsListEmpty(&lpSessionInfo->FindFileList)) {
|
|
|
|
PLIST_ENTRY pEntry;
|
|
|
|
//
|
|
// Enumerate the first entry and advance pointers
|
|
//
|
|
|
|
pEntry = RemoveHeadList(&lpSessionInfo->FindFileList);
|
|
|
|
INET_ASSERT(pEntry != NULL);
|
|
|
|
CopyMemory(lpFindFileData,
|
|
(LPWIN32_FIND_DATA)(pEntry + 1),
|
|
sizeof(WIN32_FIND_DATA)
|
|
);
|
|
FREE_MEMORY(pEntry);
|
|
error = ERROR_SUCCESS;
|
|
} else {
|
|
error = ERROR_NO_MORE_FILES;
|
|
}
|
|
DereferenceFtpSession(lpSessionInfo);
|
|
} else {
|
|
error = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
wFtpFindClose(
|
|
IN HINTERNET hFtpSession
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees the WIN32_FIND_DATA structures in the directory list for this session
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - handle of an FTP session, created by InternetConnect
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INVALID_HANDLE
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpFindClose",
|
|
"%#x",
|
|
hFtpSession
|
|
));
|
|
|
|
LPFTP_SESSION_INFO lpSessionInfo;
|
|
DWORD error;
|
|
|
|
if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
|
|
ClearFindList(&lpSessionInfo->FindFileList);
|
|
|
|
//
|
|
// this session no longer has an active directory listing
|
|
//
|
|
|
|
lpSessionInfo->Flags &= ~FFTP_FIND_ACTIVE;
|
|
DereferenceFtpSession(lpSessionInfo);
|
|
error = ERROR_SUCCESS;
|
|
} else {
|
|
error = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
wFtpConnect(
|
|
IN LPCSTR lpszServerName,
|
|
IN INTERNET_PORT nServerPort,
|
|
IN LPCSTR lpszUsername,
|
|
IN LPCSTR lpszPassword,
|
|
IN DWORD dwService,
|
|
IN DWORD dwFlags,
|
|
OUT LPHINTERNET lphInternet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a new FTP session object
|
|
|
|
Arguments:
|
|
|
|
lpszServerName - pointer to string identifying FTP server
|
|
|
|
nServerPort - port number to connect to
|
|
|
|
lpszUsername - pointer to string identifying user name to log on as
|
|
|
|
lpszPassword - pointer to string identifying password to use with user name
|
|
|
|
dwService - service type parameter (unused)
|
|
|
|
dwFlags - session flags. Currently only INTERNET_FLAG_PASSIVE
|
|
is defined
|
|
|
|
lphInternet - returned handle of created FTP session
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_NOT_ENOUGH_MEMORY
|
|
Ran out of memory while creating the session object
|
|
|
|
ERROR_INTERNET_OUT_OF_HANDLES
|
|
Ran out of handles while creating the session object
|
|
|
|
ERROR_INTERNET_SHUTDOWN
|
|
The DLL is being unloaded
|
|
|
|
--*/
|
|
|
|
{
|
|
INET_ASSERT(lpszUsername != NULL);
|
|
INET_ASSERT(lpszPassword != NULL);
|
|
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpConnect",
|
|
"%q, %d, %q, %q, %d, %#x, %#x",
|
|
lpszServerName,
|
|
nServerPort,
|
|
lpszUsername,
|
|
lpszPassword,
|
|
dwService,
|
|
dwFlags,
|
|
lphInternet
|
|
));
|
|
|
|
DWORD error;
|
|
LPFTP_SESSION_INFO sessionInfo;
|
|
|
|
UNREFERENCED_PARAMETER(lpszUsername);
|
|
UNREFERENCED_PARAMETER(lpszPassword);
|
|
UNREFERENCED_PARAMETER(dwService);
|
|
|
|
//
|
|
// create a new FTP session object
|
|
//
|
|
|
|
error = CreateFtpSession((LPSTR)lpszServerName,
|
|
nServerPort,
|
|
|
|
//
|
|
// if INTERNET_FLAG_PASSIVE then create a passive
|
|
// session object
|
|
//
|
|
|
|
(dwFlags & INTERNET_FLAG_PASSIVE)
|
|
? FFTP_PASSIVE_MODE
|
|
: 0,
|
|
&sessionInfo
|
|
);
|
|
if (error == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// return the FTP_SESSION_INFO handle
|
|
//
|
|
|
|
*lphInternet = sessionInfo->Handle;
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
wFtpMakeConnection(
|
|
IN HINTERNET hFtpSession,
|
|
IN LPCSTR lpszUsername,
|
|
IN LPCSTR lpszPassword
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Connect with and log into an FTP server.
|
|
|
|
This function is cancellable
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - handle of an FTP session, created by InternetConnect
|
|
|
|
pszUsername - pointer to string identifying user name to log on as
|
|
|
|
pszPassword - pointer to string identifying password to use with user name
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INTERNET_INCORRECT_USER_NAME
|
|
The server didn't like the user name
|
|
|
|
ERROR_INTERNET_INCORRECT_PASSWORD
|
|
The server didn't like the password
|
|
|
|
ERROR_INTERNET_LOGIN_FAILURE
|
|
The server rejected the login request
|
|
|
|
ERROR_FTP_DROPPED
|
|
The connection has been closed
|
|
|
|
ERROR_FTP_TRANSFER_IN_PROGRESS
|
|
There is already a transfer in progress on this connection
|
|
|
|
ERROR_INTERNET_NAME_NOT_RESOLVED
|
|
Couldn't resolve the server name
|
|
|
|
WSA error
|
|
Couldn't connect to the server, or problems while
|
|
communicating with it
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpMakeConnection",
|
|
"%#x, %q, %q",
|
|
hFtpSession,
|
|
lpszUsername,
|
|
lpszPassword
|
|
));
|
|
|
|
LPFTP_SESSION_INFO sessionInfo;
|
|
DWORD error;
|
|
|
|
if (FindFtpSession(hFtpSession, &sessionInfo)) {
|
|
|
|
//
|
|
// resolve the FTP server's host name and connect to the server
|
|
//
|
|
|
|
error = FtpOpenServer(sessionInfo);
|
|
if (error == ERROR_SUCCESS) {
|
|
|
|
FTP_RESPONSE_CODE rcResponse;
|
|
|
|
//
|
|
// set send and receive timeouts on the control channel socket.
|
|
// Ignore any errors
|
|
//
|
|
|
|
sessionInfo->socketControl->SetTimeout(
|
|
SEND_TIMEOUT,
|
|
GetTimeoutValue(INTERNET_OPTION_CONTROL_SEND_TIMEOUT)
|
|
);
|
|
|
|
sessionInfo->socketControl->SetTimeout(
|
|
RECEIVE_TIMEOUT,
|
|
GetTimeoutValue(INTERNET_OPTION_CONTROL_RECEIVE_TIMEOUT)
|
|
);
|
|
|
|
//
|
|
// check greeting and store in per-thread response text buffer
|
|
//
|
|
|
|
error = GetReply(sessionInfo, &rcResponse);
|
|
if (error == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// check that the server sent us an affirmative response
|
|
//
|
|
|
|
if (rcResponse.Major == FTP_RESPONSE_COMPLETE) {
|
|
|
|
//
|
|
// send the user name
|
|
//
|
|
|
|
error = Command(sessionInfo,
|
|
FALSE,
|
|
FTP_TRANSFER_TYPE_UNKNOWN,
|
|
&rcResponse,
|
|
"USER %s",
|
|
lpszUsername
|
|
);
|
|
|
|
//
|
|
// BUGBUG - is it possible to get success from Command(),
|
|
// but an error from the server - e.g. 332, need
|
|
// account for login?
|
|
//
|
|
|
|
if (error == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// send the password if required
|
|
//
|
|
|
|
if (rcResponse.Major == FTP_RESPONSE_CONTINUE) {
|
|
error = Command(sessionInfo,
|
|
FALSE,
|
|
FTP_TRANSFER_TYPE_UNKNOWN,
|
|
&rcResponse,
|
|
"PASS %s",
|
|
lpszPassword
|
|
);
|
|
|
|
//
|
|
// if we failed to send the password, or the password
|
|
// was rejected, or we are attempting to log on as
|
|
// "anonymous" and it turns out that the server does
|
|
// not allow anonymous logon, then return a password
|
|
// error. The caller can still check the response
|
|
// from the server
|
|
//
|
|
|
|
if (((error == ERROR_SUCCESS)
|
|
&& (rcResponse.Major != FTP_RESPONSE_COMPLETE))
|
|
|| (error == ERROR_INTERNET_EXTENDED_ERROR)) {
|
|
if (stricmp(lpszUsername, "anonymous") == 0) {
|
|
error = ERROR_INTERNET_LOGIN_FAILURE;
|
|
} else {
|
|
error = ERROR_INTERNET_INCORRECT_PASSWORD;
|
|
}
|
|
}
|
|
} else if (rcResponse.Major != FTP_RESPONSE_COMPLETE) {
|
|
error = ERROR_INTERNET_INCORRECT_USER_NAME;
|
|
}
|
|
|
|
//
|
|
// get the server type
|
|
//
|
|
|
|
//if (error == ERROR_SUCCESS) {
|
|
// error = wFtpFindServerType(hFtpSession);
|
|
//}
|
|
}
|
|
} else {
|
|
error = ERROR_INTERNET_LOGIN_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// success or fail: unlock the session object
|
|
//
|
|
|
|
DereferenceFtpSession(sessionInfo);
|
|
|
|
//
|
|
// if we failed to login then let wFtpDisconnect() clean up - it will
|
|
// also send a "QUIT" to the server (if we have a control connection)
|
|
// which will ensure a clean exit
|
|
//
|
|
|
|
if (error != ERROR_SUCCESS) {
|
|
|
|
//
|
|
// if we experience an error during disconnect, we will just ignore
|
|
// it and return the error generated during our failed login attempt
|
|
//
|
|
|
|
(void)wFtpDisconnect(hFtpSession, CF_EXPEDITED_CLOSE);
|
|
}
|
|
} else {
|
|
error = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
wFtpDisconnect(
|
|
IN HINTERNET hFtpSession,
|
|
IN DWORD dwFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes the connection, issues the quit command, etc.,
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - FTP session created by wFtpConnect
|
|
|
|
dwFlags - controlling operation. Can be:
|
|
|
|
CF_EXPEDITED_CLOSE - Don't send QUIT to the server, just
|
|
close the control connection
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INVALID_HANDLE
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpDisconnect",
|
|
"%#x, %#x",
|
|
hFtpSession,
|
|
dwFlags
|
|
));
|
|
|
|
LPFTP_SESSION_INFO lpSessionInfo;
|
|
DWORD error;
|
|
LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
|
|
|
|
if (lpThreadInfo == NULL) {
|
|
|
|
INET_ASSERT(FALSE);
|
|
|
|
error = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto quit;
|
|
}
|
|
|
|
if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
|
|
|
|
ICSocket * socketControl;
|
|
ICSocket * socketData;
|
|
|
|
socketControl = lpSessionInfo->socketControl;
|
|
socketData = lpSessionInfo->socketData;
|
|
|
|
//
|
|
// kill any active data transfer
|
|
//
|
|
|
|
if (socketData->IsValid()) {
|
|
|
|
//
|
|
// set the non-blocking state depending on whether we are called in
|
|
// an app thread context, or in the async scheduler thread context
|
|
//
|
|
|
|
//socketData->SetNonBlockingMode(lpThreadInfo->IsAsyncWorkerThread);
|
|
|
|
if (dwFlags & CF_EXPEDITED_CLOSE) {
|
|
error = socketData->Close();
|
|
} else {
|
|
error = wFtpCloseFile(hFtpSession);
|
|
if (error != ERROR_SUCCESS) {
|
|
|
|
DEBUG_PRINT(WORKER,
|
|
ERROR,
|
|
("wFtpCloseFile() returns %d\n",
|
|
error
|
|
));
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
INET_ASSERT(!lpSessionInfo->socketData->IsValid());
|
|
|
|
//
|
|
// perform graceful close to the server if we have a control connection
|
|
//
|
|
|
|
if (socketControl->IsValid()) {
|
|
|
|
//
|
|
// set the non-blocking state depending on whether we are called in
|
|
// an app thread context, or in the async scheduler thread context
|
|
//
|
|
|
|
//socketControl->SetNonBlockingMode(lpThreadInfo->IsAsyncWorkerThread);
|
|
|
|
if (!(dwFlags & CF_EXPEDITED_CLOSE)) {
|
|
|
|
FTP_RESPONSE_CODE rcResponse;
|
|
|
|
Command(lpSessionInfo,
|
|
FALSE,
|
|
FTP_TRANSFER_TYPE_UNKNOWN,
|
|
&rcResponse,
|
|
"QUIT"
|
|
);
|
|
}
|
|
lpSessionInfo->socketControl->Disconnect(SF_INDICATE);
|
|
}
|
|
|
|
//
|
|
// finally kill the FTP_SESSION_INFO structure
|
|
//
|
|
|
|
TerminateFtpSession(lpSessionInfo);
|
|
error = ERROR_SUCCESS;
|
|
} else {
|
|
error = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
quit:
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
wFtpReadFile(
|
|
IN HINTERNET hFtpSession,
|
|
IN LPVOID lpBuffer,
|
|
IN DWORD dwNumberOfBytesToRead,
|
|
OUT LPDWORD lpdwNumberOfBytesRead
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads data from the FTP server. We use the data channel
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - handle identifying FTP session
|
|
|
|
lpBuffer - pointer to buffer for received data
|
|
|
|
dwNumberOfBytesToRead - size of lpBuffer in bytes
|
|
|
|
lpdwNumberOfBytesRead - returned number of bytes received
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INVALID_HANDLE
|
|
Couldn't find hFtpSession
|
|
|
|
ERROR_ACCESS_DENIED
|
|
This session doesn't have read access (?)
|
|
|
|
ERROR_FTP_DROPPED
|
|
The data channel has been closed
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpReadFile",
|
|
"%#x, %#x, %d, %#x",
|
|
hFtpSession,
|
|
lpBuffer,
|
|
dwNumberOfBytesToRead,
|
|
lpdwNumberOfBytesRead
|
|
));
|
|
|
|
LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
|
|
DWORD error;
|
|
|
|
if (lpThreadInfo == NULL) {
|
|
|
|
INET_ASSERT(FALSE);
|
|
|
|
error = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto quit;
|
|
}
|
|
|
|
LPFTP_SESSION_INFO lpSessionInfo;
|
|
ICSocket * socketData;
|
|
BOOL eof;
|
|
DWORD bytesReceived;
|
|
|
|
//
|
|
// initialize variables in case we quit early (i.e. via goto)
|
|
//
|
|
|
|
bytesReceived = 0;
|
|
|
|
//
|
|
// find the FTP_SESSION_INFO and ensure it is set up to receive data
|
|
//
|
|
|
|
if (!FindFtpSession(hFtpSession, &lpSessionInfo)) {
|
|
error = ERROR_INVALID_HANDLE;
|
|
goto quit;
|
|
}
|
|
|
|
//
|
|
// if FFTP_EOF is set then we already reached the end the file
|
|
//
|
|
|
|
if (lpSessionInfo->Flags & FFTP_EOF) {
|
|
error = ERROR_SUCCESS;
|
|
goto unlock_and_quit;
|
|
}
|
|
|
|
//
|
|
// get the data socket. If it has become INVALID_SOCKET then the server
|
|
// closed the connection
|
|
//
|
|
|
|
socketData = lpSessionInfo->socketData;
|
|
if (!socketData->IsValid()) {
|
|
error = ERROR_FTP_DROPPED;
|
|
goto unlock_and_quit;
|
|
}
|
|
|
|
if (!(lpSessionInfo->dwTransferAccess & GENERIC_READ)) {
|
|
error = ERROR_ACCESS_DENIED;
|
|
goto unlock_and_quit;
|
|
}
|
|
|
|
//
|
|
// read until we fill the users buffer, get an error, or get to EOF
|
|
//
|
|
|
|
DWORD bufferRemaining;
|
|
|
|
bufferRemaining = dwNumberOfBytesToRead;
|
|
error = socketData->Receive(
|
|
&lpBuffer,
|
|
&dwNumberOfBytesToRead, // lpdwBufferLength
|
|
&bufferRemaining, // lpdwBufferRemaining
|
|
&bytesReceived, // lpdwBytesReceived
|
|
0, // dwExtraSpace
|
|
SF_RECEIVE_ALL
|
|
| SF_INDICATE,
|
|
&eof
|
|
);
|
|
if (error == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// if we got to EOF then the server will have closed the data
|
|
// connection. We need to close the socket at our end. If this is
|
|
// a passive connection then we initiate session termination
|
|
//
|
|
|
|
if (eof) {
|
|
(void)socketData->Close();
|
|
|
|
INET_ASSERT(lpSessionInfo->socketData == socketData);
|
|
|
|
//
|
|
// reset the abort flag - we no longer have to send and ABOR command
|
|
// when we close the handle
|
|
//
|
|
|
|
lpSessionInfo->Flags &= ~FFTP_ABORT_TRANSFER;
|
|
|
|
//
|
|
// set EOF in the FTP_SESSION_INFO flags so we know next time
|
|
// we call this function that the session is not dropped, but
|
|
// that we already reached the end of the data
|
|
//
|
|
|
|
lpSessionInfo->Flags |= FFTP_EOF;
|
|
}
|
|
}
|
|
|
|
//
|
|
// BUGBUG - in error case we should probably close the socket, set
|
|
// INVALID_SOCKET in the FTP_SESSION_INFO, etc.
|
|
//
|
|
|
|
unlock_and_quit:
|
|
|
|
//
|
|
// update the output parameters if we succeeded
|
|
//
|
|
|
|
if (error == ERROR_SUCCESS) {
|
|
*lpdwNumberOfBytesRead = bytesReceived;
|
|
}
|
|
|
|
DereferenceFtpSession(lpSessionInfo);
|
|
|
|
quit:
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
wFtpWriteFile(
|
|
IN HINTERNET hFtpSession,
|
|
IN LPVOID lpBuffer,
|
|
IN DWORD dwNumberOfBytesToWrite,
|
|
OUT LPDWORD lpdwNumberOfBytesWritten
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Writes data to the FTP server. We use the data channel
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - handle identifying FTP session
|
|
|
|
lpBuffer - pointer to buffer containing data to write
|
|
|
|
dwNumberOfBytesToWrite - size of lpBuffer in bytes
|
|
|
|
lpdwNumberOfBytesWritten - returned number of bytes sent
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INVALID_HANDLE
|
|
Couldn't find hFtpSession
|
|
|
|
ERROR_ACCESS_DENIED
|
|
This session doesn't have write access (?)
|
|
|
|
ERROR_FTP_DROPPED
|
|
The data channel has been closed
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpWriteFile",
|
|
"%#x, %#x, %d, %#x",
|
|
hFtpSession,
|
|
lpBuffer,
|
|
dwNumberOfBytesToWrite,
|
|
lpdwNumberOfBytesWritten
|
|
));
|
|
|
|
LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
|
|
DWORD error;
|
|
|
|
if (lpThreadInfo == NULL) {
|
|
|
|
INET_ASSERT(FALSE);
|
|
|
|
error = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto quit;
|
|
}
|
|
|
|
LPFTP_SESSION_INFO lpSessionInfo;
|
|
ICSocket * socketData;
|
|
int nSent;
|
|
|
|
//
|
|
// find the FTP_SESSION_INFO and ensure it is set up to send data
|
|
//
|
|
|
|
if (!FindFtpSession(hFtpSession, &lpSessionInfo)) {
|
|
error = ERROR_INVALID_HANDLE;
|
|
goto quit;
|
|
}
|
|
|
|
socketData = lpSessionInfo->socketData;
|
|
if (! socketData->IsValid()) {
|
|
error = ERROR_FTP_DROPPED;
|
|
goto unlock_and_quit;
|
|
}
|
|
|
|
if (!(lpSessionInfo->dwTransferAccess & GENERIC_WRITE)) {
|
|
error = ERROR_ACCESS_DENIED;
|
|
goto unlock_and_quit;
|
|
}
|
|
|
|
error = socketData->Send(lpBuffer, dwNumberOfBytesToWrite, SF_INDICATE);
|
|
if (error == ERROR_SUCCESS) {
|
|
*lpdwNumberOfBytesWritten = dwNumberOfBytesToWrite;
|
|
} else {
|
|
|
|
//
|
|
// we had a failure. We should check the control socket for any error
|
|
// info from the server
|
|
//
|
|
|
|
FTP_RESPONSE_CODE response = {0};
|
|
response.Major = FTP_RESPONSE_PERMANENT_FAILURE;
|
|
//
|
|
SetSessionLastResponseCode(lpSessionInfo, &response);
|
|
}
|
|
|
|
unlock_and_quit:
|
|
|
|
DereferenceFtpSession(lpSessionInfo);
|
|
|
|
quit:
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
wFtpQueryDataAvailable(
|
|
IN HINTERNET hFtpSession,
|
|
OUT LPDWORD lpdwNumberOfBytesAvailable
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines amount of data available to be received on a data (file) socket
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - identifies FTP session
|
|
|
|
lpdwNumberOfBytesAvailable - returned number of bytes available
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INVALID_HANDLE
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpQueryDataAvailable",
|
|
"%#x, %#x",
|
|
hFtpSession,
|
|
lpdwNumberOfBytesAvailable
|
|
));
|
|
|
|
LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
|
|
DWORD error;
|
|
|
|
HINTERNET_HANDLE_TYPE handleType;
|
|
|
|
error = RGetHandleType(lpThreadInfo->hObjectMapped, &handleType);
|
|
|
|
if (error != ERROR_SUCCESS) {
|
|
return (error);
|
|
}
|
|
|
|
*lpdwNumberOfBytesAvailable = 0;
|
|
|
|
if (lpThreadInfo == NULL) {
|
|
|
|
INET_ASSERT(FALSE);
|
|
|
|
error = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto quit;
|
|
}
|
|
|
|
LPFTP_SESSION_INFO lpSessionInfo;
|
|
|
|
if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
|
|
|
|
//
|
|
// if we are currently performing a directory list then return the size
|
|
// of a dir list entry
|
|
//
|
|
|
|
if (lpSessionInfo->Flags & FFTP_FIND_ACTIVE) {
|
|
*lpdwNumberOfBytesAvailable = !IsListEmpty(&lpSessionInfo->FindFileList)
|
|
? sizeof(WIN32_FIND_DATA) : 0;
|
|
} else {
|
|
|
|
//
|
|
// otherwise, if we are receiving data, find out how much
|
|
//
|
|
|
|
ICSocket * socketData;
|
|
|
|
socketData = lpSessionInfo->socketData;
|
|
if (socketData->IsValid()) {
|
|
error = socketData->DataAvailable(lpdwNumberOfBytesAvailable);
|
|
} else {
|
|
|
|
//
|
|
// there is no data connection
|
|
//
|
|
|
|
*lpdwNumberOfBytesAvailable = 0;
|
|
error = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
DereferenceFtpSession(lpSessionInfo);
|
|
} else {
|
|
error = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
quit:
|
|
|
|
if ((error == ERROR_SUCCESS) && (*lpdwNumberOfBytesAvailable == 0)) {
|
|
|
|
InbLocalEndCacheWrite(lpThreadInfo->hObjectMapped,
|
|
((handleType==TypeFtpFindHandleHtml)
|
|
?"htm":NULL),
|
|
TRUE);
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
wFtpCloseFile(
|
|
IN HINTERNET hFtpSession
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Terminates the connection used for file transfer. The connection may already
|
|
be closed (by the server during a READ, or by the client during a WRITE) in
|
|
which case we just need to receive the confirmation (226) on the control
|
|
socket. If the connection is still open, or the abort flag is set for this
|
|
connection, then this is an abnormal termination, and we need to send an
|
|
ABORt command
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - Identifies the session on which to terminate file transfer
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INVALID_HANDLE
|
|
Couldn't find the FTP_SESSION_INFO corresponding to
|
|
hFtpSession
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpCloseFile",
|
|
"%#x",
|
|
hFtpSession
|
|
));
|
|
|
|
LPFTP_SESSION_INFO lpSessionInfo;
|
|
DWORD error;
|
|
|
|
if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
|
|
|
|
BOOL getResponse;
|
|
ICSocket * socketData;
|
|
FTP_RESPONSE_CODE rcResponse;
|
|
|
|
socketData = lpSessionInfo->socketData;
|
|
if (socketData->IsValid()) {
|
|
|
|
//
|
|
// if we are performing a read/write operation and the transfer
|
|
// isn't complete then abort the connection
|
|
//
|
|
|
|
if (lpSessionInfo->Flags & FFTP_ABORT_TRANSFER) {
|
|
AbortTransfer(lpSessionInfo);
|
|
ResetSocket(lpSessionInfo->socketData);
|
|
} else {
|
|
|
|
//
|
|
// in all other cases - completed READ, complete or incomplete
|
|
// WRITE - just close the socket
|
|
//
|
|
|
|
lpSessionInfo->socketData->Close();
|
|
}
|
|
} else if (lpSessionInfo->Flags & FFTP_ABORT_TRANSFER) {
|
|
|
|
//
|
|
// we have no data socket, but the abort transfer flag is set. We
|
|
// are probably closing a file we opened for read without having
|
|
// read any data. In this case we send an abort anyway
|
|
//
|
|
|
|
AbortTransfer(lpSessionInfo);
|
|
}
|
|
|
|
//
|
|
// get the server response - we expect either 226 to a good transfer,
|
|
// or 426 for an aborted transfer...
|
|
//
|
|
|
|
GetSessionLastResponseCode(lpSessionInfo, &rcResponse);
|
|
if (rcResponse.Major == FTP_RESPONSE_PRELIMINARY) {
|
|
error = GetReply(lpSessionInfo, &rcResponse);
|
|
SetLastError(ERROR_SUCCESS); // a-thkesa;. added from Win CE fix of BUG WinSE: 23985
|
|
if ((error == ERROR_SUCCESS)
|
|
&& (rcResponse.Major != FTP_RESPONSE_COMPLETE)) {
|
|
error = ERROR_INTERNET_EXTENDED_ERROR;
|
|
SetLastError(error); // a-thkesa;. added from Win CE fix of BUG WinSE: 23985
|
|
}
|
|
} else if (rcResponse.Major == FTP_RESPONSE_PERMANENT_FAILURE){
|
|
error = ERROR_SUCCESS;
|
|
} else {
|
|
if(rcResponse.Major != FTP_RESPONSE_COMPLETE)
|
|
error = GetReply(lpSessionInfo, &rcResponse);
|
|
else
|
|
error = ERROR_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// reset the ABORT, FILE_ACTIVE and EOF flags
|
|
//
|
|
|
|
lpSessionInfo->Flags &= ~(FFTP_ABORT_TRANSFER
|
|
| FFTP_EOF
|
|
| FFTP_FILE_ACTIVE
|
|
);
|
|
DereferenceFtpSession(lpSessionInfo);
|
|
} else {
|
|
error = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
wFtpFindServerType(
|
|
IN HINTERNET hFtpSession
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines the type of server we are talking to (NT or Unix)
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - identifies FTP_SESSION_INFO. The structure ServerType field
|
|
will be updated with the discovered info
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INVALID_HANDLE
|
|
Couldn't find the FTP_SESSION_INFO corresponding to
|
|
hFtpSession
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpFindServerType",
|
|
"%#x",
|
|
hFtpSession
|
|
));
|
|
|
|
LPFTP_SESSION_INFO lpSessionInfo;
|
|
DWORD error;
|
|
|
|
if (FindFtpSession(hFtpSession, &lpSessionInfo)) {
|
|
|
|
FTP_RESPONSE_CODE rcResponse;
|
|
|
|
error = Command(lpSessionInfo,
|
|
FALSE,
|
|
FTP_TRANSFER_TYPE_UNKNOWN,
|
|
&rcResponse,
|
|
"SYST"
|
|
);
|
|
if (error == ERROR_SUCCESS) {
|
|
|
|
LPSTR lpszResponse = InternetLockErrorText();
|
|
|
|
if (lpszResponse != NULL) {
|
|
|
|
FTP_SERVER_TYPE serverType = FTP_SERVER_TYPE_UNKNOWN;
|
|
|
|
//
|
|
// "215 " must be first token in response text
|
|
//
|
|
|
|
lpszResponse = strstr(lpszResponse, "215 ");
|
|
if (lpszResponse != NULL) {
|
|
|
|
//
|
|
// check for existence of "Windows_NT" or "Unix" (case
|
|
// insensitive comparison)
|
|
//
|
|
|
|
//
|
|
// BUGBUG - find out from MuraliK/TerryK the values these
|
|
// ids can have
|
|
//
|
|
|
|
static struct {
|
|
LPCSTR lpszSystemName;
|
|
FTP_SERVER_TYPE ServerType;
|
|
} FtpServerTypes[] = {
|
|
"Windows_NT", FTP_SERVER_TYPE_NT,
|
|
"Unix", FTP_SERVER_TYPE_UNIX
|
|
};
|
|
|
|
DWORD textLength = strlen(lpszResponse);
|
|
|
|
for (int i = 0; i < ARRAY_ELEMENTS(FtpServerTypes); ++i) {
|
|
if (strnistr(lpszResponse,
|
|
(LPSTR)FtpServerTypes[i].lpszSystemName,
|
|
textLength
|
|
) != NULL) {
|
|
|
|
serverType = FtpServerTypes[i].ServerType;
|
|
|
|
DEBUG_PRINT(FTP,
|
|
INFO,
|
|
("serverType = %s (%d)\n",
|
|
InternetMapFtpServerType(serverType),
|
|
serverType
|
|
));
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
lpSessionInfo->ServerType = serverType;
|
|
//InternetUnlockErrorText();
|
|
}
|
|
}
|
|
DereferenceFtpSession(lpSessionInfo);
|
|
} else {
|
|
error = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
//
|
|
// We don't use this today, because FtpGetFileSize does not support
|
|
// issuing backround commands through the FTP Control socket while
|
|
// the user is doing an FTP download (with FtpOpenFile)
|
|
//
|
|
|
|
|
|
DWORD
|
|
wFtpGetFileSize(
|
|
IN HINTERNET hMappedFtpSession,
|
|
IN LPFTP_SESSION_INFO lpSessionInfo,
|
|
OUT LPDWORD lpdwFileSizeLow,
|
|
OUT LPDWORD lpdwFileSizeHigh
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Finds size of a file at server
|
|
|
|
Arguments:
|
|
|
|
hFtpSession - identifies mapped FTP handle obj
|
|
|
|
lpSessionInfo - LPFTP_SESSION_INFO structure ptr.
|
|
|
|
lpdwFileSizeLow - pointer to low dword of file size
|
|
|
|
lpdwFileSizeHigh - optional output pointer to high dword of file size
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_INVALID_HANDLE
|
|
Couldn't find the FTP_SESSION_INFO corresponding to
|
|
hFtpSession
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_FTP,
|
|
Dword,
|
|
"wFtpGetFileSize",
|
|
"%#x, %#x, %#x, %#x",
|
|
hMappedFtpSession,
|
|
lpSessionInfo,
|
|
lpdwFileSizeLow,
|
|
lpdwFileSizeHigh
|
|
));
|
|
|
|
|
|
DWORD error = ERROR_INTERNET_INTERNAL_ERROR;
|
|
FTP_FILE_HANDLE_OBJECT * pFileMapped = (FTP_FILE_HANDLE_OBJECT *) hMappedFtpSession;
|
|
|
|
*lpdwFileSizeLow = 0;
|
|
*lpdwFileSizeHigh = 0;
|
|
|
|
if (lpSessionInfo) {
|
|
|
|
FTP_RESPONSE_CODE rcResponse;
|
|
|
|
error = Command(lpSessionInfo,
|
|
FALSE,
|
|
FTP_TRANSFER_TYPE_UNKNOWN,
|
|
&rcResponse,
|
|
"SIZE %s",
|
|
pFileMapped->GetFileName()
|
|
);
|
|
if (error == ERROR_SUCCESS) {
|
|
|
|
LPSTR lpszResponse = InternetLockErrorText();
|
|
|
|
if (lpszResponse != NULL) {
|
|
|
|
FTP_SERVER_TYPE serverType = FTP_SERVER_TYPE_UNKNOWN;
|
|
|
|
//
|
|
// "213 " must be first token in response text of file size
|
|
//
|
|
|
|
lpszResponse = strstr(lpszResponse, "213 ");
|
|
if (lpszResponse != NULL) {
|
|
*lpdwFileSizeLow = atoi(lpszResponse);
|
|
error = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
error = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// private debug functions
|
|
//
|
|
|
|
#if INET_DEBUG
|
|
|
|
PRIVATE
|
|
DEBUG_FUNCTION
|
|
LPSTR
|
|
InternetMapFtpServerType(
|
|
IN FTP_SERVER_TYPE ServerType
|
|
)
|
|
{
|
|
switch (ServerType) {
|
|
CASE_OF(FTP_SERVER_TYPE_UNKNOWN);
|
|
CASE_OF(FTP_SERVER_TYPE_NT);
|
|
CASE_OF(FTP_SERVER_TYPE_UNIX);
|
|
}
|
|
return "?";
|
|
}
|
|
|
|
#endif // INET_DEBUG
|