From 819fa8ce381bec9baaad8adc8e599349499ebdab Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 11 Apr 2000 20:07:00 +0000 Subject: [PATCH] Merged urlmon.dll and wininet.dll from the Corel tree (implementation by Ulrich Czekalla ) --- Make.rules.in | 2 + configure | 4 + configure.in | 2 + dlls/Makefile.in | 10 + dlls/urlmon/.cvsignore | 3 + dlls/urlmon/Makefile.in | 16 + dlls/urlmon/umon.c | 37 + dlls/urlmon/urlmon.spec | 80 ++ dlls/wininet/.cvsignore | 3 + dlls/wininet/Makefile.in | 17 + dlls/wininet/ftp.c | 2491 +++++++++++++++++++++++++++++++++++++++++++++ dlls/wininet/internet.c | 1107 ++++++++++++++++++++ dlls/wininet/internet.h | 196 ++++ dlls/wininet/utility.c | 127 +++ dlls/wininet/wininet.spec | 165 +++ include/debugdefs.h | 4 +- include/urlmon.h | 11 + include/wininet.h | 1403 +++++++++++++++++++++++++ 18 files changed, 5677 insertions(+), 1 deletion(-) create mode 100644 dlls/urlmon/.cvsignore create mode 100644 dlls/urlmon/Makefile.in create mode 100644 dlls/urlmon/umon.c create mode 100644 dlls/urlmon/urlmon.spec create mode 100644 dlls/wininet/.cvsignore create mode 100644 dlls/wininet/Makefile.in create mode 100644 dlls/wininet/ftp.c create mode 100644 dlls/wininet/internet.c create mode 100644 dlls/wininet/internet.h create mode 100644 dlls/wininet/utility.c create mode 100644 dlls/wininet/wininet.spec create mode 100644 include/urlmon.h create mode 100644 include/wininet.h diff --git a/Make.rules.in b/Make.rules.in index 1a9e54b5fde..6dd52f181c9 100644 --- a/Make.rules.in +++ b/Make.rules.in @@ -128,6 +128,7 @@ DLLS = \ stress \ tapi32 \ ttydrv \ + urlmon \ user32 \ version \ w32skrnl \ @@ -135,6 +136,7 @@ DLLS = \ windebug \ wineoss.drv \ wing \ + wininet \ winmm \ winspool \ wnaspi32 \ diff --git a/configure b/configure index 7140620def7..3945af07ab1 100755 --- a/configure +++ b/configure @@ -6271,6 +6271,7 @@ dlls/sound/Makefile dlls/stress/Makefile dlls/tapi32/Makefile dlls/ttydrv/Makefile +dlls/urlmon/Makefile dlls/user/Makefile dlls/version/Makefile dlls/win32s/Makefile @@ -6278,6 +6279,7 @@ dlls/win87em/Makefile dlls/winaspi/Makefile dlls/windebug/Makefile dlls/wing/Makefile +dlls/wininet/Makefile dlls/winmm/Makefile dlls/winmm/joystick/Makefile dlls/winmm/mcianim/Makefile @@ -6497,6 +6499,7 @@ dlls/sound/Makefile dlls/stress/Makefile dlls/tapi32/Makefile dlls/ttydrv/Makefile +dlls/urlmon/Makefile dlls/user/Makefile dlls/version/Makefile dlls/win32s/Makefile @@ -6504,6 +6507,7 @@ dlls/win87em/Makefile dlls/winaspi/Makefile dlls/windebug/Makefile dlls/wing/Makefile +dlls/wininet/Makefile dlls/winmm/Makefile dlls/winmm/joystick/Makefile dlls/winmm/mcianim/Makefile diff --git a/configure.in b/configure.in index 915acfca6e6..bb1084dc76a 100644 --- a/configure.in +++ b/configure.in @@ -1027,6 +1027,7 @@ dlls/sound/Makefile dlls/stress/Makefile dlls/tapi32/Makefile dlls/ttydrv/Makefile +dlls/urlmon/Makefile dlls/user/Makefile dlls/version/Makefile dlls/win32s/Makefile @@ -1034,6 +1035,7 @@ dlls/win87em/Makefile dlls/winaspi/Makefile dlls/windebug/Makefile dlls/wing/Makefile +dlls/wininet/Makefile dlls/winmm/Makefile dlls/winmm/joystick/Makefile dlls/winmm/mcianim/Makefile diff --git a/dlls/Makefile.in b/dlls/Makefile.in index e0f8703384e..8e175e806d5 100644 --- a/dlls/Makefile.in +++ b/dlls/Makefile.in @@ -37,6 +37,7 @@ DLLFILES = \ stress/libstress.@LIBEXT@ \ tapi32/libtapi32.@LIBEXT@ \ ttydrv/libttydrv.@LIBEXT@ \ + urlmon/liburlmon.@LIBEXT@ \ user/libuser32.@LIBEXT@ \ version/libversion.@LIBEXT@ \ win32s/libw32skrnl.@LIBEXT@ \ @@ -44,6 +45,7 @@ DLLFILES = \ winaspi/libwnaspi32.@LIBEXT@ \ windebug/libwindebug.@LIBEXT@ \ wing/libwing.@LIBEXT@ \ + wininet/libwininet.@LIBEXT@ \ winmm/joystick/libjoystick.drv.@LIBEXT@ \ winmm/libwinmm.@LIBEXT@ \ winmm/mcianim/libmcianim.drv.@LIBEXT@ \ @@ -118,6 +120,7 @@ SUBDIRS = \ stress \ tapi32 \ ttydrv \ + urlmon \ user \ version \ win32s \ @@ -125,6 +128,7 @@ SUBDIRS = \ winaspi \ windebug \ wing \ + wininet \ winmm \ winsock \ winspool \ @@ -259,6 +263,9 @@ libtapi32.@LIBEXT@: tapi32/libtapi32.@LIBEXT@ libttydrv.@LIBEXT@: ttydrv/libttydrv.@LIBEXT@ $(RM) $@ && $(LN_S) ttydrv/libttydrv.@LIBEXT@ $@ +liburlmon.@LIBEXT@: urlmon/liburlmon.@LIBEXT@ + $(RM) $@ && $(LN_S) urlmon/liburlmon.@LIBEXT@ $@ + libuser32.@LIBEXT@ libuser.@LIBEXT@: user/libuser32.@LIBEXT@ $(RM) $@ && $(LN_S) user/libuser32.@LIBEXT@ $@ @@ -280,6 +287,9 @@ libwineoss.drv.@LIBEXT@: winmm/wineoss/libwineoss.drv.@LIBEXT@ libwing.@LIBEXT@: wing/libwing.@LIBEXT@ $(RM) $@ && $(LN_S) wing/libwing.@LIBEXT@ $@ +libwininet.@LIBEXT@: wininet/libwininet.@LIBEXT@ + $(RM) $@ && $(LN_S) wininet/libwininet.@LIBEXT@ $@ + libwinmm.@LIBEXT@ libmmsystem.@LIBEXT@: winmm/libwinmm.@LIBEXT@ $(RM) $@ && $(LN_S) winmm/libwinmm.@LIBEXT@ $@ diff --git a/dlls/urlmon/.cvsignore b/dlls/urlmon/.cvsignore new file mode 100644 index 00000000000..6c652b13249 --- /dev/null +++ b/dlls/urlmon/.cvsignore @@ -0,0 +1,3 @@ +Makefile +liburlmon.so.1.0 +urlmon.spec.c diff --git a/dlls/urlmon/Makefile.in b/dlls/urlmon/Makefile.in new file mode 100644 index 00000000000..3993b8992a1 --- /dev/null +++ b/dlls/urlmon/Makefile.in @@ -0,0 +1,16 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = urlmon +SOVERSION = 1.0 +IMPORTS = ole32 +WRCEXTRA = -s -p$(MODULE) + +SPEC_SRCS = urlmon.spec + +C_SRCS = umon.c + +@MAKE_DLL_RULES@ + +### Dependencies: diff --git a/dlls/urlmon/umon.c b/dlls/urlmon/umon.c new file mode 100644 index 00000000000..d8317125ee4 --- /dev/null +++ b/dlls/urlmon/umon.c @@ -0,0 +1,37 @@ +/* + * UrlMon + * + * Copyright 1999 Corel Corporation + * + * Ulrich Czekalla + * + */ + +#include "windows.h" +#include "objbase.h" +#include "debugtools.h" + +#include "urlmon.h" + +DEFAULT_DEBUG_CHANNEL(win32); + +/*********************************************************************** + * CreateURLMoniker (URLMON.22) + * + * Create a url moniker + * + * RETURNS + * S_OK success + * E_OUTOFMEMORY out of memory + * MK_E_SYNTAX not a valid url + * + */ +HRESULT CreateURLMoniker(IMoniker *pmkContext, LPWSTR szURL, IMoniker **ppmk) +{ + TRACE("\n"); + + if (NULL != pmkContext) + FIXME("Non-null pmkContext not implemented\n"); + + return CreateFileMoniker(szURL, ppmk); +} diff --git a/dlls/urlmon/urlmon.spec b/dlls/urlmon/urlmon.spec new file mode 100644 index 00000000000..a9b68321fc8 --- /dev/null +++ b/dlls/urlmon/urlmon.spec @@ -0,0 +1,80 @@ +name urlmon +type win32 + +1 stub CDLGetLongPathNameA +2 stub CDLGetLongPathNameW +@ stub AsyncGetClassBits +@ stub AsyncInstallDistributionUnit +@ stub BindAsyncMoniker +@ stub CoGetClassObjectFromURL +@ stub CoInstall +@ stub CoInternetCombineUrl +@ stub CoInternetCompareUrl +@ stub CoInternetCreateSecurityManager +@ stub CoInternetCreateZoneManager +@ stub CoInternetGetProtocolFlags +@ stub CoInternetGetSecurityUrl +@ stub CoInternetGetSession +@ stub CoInternetParseUrl +@ stub CoInternetQueryInfo +@ stub CopyBindInfo +@ stub CopyStgMedium +@ stub CreateAsyncBindCtx +@ stub CreateAsyncBindCtxEx +@ stub CreateFormatEnumerator +@ stdcall CreateURLMoniker(ptr str ptr) CreateURLMoniker +@ stub DllCanUnloadNow +@ stub DllGetClassObject +@ stub DllInstall +@ stub DllRegisterServer +@ stub DllRegisterServerEx +@ stub DllUnregisterServer +@ stub Extract +@ stub FaultInIEFeature +@ stub FindMediaType +@ stub FindMediaTypeClass +@ stub FindMimeFromData +@ stub GetClassFileOrMime +@ stub GetClassURL +@ stub GetComponentIDFromCLSSPEC +@ stub GetMarkOfTheWeb +@ stub GetSoftwareUpdateInfo +@ stub HlinkGoBack +@ stub HlinkGoForward +@ stub HlinkNavigateMoniker +@ stub HlinkNavigateString +@ stub HlinkSimpleNavigateToMoniker +@ stub HlinkSimpleNavigateToString +@ stub IsAsyncMoniker +@ stub IsLoggingEnabledA +@ stub IsLoggingEnabledW +@ stub IsValidURL +@ stub MkParseDisplayNameEx +@ stub ObtainUserAgentString +@ stub PrivateCoInstall +@ stub RegisterBindStatusCallback +@ stub RegisterFormatEnumerator +@ stub RegisterMediaTypeClass +@ stub RegisterMediaTypes +@ stub ReleaseBindInfo +@ stub RevokeBindStatusCallback +@ stub RevokeFormatEnumerator +@ stub SetSoftwareUpdateAdvertisementState +@ stub URLDownloadA +@ stub URLDownloadToCacheFileA +@ stub URLDownloadToCacheFileW +@ stub URLDownloadToFileA +@ stub URLDownloadToFileW +@ stub URLDownloadW +@ stub URLOpenBlockingStreamA +@ stub URLOpenBlockingStreamW +@ stub URLOpenPullStreamA +@ stub URLOpenPullStreamW +@ stub URLOpenStreamA +@ stub URLOpenStreamW +@ stub UrlMkBuildVersion +@ stub UrlMkGetSessionOption +@ stub UrlMkSetSessionOption +@ stub WriteHitLogging +@ stub ZonesReInit + diff --git a/dlls/wininet/.cvsignore b/dlls/wininet/.cvsignore new file mode 100644 index 00000000000..a3fb11a1b0c --- /dev/null +++ b/dlls/wininet/.cvsignore @@ -0,0 +1,3 @@ +Makefile +libwininet.so.1.0 +wininet.spec.c diff --git a/dlls/wininet/Makefile.in b/dlls/wininet/Makefile.in new file mode 100644 index 00000000000..4181df4f82b --- /dev/null +++ b/dlls/wininet/Makefile.in @@ -0,0 +1,17 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = wininet +SOVERSION = 1.0 + +SPEC_SRCS = wininet.spec + +C_SRCS = \ + internet.c \ + ftp.c \ + utility.c + +@MAKE_DLL_RULES@ + +### Dependencies: diff --git a/dlls/wininet/ftp.c b/dlls/wininet/ftp.c new file mode 100644 index 00000000000..4bb99f97768 --- /dev/null +++ b/dlls/wininet/ftp.c @@ -0,0 +1,2491 @@ +/* + * WININET - Ftp implementation + * + * Copyright 1999 Corel Corporation + * + * Ulrich Czekalla + * Noureddine Jemmali + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "windows.h" +#include "wininet.h" +#include "winerror.h" +#include "winsock.h" + +#include "debugtools.h" +#include "internet.h" + +DEFAULT_DEBUG_CHANNEL(wininet); + +#define NOACCOUNT "noaccount" +#define DATA_PACKET_SIZE 0x2000 +#define szCRLF "\r\n" +#define MAX_BACKLOG 5 +#define RESPONSE_TIMEOUT 30 + +typedef enum { + /* FTP commands with arguments. */ + FTP_CMD_ACCT, + FTP_CMD_CWD, + FTP_CMD_DELE, + FTP_CMD_MKD, + FTP_CMD_PASS, + FTP_CMD_PORT, + FTP_CMD_RETR, + FTP_CMD_RMD, + FTP_CMD_RNFR, + FTP_CMD_RNTO, + FTP_CMD_STOR, + FTP_CMD_TYPE, + FTP_CMD_USER, + + /* FTP commands without arguments. */ + FTP_CMD_ABOR, + FTP_CMD_LIST, + FTP_CMD_NLST, + FTP_CMD_PWD, + FTP_CMD_QUIT, +} FTP_COMMAND; + +static const CHAR *szFtpCommands[] = { + "ACCT", + "CWD", + "DELE", + "MKD", + "PASS", + "PORT", + "RETR", + "RMD", + "RNFR", + "RNTO", + "STOR", + "TYPE", + "USER", + "ABOR", + "LIST", + "NLST", + "PWD", + "QUIT", +}; + +static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; + +BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam, + INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext); +BOOL FTP_SendStore(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType); +BOOL FTP_InitDataSocket(LPWININETFTPSESSIONA lpwfs, LPINT nDataSocket); +BOOL FTP_SendData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, HANDLE hFile); +INT FTP_ReceiveResponse(INT nSocket, LPSTR lpszResponse, DWORD dwResponse, + INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext); +DWORD FTP_SendRetrieve(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType); +BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile); +BOOL FTP_InitListenSocket(LPWININETFTPSESSIONA lpwfs); +BOOL FTP_ConnectToHost(LPWININETFTPSESSIONA lpwfs); +BOOL FTP_SendPassword(LPWININETFTPSESSIONA lpwfs); +BOOL FTP_SendAccount(LPWININETFTPSESSIONA lpwfs); +BOOL FTP_SendType(LPWININETFTPSESSIONA lpwfs, DWORD dwType); +BOOL FTP_SendPort(LPWININETFTPSESSIONA lpwfs); +BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESA lpfp); +BOOL FTP_ParseDirectory(LPWININETFTPSESSIONA lpwfs, INT nSocket, LPFILEPROPERTIESA *lpafp, LPDWORD dwfp); +HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONA lpwfs, INT nSocket, + LPWIN32_FIND_DATAA lpFindFileData, DWORD dwContext); +LPSTR FTP_GetNextLine(INT nSocket, LPSTR lpszBuffer, LPDWORD dwBuffer); +DWORD FTP_SetResponseError(DWORD dwResponse); + +/*********************************************************************** + * FtpPutFileA (WININET.43) + * + * Uploads a file to the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOLAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile, + LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext) +{ + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect; + LPWININETAPPINFOA hIC = NULL; + + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + + workRequest.asyncall = FTPPUTFILEA; + workRequest.HFTPSESSION = (DWORD)hConnect; + workRequest.LPSZLOCALFILE = (DWORD)strdup(lpszLocalFile); + workRequest.LPSZNEWREMOTEFILE = (DWORD)strdup(lpszNewRemoteFile); + workRequest.DWFLAGS = dwFlags; + workRequest.DWCONTEXT = dwContext; + + return INTERNET_AsyncCall(&workRequest); + } + else + { + return FTP_FtpPutFileA(hConnect, lpszLocalFile, + lpszNewRemoteFile, dwFlags, dwContext); + } +} + +/*********************************************************************** + * FTP_FtpPutFileA (Internal) + * + * Uploads a file to the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOLAPI FTP_FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile, + LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext) +{ + HANDLE hFile = (HANDLE)NULL; + BOOL bSuccess = FALSE; + LPWININETAPPINFOA hIC = NULL; + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect; + + TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", lpszLocalFile, lpszNewRemoteFile); + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + /* Clear any error information */ + INTERNET_SetLastError(0); + + /* Open file to be uploaded */ + if (INVALID_HANDLE_VALUE == + (hFile = CreateFileA(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0))) + { + INTERNET_SetLastError(ERROR_FILE_NOT_FOUND); + goto lend; + } + + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (hIC->lpfnStatusCB) + hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0); + + if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags)) + { + INT nDataSocket; + + /* Accept connection from ftp server */ + if (FTP_InitDataSocket(lpwfs, &nDataSocket)) + { + FTP_SendData(lpwfs, nDataSocket, hFile); + bSuccess = TRUE; + close(nDataSocket); + } + } + +lend: + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); + hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + if (hFile) + CloseHandle(hFile); + + return bSuccess; +} + + +/*********************************************************************** + * FtpSetCurrentDirectoryA (WININET.49) + * + * Change the working directory on the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOLAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory) +{ + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect; + LPWININETAPPINFOA hIC = NULL; + + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + TRACE("lpszDirectory(%s)\n", lpszDirectory); + + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + + workRequest.asyncall = FTPSETCURRENTDIRECTORYA; + workRequest.HFTPSESSION = (DWORD)hConnect; + workRequest.LPSZDIRECTORY = (DWORD)strdup(lpszDirectory); + + return INTERNET_AsyncCall(&workRequest); + } + else + { + return FTP_FtpSetCurrentDirectoryA(hConnect, lpszDirectory); + } +} + + +/*********************************************************************** + * FTP_FtpSetCurrentDirectoryA (Internal) + * + * Change the working directory on the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOLAPI FTP_FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory) +{ + INT nResCode; + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect; + LPWININETAPPINFOA hIC = NULL; + DWORD bSuccess = FALSE; + + TRACE("lpszDirectory(%s)\n", lpszDirectory); + + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + /* Clear any error information */ + INTERNET_SetLastError(0); + + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory, + hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), + MAX_REPLY_LEN, hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext); + + if (nResCode) + { + if (nResCode == 250) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + } + +lend: + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR; + hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + return bSuccess; +} + + +/*********************************************************************** + * FtpCreateDirectoryA (WININET.31) + * + * Create new directory on the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOLAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory) +{ + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect; + LPWININETAPPINFOA hIC = NULL; + + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + + workRequest.asyncall = FTPCREATEDIRECTORYA; + workRequest.HFTPSESSION = (DWORD)hConnect; + workRequest.LPSZDIRECTORY = (DWORD)strdup(lpszDirectory); + + return INTERNET_AsyncCall(&workRequest); + } + else + { + return FTP_FtpCreateDirectoryA(hConnect, lpszDirectory); + } +} + + +/*********************************************************************** + * FTP_FtpCreateDirectoryA (Internal) + * + * Create new directory on the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOLAPI FTP_FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory) +{ + INT nResCode; + BOOL bSuccess = FALSE; + LPWININETAPPINFOA hIC = NULL; + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect; + + TRACE("\n"); + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + /* Clear any error information */ + INTERNET_SetLastError(0); + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), + MAX_REPLY_LEN, 0, 0, 0); + if (nResCode) + { + if (nResCode == 257) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + } + +lend: + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); + hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + return bSuccess; +} + + +/*********************************************************************** + * FtpFindFirstFileA (WININET.35) + * + * Search the specified directory + * + * RETURNS + * HINTERNET on success + * NULL on failure + * + */ +INTERNETAPI HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect, + LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD dwContext) +{ + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect; + LPWININETAPPINFOA hIC = NULL; + + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + + workRequest.asyncall = FTPFINDFIRSTFILEA; + workRequest.HFTPSESSION = (DWORD)hConnect; + workRequest.LPSZSEARCHFILE = (DWORD)strdup(lpszSearchFile); + workRequest.LPFINDFILEDATA = (DWORD)lpFindFileData; + workRequest.DWFLAGS = dwFlags; + workRequest.DWCONTEXT= dwContext; + + INTERNET_AsyncCall(&workRequest); + return NULL; + } + else + { + return FTP_FtpFindFirstFileA(hConnect, lpszSearchFile, lpFindFileData, + dwFlags, dwContext); + } +} + + +/*********************************************************************** + * FTP_FtpFindFirstFileA (Internal) + * + * Search the specified directory + * + * RETURNS + * HINTERNET on success + * NULL on failure + * + */ +INTERNETAPI HINTERNET WINAPI FTP_FtpFindFirstFileA(HINTERNET hConnect, + LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD dwContext) +{ + INT nResCode; + LPWININETAPPINFOA hIC = NULL; + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect; + LPWININETFINDNEXTA hFindNext = NULL; + + TRACE("\n"); + + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + /* Clear any error information */ + INTERNET_SetLastError(0); + + if (!FTP_InitListenSocket(lpwfs)) + goto lend; + + if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII)) + goto lend; + + if (!FTP_SendPort(lpwfs)) + goto lend; + + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, lpszSearchFile, + hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), + MAX_REPLY_LEN, hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext); + if (nResCode) + { + if (nResCode == 125 || nResCode == 150) + { + INT nDataSocket; + + if (FTP_InitDataSocket(lpwfs, &nDataSocket)) + { + hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpFindFileData, dwContext); + + nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), + MAX_REPLY_LEN, hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext); + if (nResCode != 226 && nResCode != 250) + INTERNET_SetLastError(ERROR_NO_MORE_FILES); + + close(nDataSocket); + } + } + else + FTP_SetResponseError(nResCode); + } + +lend: + if (lpwfs->lstnSocket != INVALID_SOCKET) + close(lpwfs->lstnSocket); + + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) + { + INTERNET_ASYNC_RESULT iar; + + if (hFindNext) + { + iar.dwResult = (DWORD)hFindNext; + iar.dwError = ERROR_SUCCESS; + hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + iar.dwResult = (DWORD)hFindNext; + iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError(); + hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + return (HINTERNET)hFindNext; +} + + +/*********************************************************************** + * FtpGetCurrentDirectoryA (WININET.37) + * + * Retrieves the current directory + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOLAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory, + LPDWORD lpdwCurrentDirectory) +{ + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; + LPWININETAPPINFOA hIC = NULL; + + TRACE("len(%ld)\n", *lpdwCurrentDirectory); + + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + + workRequest.asyncall = FTPGETCURRENTDIRECTORYA; + workRequest.HFTPSESSION = (DWORD)hFtpSession; + workRequest.LPSZDIRECTORY = (DWORD)lpszCurrentDirectory; + workRequest.LPDWDIRECTORY = (DWORD)lpdwCurrentDirectory; + + return INTERNET_AsyncCall(&workRequest); + } + else + { + return FTP_FtpGetCurrentDirectoryA(hFtpSession, lpszCurrentDirectory, + lpdwCurrentDirectory); + } +} + + +/*********************************************************************** + * FTP_FtpGetCurrentDirectoryA (Internal) + * + * Retrieves the current directory + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOLAPI FTP_FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory, + LPDWORD lpdwCurrentDirectory) +{ + INT nResCode; + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; + LPWININETAPPINFOA hIC = NULL; + DWORD bSuccess = FALSE; + + TRACE("len(%ld)\n", *lpdwCurrentDirectory); + + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + /* Clear any error information */ + INTERNET_SetLastError(0); + + ZeroMemory(lpszCurrentDirectory, *lpdwCurrentDirectory); + + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL, + hIC->lpfnStatusCB, hFtpSession, lpwfs->hdr.dwContext)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), + MAX_REPLY_LEN, hIC->lpfnStatusCB, hFtpSession, lpwfs->hdr.dwContext); + if (nResCode) + { + if (nResCode == 257) /* Extract directory name */ + { + INT firstpos, lastpos, len; + LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer(); + + for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++) + { + if ('"' == lpszResponseBuffer[lastpos]) + { + if (!firstpos) + firstpos = lastpos; + else + break; + } + } + + len = lastpos - firstpos - 1; + strncpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos+1], + len < *lpdwCurrentDirectory ? len : *lpdwCurrentDirectory); + *lpdwCurrentDirectory = len; + bSuccess = TRUE; + } + else + FTP_SetResponseError(nResCode); + } + +lend: + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR; + hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + return (DWORD) bSuccess; +} + +/*********************************************************************** + * FtpOpenFileA (WININET.41) + * + * Open a remote file for writing or reading + * + * RETURNS + * HINTERNET handle on success + * NULL on failure + * + */ +INTERNETAPI HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession, + LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags, + DWORD dwContext) +{ + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; + LPWININETAPPINFOA hIC = NULL; + + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + + workRequest.asyncall = FTPOPENFILEA; + workRequest.HFTPSESSION = (DWORD)hFtpSession; + workRequest.LPSZFILENAME = (DWORD)strdup(lpszFileName); + workRequest.FDWACCESS = fdwAccess; + workRequest.DWFLAGS = dwFlags; + workRequest.DWCONTEXT = dwContext; + + INTERNET_AsyncCall(&workRequest); + return NULL; + } + else + { + return FTP_FtpOpenFileA(hFtpSession, lpszFileName, fdwAccess, dwFlags, dwContext); + } +} + + +/*********************************************************************** + * FTP_FtpOpenFileA (Internal) + * + * Open a remote file for writing or reading + * + * RETURNS + * HINTERNET handle on success + * NULL on failure + * + */ +HINTERNET FTP_FtpOpenFileA(HINTERNET hFtpSession, + LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags, + DWORD dwContext) +{ + INT nDataSocket; + BOOL bSuccess = FALSE; + LPWININETFILE hFile = NULL; + LPWININETAPPINFOA hIC = NULL; + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; + + TRACE("\n"); + + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + /* Clear any error information */ + INTERNET_SetLastError(0); + + if (GENERIC_READ == fdwAccess) + { + /* Set up socket to retrieve data */ + bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags); + } + else if (GENERIC_WRITE == fdwAccess) + { + /* Set up socket to send data */ + bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags); + } + + /* Accept connection from server */ + if (bSuccess && FTP_InitDataSocket(lpwfs, &nDataSocket)) + { + hFile = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFILE)); + hFile->hdr.htype = WH_HFILE; + hFile->hdr.dwFlags = dwFlags; + hFile->hdr.dwContext = dwContext; + hFile->hdr.lpwhparent = hFtpSession; + hFile->nDataSocket = nDataSocket; + } + + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) + { + INTERNET_ASYNC_RESULT iar; + + if (hFile) + { + iar.dwResult = (DWORD)hFile; + iar.dwError = ERROR_SUCCESS; + hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); + hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + return (HINTERNET)hFile; +} + + +/*********************************************************************** + * FtpGetFileA (WININET.39) + * + * Retrieve file from the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOLAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile, + BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags, + DWORD dwContext) +{ + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hInternet; + LPWININETAPPINFOA hIC = NULL; + + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + + workRequest.asyncall = FTPGETFILEA; + workRequest.HFTPSESSION = (DWORD)hInternet; + workRequest.LPSZREMOTEFILE = (DWORD)strdup(lpszRemoteFile); + workRequest.LPSZNEWFILE = (DWORD)strdup(lpszNewFile); + workRequest.DWLOCALFLAGSATTRIBUTE = dwLocalFlagsAttribute; + workRequest.FFAILIFEXISTS = (DWORD)fFailIfExists; + workRequest.DWFLAGS = dwInternetFlags; + workRequest.DWCONTEXT = dwContext; + + return INTERNET_AsyncCall(&workRequest); + } + else + { + return FTP_FtpGetFileA(hInternet, lpszRemoteFile, lpszNewFile, + fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext); + } +} + + +/*********************************************************************** + * FTP_FtpGetFileA (Internal) + * + * Retrieve file from the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOLAPI FTP_FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile, + BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags, + DWORD dwContext) +{ + DWORD nBytes; + BOOL bSuccess = FALSE; + HANDLE hFile; + LPWININETAPPINFOA hIC = NULL; + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hInternet; + + TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", lpszRemoteFile, lpszNewFile); + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + /* Clear any error information */ + INTERNET_SetLastError(0); + + /* Ensure we can write to lpszNewfile by opening it */ + hFile = CreateFileA(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ? + CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0); + if (INVALID_HANDLE_VALUE == hFile) + goto lend; + + /* Set up socket to retrieve data */ + nBytes = FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags); + + if (nBytes > 0) + { + INT nDataSocket; + + /* Accept connection from ftp server */ + if (FTP_InitDataSocket(lpwfs, &nDataSocket)) + { + INT nResCode; + + /* Receive data */ + FTP_RetrieveFileData(lpwfs, nDataSocket, nBytes, hFile); + nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), + MAX_REPLY_LEN, 0, 0, 0); + if (nResCode) + { + if (nResCode == 226) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + } + close(nDataSocket); + } + } + +lend: + if (hFile) + CloseHandle(hFile); + + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); + hIC->lpfnStatusCB(hInternet, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + return bSuccess; +} + + +/*********************************************************************** + * FtpDeleteFileA (WININET.33) + * + * Delete a file on the ftp server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOLAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName) +{ + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; + LPWININETAPPINFOA hIC = NULL; + + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + + workRequest.asyncall = FTPRENAMEFILEA; + workRequest.HFTPSESSION = (DWORD)hFtpSession; + workRequest.LPSZFILENAME = (DWORD)strdup(lpszFileName); + + return INTERNET_AsyncCall(&workRequest); + } + else + { + return FTP_FtpDeleteFileA(hFtpSession, lpszFileName); + } +} + + +/*********************************************************************** + * FTP_FtpDeleteFileA (Internal) + * + * Delete a file on the ftp server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName) +{ + INT nResCode; + BOOL bSuccess = FALSE; + LPWININETAPPINFOA hIC = NULL; + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; + + TRACE("0x%08lx\n", (ULONG) hFtpSession); + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + /* Clear any error information */ + INTERNET_SetLastError(0); + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), + MAX_REPLY_LEN, 0, 0, 0); + if (nResCode) + { + if (nResCode == 250) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + } +lend: + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); + hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + return bSuccess; +} + + +/*********************************************************************** + * FtpRemoveDirectoryA (WININET.45) + * + * Remove a directory on the ftp server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOLAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory) +{ + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; + LPWININETAPPINFOA hIC = NULL; + + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + + workRequest.asyncall = FTPREMOVEDIRECTORYA; + workRequest.HFTPSESSION = (DWORD)hFtpSession; + workRequest.LPSZDIRECTORY = (DWORD)strdup(lpszDirectory); + + return INTERNET_AsyncCall(&workRequest); + } + else + { + return FTP_FtpRemoveDirectoryA(hFtpSession, lpszDirectory); + } +} + + +/*********************************************************************** + * FTP_FtpRemoveDirectoryA (Internal) + * + * Remove a directory on the ftp server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory) +{ + INT nResCode; + BOOL bSuccess = FALSE; + LPWININETAPPINFOA hIC = NULL; + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; + + TRACE("\n"); + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + /* Clear any error information */ + INTERNET_SetLastError(0); + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), + MAX_REPLY_LEN, 0, 0, 0); + if (nResCode) + { + if (nResCode == 250) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + } + +lend: + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); + hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + return bSuccess; +} + + +/*********************************************************************** + * FtpRenameFileA (WININET.47) + * + * Rename a file on the ftp server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOLAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest) +{ + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; + LPWININETAPPINFOA hIC = NULL; + + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + + workRequest.asyncall = FTPRENAMEFILEA; + workRequest.HFTPSESSION = (DWORD)hFtpSession; + workRequest.LPSZSRCFILE = (DWORD)strdup(lpszSrc); + workRequest.LPSZDESTFILE = (DWORD)strdup(lpszDest); + + return INTERNET_AsyncCall(&workRequest); + } + else + { + return FTP_FtpRenameFileA(hFtpSession, lpszSrc, lpszDest); + } +} + +/*********************************************************************** + * FTP_FtpRenameFileA (Internal) + * + * Rename a file on the ftp server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest) +{ + INT nResCode; + BOOL bSuccess = FALSE; + LPWININETAPPINFOA hIC = NULL; + LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; + + TRACE("\n"); + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + /* Clear any error information */ + INTERNET_SetLastError(0); + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, + INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0); + if (nResCode == 350) + { + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, + INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0); + } + + if (nResCode == 250) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + +lend: + hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); + hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + return bSuccess; +} + + +/*********************************************************************** + * FTP_Connect (internal) + * + * Connect to a ftp server + * + * RETURNS + * HINTERNET a session handle on success + * NULL on failure + * + */ + +HINTERNET FTP_Connect(HINTERNET hInternet, LPCSTR lpszServerName, + INTERNET_PORT nServerPort, LPCSTR lpszUserName, + LPCSTR lpszPassword, DWORD dwFlags, DWORD dwContext) +{ + struct sockaddr_in socketAddr; + struct hostent *phe = NULL; + INT nsocket = INVALID_SOCKET; + LPWININETAPPINFOA hIC = NULL; + BOOL bSuccess = FALSE; + LPWININETFTPSESSIONA lpwfs = NULL; + + TRACE("0x%08lx Server(%s) Port(%d) User(%s) Paswd(%s)\n", + (ULONG) hInternet, lpszServerName, + nServerPort, lpszUserName, lpszPassword); + + if (((LPWININETHANDLEHEADER)hInternet)->htype != WH_HINIT) + goto lerror; + + hIC = (LPWININETAPPINFOA) hInternet; + + if (NULL == lpszUserName && NULL != lpszPassword) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_USER_NAME); + goto lerror; + } + + if (nServerPort == INTERNET_INVALID_PORT_NUMBER) + nServerPort = INTERNET_DEFAULT_FTP_PORT; + + if (hIC->lpfnStatusCB) + hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_RESOLVING_NAME, + lpszServerName, strlen(lpszServerName)); + + if (!GetAddress(lpszServerName, nServerPort, &phe, &socketAddr)) + { + INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED); + goto lerror; + } + + if (hIC->lpfnStatusCB) + hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_NAME_RESOLVED, + lpszServerName, strlen(lpszServerName)); + + if (INVALID_SOCKET == (nsocket = socket(AF_INET,SOCK_STREAM,0))) + { + INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT); + goto lerror; + } + + if (hIC->lpfnStatusCB) + hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER, + &socketAddr, sizeof(struct sockaddr_in)); + + if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0) + { + ERR("Unable to connect: errno(%d)\n", errno); + INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT); + } + else + { + TRACE("Connected to server\n"); + if (hIC->lpfnStatusCB) + hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER, + &socketAddr, sizeof(struct sockaddr_in)); + + lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONA)); + if (NULL == lpwfs) + { + INTERNET_SetLastError(ERROR_OUTOFMEMORY); + goto lerror; + } + + lpwfs->hdr.htype = WH_HFTPSESSION; + lpwfs->hdr.dwFlags = dwFlags; + lpwfs->hdr.dwContext = dwContext; + lpwfs->hdr.lpwhparent = (LPWININETHANDLEHEADER)hInternet; + lpwfs->sndSocket = nsocket; + memcpy(&lpwfs->socketAddress, &socketAddr, sizeof(socketAddr)); + lpwfs->phostent = phe; + + if (NULL == lpszUserName) + { + lpwfs->lpszUserName = strdup("anonymous"); + lpwfs->lpszPassword = strdup("user@server"); + } + else + { + lpwfs->lpszUserName = strdup(lpszUserName); + lpwfs->lpszPassword = strdup(lpszPassword); + } + + if (FTP_ConnectToHost(lpwfs)) + { + if (hIC->lpfnStatusCB) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)lpwfs; + iar.dwError = ERROR_SUCCESS; + + hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_HANDLE_CREATED, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + TRACE("Successfully logged into server\n"); + bSuccess = TRUE; + } + } + +lerror: + if (!bSuccess && INVALID_SOCKET != nsocket) + close(nsocket); + + if (!bSuccess && lpwfs) + { + HeapFree(GetProcessHeap(), 0, lpwfs); + lpwfs = NULL; + } + + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)lpwfs; + iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); + hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + return (HINTERNET) lpwfs; +} + + +/*********************************************************************** + * FTP_ConnectHost (internal) + * + * Connect to a ftp server + * + * RETURNS + * TRUE on success + * NULL on failure + * + */ +BOOL FTP_ConnectToHost(LPWININETFTPSESSIONA lpwfs) +{ + INT nResCode; + BOOL bSuccess = FALSE; + + TRACE("\n"); + FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0); + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), + MAX_REPLY_LEN, 0, 0, 0); + if (nResCode) + { + /* Login successful... */ + if (nResCode == 230) + bSuccess = TRUE; + /* User name okay, need password... */ + else if (nResCode == 331) + bSuccess = FTP_SendPassword(lpwfs); + /* Need account for login... */ + else if (nResCode == 332) + bSuccess = FTP_SendAccount(lpwfs); + else + FTP_SetResponseError(nResCode); + } + + TRACE("Returning %d\n", bSuccess); +lend: + return bSuccess; +} + + +/*********************************************************************** + * FTP_SendCommand (internal) + * + * Send command to server + * + * RETURNS + * TRUE on success + * NULL on failure + * + */ +BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam, + INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext) +{ + DWORD len; + CHAR *buf; + DWORD nBytesSent = 0; + DWORD nRC = 0; + BOOL bParamHasLen; + + TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket); + + if (lpfnStatusCB) + lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0); + + bParamHasLen = lpszParam && strlen(lpszParam) > 0; + len = (bParamHasLen ? strlen(lpszParam) : -1) + strlen(szFtpCommands[ftpCmd]) + + strlen(szCRLF)+ 1; + if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1))) + { + INTERNET_SetLastError(ERROR_OUTOFMEMORY); + return FALSE; + } + sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], bParamHasLen ? " " : "", + bParamHasLen ? lpszParam : "", szCRLF); + + TRACE("Sending (%s) len(%ld)\n", buf, len); + while((nBytesSent < len) && (nRC != SOCKET_ERROR)) + { + nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0); + nBytesSent += nRC; + } + + HeapFree(GetProcessHeap(), 0, (LPVOID)buf); + + if (lpfnStatusCB) + lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_REQUEST_SENT, + &nBytesSent, sizeof(DWORD)); + + TRACE("Sent %ld bytes\n", nBytesSent); + return (nRC != SOCKET_ERROR); +} + + +/*********************************************************************** + * FTP_ReceiveResponse (internal) + * + * Receive response from server + * + * RETURNS + * Reply code on success + * 0 on failure + * + */ + +INT FTP_ReceiveResponse(INT nSocket, LPSTR lpszResponse, DWORD dwResponse, + INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext) +{ + DWORD nRecv; + INT rc = 0; + + TRACE("socket(%d) \n", nSocket); + + if (lpfnStatusCB) + lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0); + + while(1) + { + nRecv = dwResponse; + if (!FTP_GetNextLine(nSocket, lpszResponse, &nRecv)) + goto lerror; + + if (nRecv >= 3 && lpszResponse[3] != '-') + break; + } + + if (nRecv >= 3) + { + lpszResponse[nRecv] = '\0'; + rc = atoi(lpszResponse); + + if (lpfnStatusCB) + lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED, + &nRecv, sizeof(DWORD)); + } + +lerror: + TRACE("return %d\n", rc); + return rc; +} + + +/*********************************************************************** + * FTP_SendPassword (internal) + * + * Send password to ftp server + * + * RETURNS + * TRUE on success + * NULL on failure + * + */ +BOOL FTP_SendPassword(LPWININETFTPSESSIONA lpwfs) +{ + INT nResCode; + BOOL bSuccess = FALSE; + + TRACE("\n"); + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), + MAX_REPLY_LEN, 0, 0, 0); + if (nResCode) + { + TRACE("Received reply code %d\n", nResCode); + /* Login successful... */ + if (nResCode == 230) + bSuccess = TRUE; + /* Command not implemented, superfluous at the server site... */ + /* Need account for login... */ + else if (nResCode == 332) + bSuccess = FTP_SendAccount(lpwfs); + else + FTP_SetResponseError(nResCode); + } + +lend: + TRACE("Returning %d\n", bSuccess); + return bSuccess; +} + + +/*********************************************************************** + * FTP_SendAccount (internal) + * + * + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_SendAccount(LPWININETFTPSESSIONA lpwfs) +{ + INT nResCode; + BOOL bSuccess = FALSE; + + TRACE("\n"); + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, NOACCOUNT, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), + MAX_REPLY_LEN, 0, 0, 0); + if (nResCode) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + +lend: + return bSuccess; +} + + +/*********************************************************************** + * FTP_SendStore (internal) + * + * Send request to upload file to ftp server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_SendStore(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType) +{ + INT nResCode; + BOOL bSuccess = FALSE; + + TRACE("\n"); + if (!FTP_InitListenSocket(lpwfs)) + goto lend; + + if (!FTP_SendType(lpwfs, dwType)) + goto lend; + + if (!FTP_SendPort(lpwfs)) + goto lend; + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0)) + goto lend; + nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), + MAX_REPLY_LEN, 0, 0, 0); + if (nResCode) + { + if (nResCode == 150) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + } + +lend: + if (!bSuccess && INVALID_SOCKET != lpwfs->lstnSocket) + { + close(lpwfs->lstnSocket); + lpwfs->lstnSocket = INVALID_SOCKET; + } + + return bSuccess; +} + + +/*********************************************************************** + * FTP_InitListenSocket (internal) + * + * Create a socket to listen for server response + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_InitListenSocket(LPWININETFTPSESSIONA lpwfs) +{ + BOOL bSuccess = FALSE; + socklen_t namelen = sizeof(struct sockaddr_in); + + TRACE("\n"); + + lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0); + if (INVALID_SOCKET == lpwfs->lstnSocket) + { + TRACE("Unable to create listening socket\n"); + goto lend; + } + + lpwfs->lstnSocketAddress.sin_family = AF_INET; + lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0); + lpwfs->lstnSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY); + if (SOCKET_ERROR == bind(lpwfs->lstnSocket,&lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in))) + { + TRACE("Unable to bind socket\n"); + goto lend; + } + + if (SOCKET_ERROR == listen(lpwfs->lstnSocket, MAX_BACKLOG)) + { + TRACE("listen failed\n"); + goto lend; + } + + if (SOCKET_ERROR != getsockname(lpwfs->lstnSocket, &lpwfs->lstnSocketAddress, &namelen)) + bSuccess = TRUE; + +lend: + if (!bSuccess && INVALID_SOCKET == lpwfs->lstnSocket) + { + close(lpwfs->lstnSocket); + lpwfs->lstnSocket = INVALID_SOCKET; + } + + return bSuccess; +} + + +/*********************************************************************** + * FTP_SendType (internal) + * + * Tell server type of data being transfered + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_SendType(LPWININETFTPSESSIONA lpwfs, DWORD dwType) +{ + INT nResCode; + CHAR type[2] = { "I\0" }; + BOOL bSuccess = FALSE; + + TRACE("\n"); + if (dwType & INTERNET_FLAG_TRANSFER_ASCII) + *type = 'A'; + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), + MAX_REPLY_LEN, 0, 0, 0)/100; + if (nResCode) + { + if (nResCode == 2) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + } + +lend: + return bSuccess; +} + + +/*********************************************************************** + * FTP_SendPort (internal) + * + * Tell server which port to use + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_SendPort(LPWININETFTPSESSIONA lpwfs) +{ + INT nResCode; + CHAR szIPAddress[64]; + BOOL bSuccess = FALSE; + + TRACE("\n"); + + sprintf(szIPAddress, "%d,%d,%d,%d,%d,%d", + lpwfs->socketAddress.sin_addr.s_addr&0x000000FF, + (lpwfs->socketAddress.sin_addr.s_addr&0x0000FF00)>>8, + (lpwfs->socketAddress.sin_addr.s_addr&0x00FF0000)>>16, + (lpwfs->socketAddress.sin_addr.s_addr&0xFF000000)>>24, + lpwfs->lstnSocketAddress.sin_port & 0xFF, + (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8); + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), + MAX_REPLY_LEN,0, 0, 0); + if (nResCode) + { + if (nResCode == 200) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + } + +lend: + return bSuccess; +} + + +/*********************************************************************** + * FTP_InitDataSocket (internal) + * + * + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_InitDataSocket(LPWININETFTPSESSIONA lpwfs, LPINT nDataSocket) +{ + struct sockaddr_in saddr; + socklen_t addrlen = sizeof(struct sockaddr); + + TRACE("\n"); + *nDataSocket = accept(lpwfs->lstnSocket, &saddr, &addrlen); + close(lpwfs->lstnSocket); + lpwfs->lstnSocket = INVALID_SOCKET; + + return *nDataSocket != INVALID_SOCKET; +} + + +/*********************************************************************** + * FTP_SendData (internal) + * + * Send data to the server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_SendData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, HANDLE hFile) +{ + BY_HANDLE_FILE_INFORMATION fi; + DWORD nBytesRead = 0; + DWORD nBytesSent = 0; + DWORD nTotalSent = 0; + DWORD nBytesToSend, nLen, nRC = 1; + time_t s_long_time, e_long_time; + LONG nSeconds; + CHAR *lpszBuffer; + + TRACE("\n"); + lpszBuffer = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR)*DATA_PACKET_SIZE); + memset(lpszBuffer, 0, sizeof(CHAR)*DATA_PACKET_SIZE); + + /* Get the size of the file. */ + GetFileInformationByHandle(hFile, &fi); + time(&s_long_time); + + do + { + nBytesToSend = nBytesRead - nBytesSent; + + if (nBytesToSend <= 0) + { + /* Read data from file. */ + nBytesSent = 0; + if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0)) + ERR("Failed reading from file\n"); + + if (nBytesRead > 0) + nBytesToSend = nBytesRead; + else + break; + } + + nLen = DATA_PACKET_SIZE < nBytesToSend ? + DATA_PACKET_SIZE : nBytesToSend; + nRC = send(nDataSocket, lpszBuffer, nLen, 0); + + if (nRC != SOCKET_ERROR) + { + nBytesSent += nRC; + nTotalSent += nRC; + } + + /* Do some computation to display the status. */ + time(&e_long_time); + nSeconds = e_long_time - s_long_time; + if( nSeconds / 60 > 0 ) + { + TRACE( "%ld bytes of %d bytes (%ld%%) in %ld min %ld sec estimated remainig time %ld sec\t\t\r", + nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60, + nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent ); + } + else + { + TRACE( "%ld bytes of %d bytes (%ld%%) in %ld sec estimated remainig time %ld sec\t\t\r", + nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds, + (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent); + } + } while (nRC != SOCKET_ERROR); + + TRACE("file transfer complete!\n"); + + if(lpszBuffer != NULL) + HeapFree(GetProcessHeap(), 0, lpszBuffer); + + return nTotalSent; +} + + +/*********************************************************************** + * FTP_SendRetrieve (internal) + * + * Send request to retrieve a file + * + * RETURNS + * Number of bytes to be received on success + * 0 on failure + * + */ +DWORD FTP_SendRetrieve(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType) +{ + INT nResCode; + DWORD nResult = 0; + + TRACE("\n"); + if (!FTP_InitListenSocket(lpwfs)) + goto lend; + + if (!FTP_SendType(lpwfs, dwType)) + goto lend; + + if (!FTP_SendPort(lpwfs)) + goto lend; + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), + MAX_REPLY_LEN, 0, 0, 0); + if (nResCode) + { + if (nResCode == 125 || nResCode == 150) + { + /* Parse size of data to be retrieved */ + INT i, sizepos = -1; + LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer(); + for (i = strlen(lpszResponseBuffer) - 1; i >= 0; i--) + { + if ('(' == lpszResponseBuffer[i]) + { + sizepos = i; + break; + } + } + + if (sizepos >= 0) + { + nResult = atol(&lpszResponseBuffer[sizepos+1]); + TRACE("Waiting to receive %ld bytes\n", nResult); + } + } + } + +lend: + if (0 == nResult && INVALID_SOCKET != lpwfs->lstnSocket) + { + close(lpwfs->lstnSocket); + lpwfs->lstnSocket = INVALID_SOCKET; + } + + return nResult; +} + + +/*********************************************************************** + * FTP_RetrieveData (internal) + * + * Retrieve data from server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile) +{ + DWORD nBytesWritten; + DWORD nBytesReceived = 0; + INT nRC = 0; + CHAR *lpszBuffer; + + TRACE("\n"); + + if (INVALID_HANDLE_VALUE == hFile) + return FALSE; + + lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE); + if (NULL == lpszBuffer) + { + INTERNET_SetLastError(ERROR_OUTOFMEMORY); + return FALSE; + } + + while (nBytesReceived < nBytes && nRC != SOCKET_ERROR) + { + nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0); + if (nRC != SOCKET_ERROR) + { + /* other side closed socket. */ + if (nRC == 0) + goto recv_end; + WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL); + nBytesReceived += nRC; + } + + TRACE("%ld bytes of %ld (%ld%%)\r", nBytesReceived, nBytes, + nBytesReceived * 100 / nBytes); + } + + TRACE("Data transfer complete\n"); + if (NULL != lpszBuffer) + HeapFree(GetProcessHeap(), 0, lpszBuffer); + +recv_end: + return (nRC != SOCKET_ERROR); +} + + +/*********************************************************************** + * FTP_CloseSessionHandle (internal) + * + * Deallocate session handle + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_CloseSessionHandle(LPWININETFTPSESSIONA lpwfs) +{ + if (INVALID_SOCKET != lpwfs->sndSocket) + close(lpwfs->sndSocket); + + if (INVALID_SOCKET != lpwfs->lstnSocket) + close(lpwfs->lstnSocket); + + if (lpwfs->lpszPassword) + HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword); + + if (lpwfs->lpszUserName) + HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName); + + HeapFree(GetProcessHeap(), 0, lpwfs); + + return TRUE; +} + + +/*********************************************************************** + * FTP_CloseSessionHandle (internal) + * + * Deallocate session handle + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_CloseFindNextHandle(LPWININETFINDNEXTA lpwfn) +{ + INT i; + + TRACE("\n"); + + for (i = 0; i < lpwfn->size; i++) + { + if (NULL != lpwfn->lpafp[i].lpszName) + HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName); + } + + HeapFree(GetProcessHeap(), 0, lpwfn->lpafp); + HeapFree(GetProcessHeap(), 0, lpwfn); + + return TRUE; +} + + +/*********************************************************************** + * FTP_ReceiveFileList (internal) + * + * Read file list from server + * + * RETURNS + * Handle to file list on success + * NULL on failure + * + */ +HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONA lpwfs, INT nSocket, + LPWIN32_FIND_DATAA lpFindFileData, DWORD dwContext) +{ + DWORD dwSize = 0; + LPFILEPROPERTIESA lpafp = NULL; + LPWININETFINDNEXTA lpwfn = NULL; + + TRACE("\n"); + + if (FTP_ParseDirectory(lpwfs, nSocket, &lpafp, &dwSize)) + { + FTP_ConvertFileProp(lpafp, lpFindFileData); + + lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFINDNEXTA)); + if (NULL != lpwfn) + { + lpwfn->hdr.htype = WH_HFINDNEXT; + lpwfn->hdr.lpwhparent = (LPWININETHANDLEHEADER)lpwfs; + lpwfn->hdr.dwContext = dwContext; + lpwfn->index = 1; /* Next index is 1 since we return index 0 */ + lpwfn->size = dwSize; + lpwfn->lpafp = lpafp; + } + } + + TRACE("Matched %ld files\n", dwSize); + return (HINTERNET)lpwfn; +} + + +/*********************************************************************** + * FTP_ConvertFileProp (internal) + * + * Converts FILEPROPERTIESA struct to WIN32_FIND_DATAA + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_ConvertFileProp(LPFILEPROPERTIESA lpafp, LPWIN32_FIND_DATAA lpFindFileData) +{ + BOOL bSuccess = FALSE; + + ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA)); + + if (lpafp) + { + DWORD access = mktime(&lpafp->tmLastModified); + + /* Not all fields are filled in */ + lpFindFileData->ftLastAccessTime.dwHighDateTime = HIWORD(access); + lpFindFileData->ftLastAccessTime.dwLowDateTime = LOWORD(access); + lpFindFileData->nFileSizeHigh = HIWORD(lpafp->nSize); + lpFindFileData->nFileSizeLow = LOWORD(lpafp->nSize); + + if (lpafp->bIsDirectory) + lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY; + + if (lpafp->lpszName) + strncpy(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH); + + bSuccess = TRUE; + } + + return bSuccess; +} + + +/*********************************************************************** + * FTP_ParseDirectory (internal) + * + * Parse string of directory information + * + * RETURNS + * TRUE on success + * FALSE on failure + * + * FIXME: - This function needs serious clea-up + * - We should consider both UNIX and NT list formats + */ +#define MAX_MONTH_LEN 10 +#define MIN_LEN_DIR_ENTRY 15 + +BOOL FTP_ParseDirectory(LPWININETFTPSESSIONA lpwfs, INT nSocket, LPFILEPROPERTIESA *lpafp, LPDWORD dwfp) +{ + /* + *