ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

ftp.c
Go to the documentation of this file.
00001 /*
00002  * WININET - Ftp implementation
00003  *
00004  * Copyright 1999 Corel Corporation
00005  * Copyright 2004 Mike McCormack for CodeWeavers
00006  * Copyright 2004 Kevin Koltzau
00007  * Copyright 2007 Hans Leidekker
00008  *
00009  * Ulrich Czekalla
00010  * Noureddine Jemmali
00011  *
00012  * Copyright 2000 Andreas Mohr
00013  * Copyright 2002 Jaco Greeff
00014  *
00015  * This library is free software; you can redistribute it and/or
00016  * modify it under the terms of the GNU Lesser General Public
00017  * License as published by the Free Software Foundation; either
00018  * version 2.1 of the License, or (at your option) any later version.
00019  *
00020  * This library is distributed in the hope that it will be useful,
00021  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00022  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00023  * Lesser General Public License for more details.
00024  *
00025  * You should have received a copy of the GNU Lesser General Public
00026  * License along with this library; if not, write to the Free Software
00027  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00028  */
00029 
00030 #include "config.h"
00031 #include "wine/port.h"
00032 
00033 #if defined(__MINGW32__) || defined (_MSC_VER)
00034 #include <ws2tcpip.h>
00035 #endif
00036 
00037 #include <errno.h>
00038 #include <stdarg.h>
00039 #include <stdio.h>
00040 #include <stdlib.h>
00041 #include <string.h>
00042 #include <sys/types.h>
00043 #ifdef HAVE_SYS_SOCKET_H
00044 # include <sys/socket.h>
00045 #endif
00046 #ifdef HAVE_ARPA_INET_H
00047 # include <arpa/inet.h>
00048 #endif
00049 #ifdef HAVE_UNISTD_H
00050 # include <unistd.h>
00051 #endif
00052 #ifdef HAVE_SYS_IOCTL_H
00053 # include <sys/ioctl.h>
00054 #endif
00055 #include <time.h>
00056 #include <assert.h>
00057 
00058 #include "windef.h"
00059 #include "winbase.h"
00060 #include "wingdi.h"
00061 #include "winuser.h"
00062 #include "wininet.h"
00063 #include "winnls.h"
00064 #include "winerror.h"
00065 #include "winreg.h"
00066 #include "winternl.h"
00067 #include "shlwapi.h"
00068 
00069 #include "wine/debug.h"
00070 #include "internet.h"
00071 
00072 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
00073 
00074 typedef struct _ftp_session_t ftp_session_t;
00075 
00076 typedef struct
00077 {
00078     object_header_t hdr;
00079     ftp_session_t *lpFtpSession;
00080     BOOL session_deleted;
00081     int nDataSocket;
00082     WCHAR *cache_file;
00083     HANDLE cache_file_handle;
00084 } ftp_file_t;
00085 
00086 struct _ftp_session_t
00087 {
00088     object_header_t hdr;
00089     appinfo_t *lpAppInfo;
00090     int sndSocket;
00091     int lstnSocket;
00092     int pasvSocket; /* data socket connected by us in case of passive FTP */
00093     ftp_file_t *download_in_progress;
00094     struct sockaddr_in socketAddress;
00095     struct sockaddr_in lstnSocketAddress;
00096     LPWSTR servername;
00097     INTERNET_PORT serverport;
00098     LPWSTR  lpszPassword;
00099     LPWSTR  lpszUserName;
00100 };
00101 
00102 typedef struct
00103 {
00104     BOOL bIsDirectory;
00105     LPWSTR lpszName;
00106     DWORD nSize;
00107     SYSTEMTIME tmLastModified;
00108     unsigned short permissions;
00109 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
00110 
00111 typedef struct
00112 {
00113     object_header_t hdr;
00114     ftp_session_t *lpFtpSession;
00115     DWORD index;
00116     DWORD size;
00117     LPFILEPROPERTIESW lpafp;
00118 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
00119 
00120 #define DATA_PACKET_SIZE    0x2000
00121 #define szCRLF          "\r\n"
00122 #define MAX_BACKLOG         5
00123 
00124 /* Testing shows that Windows only accepts dwFlags where the last
00125  * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
00126  */
00127 #define FTP_CONDITION_MASK      0x0007
00128 
00129 typedef enum {
00130   /* FTP commands with arguments. */
00131   FTP_CMD_ACCT,
00132   FTP_CMD_CWD,
00133   FTP_CMD_DELE,
00134   FTP_CMD_MKD,
00135   FTP_CMD_PASS,
00136   FTP_CMD_PORT,
00137   FTP_CMD_RETR,
00138   FTP_CMD_RMD,
00139   FTP_CMD_RNFR,
00140   FTP_CMD_RNTO,
00141   FTP_CMD_STOR,
00142   FTP_CMD_TYPE,
00143   FTP_CMD_USER,
00144   FTP_CMD_SIZE,
00145 
00146   /* FTP commands without arguments. */
00147   FTP_CMD_ABOR,
00148   FTP_CMD_LIST,
00149   FTP_CMD_NLST,
00150   FTP_CMD_PASV,
00151   FTP_CMD_PWD,
00152   FTP_CMD_QUIT,
00153 } FTP_COMMAND;
00154 
00155 static const CHAR *const szFtpCommands[] = {
00156   "ACCT",
00157   "CWD",
00158   "DELE",
00159   "MKD",
00160   "PASS",
00161   "PORT",
00162   "RETR",
00163   "RMD",
00164   "RNFR",
00165   "RNTO",
00166   "STOR",
00167   "TYPE",
00168   "USER",
00169   "SIZE",
00170   "ABOR",
00171   "LIST",
00172   "NLST",
00173   "PASV",
00174   "PWD",
00175   "QUIT",
00176 };
00177 
00178 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
00179 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
00180 
00181 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
00182     INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext);
00183 static BOOL FTP_SendStore(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
00184 static BOOL FTP_GetDataSocket(ftp_session_t*, LPINT nDataSocket);
00185 static BOOL FTP_SendData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
00186 static INT FTP_ReceiveResponse(ftp_session_t*, DWORD_PTR dwContext);
00187 static BOOL FTP_SendRetrieve(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
00188 static BOOL FTP_RetrieveFileData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
00189 static BOOL FTP_InitListenSocket(ftp_session_t*);
00190 static BOOL FTP_ConnectToHost(ftp_session_t*);
00191 static BOOL FTP_SendPassword(ftp_session_t*);
00192 static BOOL FTP_SendAccount(ftp_session_t*);
00193 static BOOL FTP_SendType(ftp_session_t*, DWORD dwType);
00194 static BOOL FTP_SendPort(ftp_session_t*);
00195 static BOOL FTP_DoPassive(ftp_session_t*);
00196 static BOOL FTP_SendPortOrPasv(ftp_session_t*);
00197 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
00198 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
00199 static BOOL FTP_ParseDirectory(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
00200         LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
00201 static HINTERNET FTP_ReceiveFileList(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
00202         LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
00203 static DWORD FTP_SetResponseError(DWORD dwResponse);
00204 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
00205 static BOOL FTP_FtpPutFileW(ftp_session_t*, LPCWSTR lpszLocalFile,
00206         LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
00207 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
00208 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
00209 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t*,
00210         LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
00211 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t*, LPWSTR lpszCurrentDirectory,
00212         LPDWORD lpdwCurrentDirectory);
00213 static BOOL FTP_FtpRenameFileW(ftp_session_t*, LPCWSTR lpszSrc, LPCWSTR lpszDest);
00214 static BOOL FTP_FtpRemoveDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
00215 static BOOL FTP_FtpDeleteFileW(ftp_session_t*, LPCWSTR lpszFileName);
00216 static BOOL FTP_FtpGetFileW(ftp_session_t*, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
00217         BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
00218         DWORD_PTR dwContext);
00219 
00220 /* A temporary helper until we get rid of INTERNET_GetLastError calls */
00221 static BOOL res_to_le(DWORD res)
00222 {
00223     if(res != ERROR_SUCCESS)
00224         INTERNET_SetLastError(res);
00225     return res == ERROR_SUCCESS;
00226 }
00227 
00228 /***********************************************************************
00229  *           FtpPutFileA (WININET.@)
00230  *
00231  * Uploads a file to the FTP server
00232  *
00233  * RETURNS
00234  *    TRUE on success
00235  *    FALSE on failure
00236  *
00237  */
00238 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
00239     LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
00240 {
00241     LPWSTR lpwzLocalFile;
00242     LPWSTR lpwzNewRemoteFile;
00243     BOOL ret;
00244     
00245     lpwzLocalFile = heap_strdupAtoW(lpszLocalFile);
00246     lpwzNewRemoteFile = heap_strdupAtoW(lpszNewRemoteFile);
00247     ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
00248                       dwFlags, dwContext);
00249     HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
00250     HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
00251     return ret;
00252 }
00253 
00254 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
00255 {
00256     struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
00257     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
00258 
00259     TRACE("%p\n", lpwfs);
00260 
00261     FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
00262                req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
00263 
00264     HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
00265     HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
00266 }
00267 
00268 /***********************************************************************
00269  *           FtpPutFileW (WININET.@)
00270  *
00271  * Uploads a file to the FTP server
00272  *
00273  * RETURNS
00274  *    TRUE on success
00275  *    FALSE on failure
00276  *
00277  */
00278 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
00279     LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
00280 {
00281     ftp_session_t *lpwfs;
00282     appinfo_t *hIC = NULL;
00283     BOOL r = FALSE;
00284 
00285     if (!lpszLocalFile || !lpszNewRemoteFile)
00286     {
00287         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
00288         return FALSE;
00289     }
00290 
00291     lpwfs = (ftp_session_t*) get_handle_object( hConnect );
00292     if (!lpwfs)
00293     {
00294         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
00295         return FALSE;
00296     }
00297 
00298     if (WH_HFTPSESSION != lpwfs->hdr.htype)
00299     {
00300         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
00301         goto lend;
00302     }
00303 
00304     if (lpwfs->download_in_progress != NULL)
00305     {
00306         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
00307         goto lend;
00308     }
00309 
00310     if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
00311     {
00312         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
00313         goto lend;
00314     }
00315 
00316     hIC = lpwfs->lpAppInfo;
00317     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
00318     {
00319         WORKREQUEST workRequest;
00320         struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
00321 
00322         workRequest.asyncproc = AsyncFtpPutFileProc;
00323         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
00324         req->lpszLocalFile = heap_strdupW(lpszLocalFile);
00325         req->lpszNewRemoteFile = heap_strdupW(lpszNewRemoteFile);
00326     req->dwFlags = dwFlags;
00327     req->dwContext = dwContext;
00328 
00329     r = res_to_le(INTERNET_AsyncCall(&workRequest));
00330     }
00331     else
00332     {
00333         r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
00334         lpszNewRemoteFile, dwFlags, dwContext);
00335     }
00336 
00337 lend:
00338     WININET_Release( &lpwfs->hdr );
00339 
00340     return r;
00341 }
00342 
00343 /***********************************************************************
00344  *           FTP_FtpPutFileW (Internal)
00345  *
00346  * Uploads a file to the FTP server
00347  *
00348  * RETURNS
00349  *    TRUE on success
00350  *    FALSE on failure
00351  *
00352  */
00353 static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
00354     LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
00355 {
00356     HANDLE hFile;
00357     BOOL bSuccess = FALSE;
00358     appinfo_t *hIC = NULL;
00359     INT nResCode;
00360 
00361     TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
00362 
00363     /* Clear any error information */
00364     INTERNET_SetLastError(0);
00365 
00366     /* Open file to be uploaded */
00367     if (INVALID_HANDLE_VALUE ==
00368         (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
00369         /* Let CreateFile set the appropriate error */
00370         return FALSE;
00371 
00372     hIC = lpwfs->lpAppInfo;
00373 
00374     SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
00375 
00376     if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
00377     {
00378         INT nDataSocket;
00379 
00380         /* Get data socket to server */
00381         if (FTP_GetDataSocket(lpwfs, &nDataSocket))
00382         {
00383             FTP_SendData(lpwfs, nDataSocket, hFile);
00384             closesocket(nDataSocket);
00385         nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
00386         if (nResCode)
00387         {
00388             if (nResCode == 226)
00389             bSuccess = TRUE;
00390         else
00391             FTP_SetResponseError(nResCode);
00392         }
00393         }
00394     }
00395 
00396     if (lpwfs->lstnSocket != -1)
00397     {
00398         closesocket(lpwfs->lstnSocket);
00399         lpwfs->lstnSocket = -1;
00400     }
00401 
00402     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
00403     {
00404         INTERNET_ASYNC_RESULT iar;
00405 
00406         iar.dwResult = (DWORD)bSuccess;
00407         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
00408         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
00409             &iar, sizeof(INTERNET_ASYNC_RESULT));
00410     }
00411 
00412     CloseHandle(hFile);
00413 
00414     return bSuccess;
00415 }
00416 
00417 
00418 /***********************************************************************
00419  *           FtpSetCurrentDirectoryA (WININET.@)
00420  *
00421  * Change the working directory on the FTP server
00422  *
00423  * RETURNS
00424  *    TRUE on success
00425  *    FALSE on failure
00426  *
00427  */
00428 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
00429 {
00430     LPWSTR lpwzDirectory;
00431     BOOL ret;
00432     
00433     lpwzDirectory = heap_strdupAtoW(lpszDirectory);
00434     ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
00435     HeapFree(GetProcessHeap(), 0, lpwzDirectory);
00436     return ret;
00437 }
00438 
00439 
00440 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
00441 {
00442     struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
00443     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
00444 
00445     TRACE("%p\n", lpwfs);
00446 
00447     FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
00448     HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
00449 }
00450 
00451 /***********************************************************************
00452  *           FtpSetCurrentDirectoryW (WININET.@)
00453  *
00454  * Change the working directory on the FTP server
00455  *
00456  * RETURNS
00457  *    TRUE on success
00458  *    FALSE on failure
00459  *
00460  */
00461 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
00462 {
00463     ftp_session_t *lpwfs = NULL;
00464     appinfo_t *hIC = NULL;
00465     BOOL r = FALSE;
00466 
00467     if (!lpszDirectory)
00468     {
00469         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
00470         goto lend;
00471     }
00472 
00473     lpwfs = (ftp_session_t*) get_handle_object( hConnect );
00474     if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
00475     {
00476         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
00477         goto lend;
00478     }
00479 
00480     if (lpwfs->download_in_progress != NULL)
00481     {
00482         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
00483         goto lend;
00484     }
00485 
00486     TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
00487 
00488     hIC = lpwfs->lpAppInfo;
00489     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
00490     {
00491         WORKREQUEST workRequest;
00492         struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
00493 
00494         workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
00495         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
00496         req = &workRequest.u.FtpSetCurrentDirectoryW;
00497         req->lpszDirectory = heap_strdupW(lpszDirectory);
00498 
00499     r = res_to_le(INTERNET_AsyncCall(&workRequest));
00500     }
00501     else
00502     {
00503         r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
00504     }
00505 
00506 lend:
00507     if( lpwfs )
00508         WININET_Release( &lpwfs->hdr );
00509 
00510     return r;
00511 }
00512 
00513 
00514 /***********************************************************************
00515  *           FTP_FtpSetCurrentDirectoryW (Internal)
00516  *
00517  * Change the working directory on the FTP server
00518  *
00519  * RETURNS
00520  *    TRUE on success
00521  *    FALSE on failure
00522  *
00523  */
00524 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
00525 {
00526     INT nResCode;
00527     appinfo_t *hIC = NULL;
00528     DWORD bSuccess = FALSE;
00529 
00530     TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
00531 
00532     /* Clear any error information */
00533     INTERNET_SetLastError(0);
00534 
00535     hIC = lpwfs->lpAppInfo;
00536     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
00537         lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
00538         goto lend;
00539 
00540     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
00541 
00542     if (nResCode)
00543     {
00544         if (nResCode == 250)
00545             bSuccess = TRUE;
00546         else
00547             FTP_SetResponseError(nResCode);
00548     }
00549 
00550 lend:
00551     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
00552     {
00553         INTERNET_ASYNC_RESULT iar;
00554 
00555         iar.dwResult = bSuccess;
00556         iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
00557         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
00558             &iar, sizeof(INTERNET_ASYNC_RESULT));
00559     }
00560     return bSuccess;
00561 }
00562 
00563 
00564 /***********************************************************************
00565  *           FtpCreateDirectoryA (WININET.@)
00566  *
00567  * Create new directory on the FTP server
00568  *
00569  * RETURNS
00570  *    TRUE on success
00571  *    FALSE on failure
00572  *
00573  */
00574 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
00575 {
00576     LPWSTR lpwzDirectory;
00577     BOOL ret;
00578     
00579     lpwzDirectory = heap_strdupAtoW(lpszDirectory);
00580     ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
00581     HeapFree(GetProcessHeap(), 0, lpwzDirectory);
00582     return ret;
00583 }
00584 
00585 
00586 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
00587 {
00588     struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
00589     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
00590 
00591     TRACE(" %p\n", lpwfs);
00592 
00593     FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
00594     HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
00595 }
00596 
00597 /***********************************************************************
00598  *           FtpCreateDirectoryW (WININET.@)
00599  *
00600  * Create new directory on the FTP server
00601  *
00602  * RETURNS
00603  *    TRUE on success
00604  *    FALSE on failure
00605  *
00606  */
00607 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
00608 {
00609     ftp_session_t *lpwfs;
00610     appinfo_t *hIC = NULL;
00611     BOOL r = FALSE;
00612 
00613     lpwfs = (ftp_session_t*) get_handle_object( hConnect );
00614     if (!lpwfs)
00615     {
00616         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
00617         return FALSE;
00618     }
00619 
00620     if (WH_HFTPSESSION != lpwfs->hdr.htype)
00621     {
00622         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
00623         goto lend;
00624     }
00625 
00626     if (lpwfs->download_in_progress != NULL)
00627     {
00628         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
00629         goto lend;
00630     }
00631 
00632     if (!lpszDirectory)
00633     {
00634         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
00635         goto lend;
00636     }
00637 
00638     hIC = lpwfs->lpAppInfo;
00639     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
00640     {
00641         WORKREQUEST workRequest;
00642         struct WORKREQ_FTPCREATEDIRECTORYW *req;
00643 
00644         workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
00645         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
00646         req = &workRequest.u.FtpCreateDirectoryW;
00647         req->lpszDirectory = heap_strdupW(lpszDirectory);
00648 
00649     r = res_to_le(INTERNET_AsyncCall(&workRequest));
00650     }
00651     else
00652     {
00653         r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
00654     }
00655 lend:
00656     WININET_Release( &lpwfs->hdr );
00657 
00658     return r;
00659 }
00660 
00661 
00662 /***********************************************************************
00663  *           FTP_FtpCreateDirectoryW (Internal)
00664  *
00665  * Create new directory on the FTP server
00666  *
00667  * RETURNS
00668  *    TRUE on success
00669  *    FALSE on failure
00670  *
00671  */
00672 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
00673 {
00674     INT nResCode;
00675     BOOL bSuccess = FALSE;
00676     appinfo_t *hIC = NULL;
00677 
00678     TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
00679 
00680     /* Clear any error information */
00681     INTERNET_SetLastError(0);
00682 
00683     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
00684         goto lend;
00685 
00686     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
00687     if (nResCode)
00688     {
00689         if (nResCode == 257)
00690             bSuccess = TRUE;
00691         else
00692             FTP_SetResponseError(nResCode);
00693     }
00694 
00695 lend:
00696     hIC = lpwfs->lpAppInfo;
00697     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
00698     {
00699         INTERNET_ASYNC_RESULT iar;
00700 
00701         iar.dwResult = (DWORD)bSuccess;
00702         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
00703         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
00704             &iar, sizeof(INTERNET_ASYNC_RESULT));
00705     }
00706 
00707     return bSuccess;
00708 }
00709 
00710 /***********************************************************************
00711  *           FtpFindFirstFileA (WININET.@)
00712  *
00713  * Search the specified directory
00714  *
00715  * RETURNS
00716  *    HINTERNET on success
00717  *    NULL on failure
00718  *
00719  */
00720 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
00721     LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
00722 {
00723     LPWSTR lpwzSearchFile;
00724     WIN32_FIND_DATAW wfd;
00725     LPWIN32_FIND_DATAW lpFindFileDataW;
00726     HINTERNET ret;
00727     
00728     lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
00729     lpFindFileDataW = lpFindFileData?&wfd:NULL;
00730     ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
00731     HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
00732     
00733     if (ret && lpFindFileData)
00734         WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
00735 
00736     return ret;
00737 }
00738 
00739 
00740 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
00741 {
00742     struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
00743     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
00744 
00745     TRACE("%p\n", lpwfs);
00746 
00747     FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
00748        req->lpFindFileData, req->dwFlags, req->dwContext);
00749     HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
00750 }
00751 
00752 /***********************************************************************
00753  *           FtpFindFirstFileW (WININET.@)
00754  *
00755  * Search the specified directory
00756  *
00757  * RETURNS
00758  *    HINTERNET on success
00759  *    NULL on failure
00760  *
00761  */
00762 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
00763     LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
00764 {
00765     ftp_session_t *lpwfs;
00766     appinfo_t *hIC = NULL;
00767     HINTERNET r = NULL;
00768 
00769     lpwfs = (ftp_session_t*) get_handle_object( hConnect );
00770     if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
00771     {
00772         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
00773         goto lend;
00774     }
00775 
00776     if (lpwfs->download_in_progress != NULL)
00777     {
00778         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
00779         goto lend;
00780     }
00781 
00782     hIC = lpwfs->lpAppInfo;
00783     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
00784     {
00785         WORKREQUEST workRequest;
00786         struct WORKREQ_FTPFINDFIRSTFILEW *req;
00787 
00788         workRequest.asyncproc = AsyncFtpFindFirstFileProc;
00789         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
00790         req = &workRequest.u.FtpFindFirstFileW;
00791         req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : heap_strdupW(lpszSearchFile);
00792     req->lpFindFileData = lpFindFileData;
00793     req->dwFlags = dwFlags;
00794     req->dwContext= dwContext;
00795 
00796     INTERNET_AsyncCall(&workRequest);
00797     r = NULL;
00798     }
00799     else
00800     {
00801         r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
00802             dwFlags, dwContext);
00803     }
00804 lend:
00805     if( lpwfs )
00806         WININET_Release( &lpwfs->hdr );
00807 
00808     return r;
00809 }
00810 
00811 
00812 /***********************************************************************
00813  *           FTP_FtpFindFirstFileW (Internal)
00814  *
00815  * Search the specified directory
00816  *
00817  * RETURNS
00818  *    HINTERNET on success
00819  *    NULL on failure
00820  *
00821  */
00822 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
00823     LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
00824 {
00825     INT nResCode;
00826     appinfo_t *hIC = NULL;
00827     HINTERNET hFindNext = NULL;
00828 
00829     TRACE("\n");
00830 
00831     /* Clear any error information */
00832     INTERNET_SetLastError(0);
00833 
00834     if (!FTP_InitListenSocket(lpwfs))
00835         goto lend;
00836 
00837     if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
00838         goto lend;
00839 
00840     if (!FTP_SendPortOrPasv(lpwfs))
00841         goto lend;
00842 
00843     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
00844         lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
00845         goto lend;
00846 
00847     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
00848     if (nResCode)
00849     {
00850         if (nResCode == 125 || nResCode == 150)
00851         {
00852             INT nDataSocket;
00853 
00854             /* Get data socket to server */
00855             if (FTP_GetDataSocket(lpwfs, &nDataSocket))
00856             {
00857                 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
00858                 closesocket(nDataSocket);
00859                 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
00860                 if (nResCode != 226 && nResCode != 250)
00861                     INTERNET_SetLastError(ERROR_NO_MORE_FILES);
00862             }
00863         }
00864         else
00865             FTP_SetResponseError(nResCode);
00866     }
00867 
00868 lend:
00869     if (lpwfs->lstnSocket != -1)
00870     {
00871         closesocket(lpwfs->lstnSocket);
00872         lpwfs->lstnSocket = -1;
00873     }
00874 
00875     hIC = lpwfs->lpAppInfo;
00876     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
00877     {
00878         INTERNET_ASYNC_RESULT iar;
00879 
00880         if (hFindNext)
00881     {
00882             iar.dwResult = (DWORD_PTR)hFindNext;
00883             iar.dwError = ERROR_SUCCESS;
00884             SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
00885                 &iar, sizeof(INTERNET_ASYNC_RESULT));
00886     }
00887 
00888         iar.dwResult = (DWORD_PTR)hFindNext;
00889         iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
00890         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
00891             &iar, sizeof(INTERNET_ASYNC_RESULT));
00892     }
00893 
00894     return hFindNext;
00895 }
00896 
00897 
00898 /***********************************************************************
00899  *           FtpGetCurrentDirectoryA (WININET.@)
00900  *
00901  * Retrieves the current directory
00902  *
00903  * RETURNS
00904  *    TRUE on success
00905  *    FALSE on failure
00906  *
00907  */
00908 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
00909     LPDWORD lpdwCurrentDirectory)
00910 {
00911     WCHAR *dir = NULL;
00912     DWORD len;
00913     BOOL ret;
00914 
00915     if(lpdwCurrentDirectory) {
00916         len = *lpdwCurrentDirectory;
00917         if(lpszCurrentDirectory)
00918         {
00919             dir = heap_alloc(len * sizeof(WCHAR));
00920             if (NULL == dir)
00921             {
00922                 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
00923                 return FALSE;
00924             }
00925         }
00926     }
00927     ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
00928 
00929     if (ret && lpszCurrentDirectory)
00930         WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
00931 
00932     if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
00933     HeapFree(GetProcessHeap(), 0, dir);
00934     return ret;
00935 }
00936 
00937 
00938 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
00939 {
00940     struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
00941     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
00942 
00943     TRACE("%p\n", lpwfs);
00944 
00945     FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
00946 }
00947 
00948 /***********************************************************************
00949  *           FtpGetCurrentDirectoryW (WININET.@)
00950  *
00951  * Retrieves the current directory
00952  *
00953  * RETURNS
00954  *    TRUE on success
00955  *    FALSE on failure
00956  *
00957  */
00958 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
00959     LPDWORD lpdwCurrentDirectory)
00960 {
00961     ftp_session_t *lpwfs;
00962     appinfo_t *hIC = NULL;
00963     BOOL r = FALSE;
00964 
00965     TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
00966 
00967     lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
00968     if (NULL == lpwfs)
00969     {
00970         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
00971         goto lend;
00972     }
00973 
00974     if (WH_HFTPSESSION != lpwfs->hdr.htype)
00975     {
00976         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
00977         goto lend;
00978     }
00979 
00980     if (!lpdwCurrentDirectory)
00981     {
00982         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
00983         goto lend;
00984     }
00985 
00986     if (lpszCurrentDirectory == NULL)
00987     {
00988         INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
00989         goto lend;
00990     }
00991 
00992     if (lpwfs->download_in_progress != NULL)
00993     {
00994         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
00995         goto lend;
00996     }
00997 
00998     hIC = lpwfs->lpAppInfo;
00999     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
01000     {
01001         WORKREQUEST workRequest;
01002         struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
01003 
01004         workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
01005         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
01006         req = &workRequest.u.FtpGetCurrentDirectoryW;
01007     req->lpszDirectory = lpszCurrentDirectory;
01008     req->lpdwDirectory = lpdwCurrentDirectory;
01009 
01010     r = res_to_le(INTERNET_AsyncCall(&workRequest));
01011     }
01012     else
01013     {
01014         r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
01015             lpdwCurrentDirectory);
01016     }
01017 
01018 lend:
01019     if( lpwfs )
01020         WININET_Release( &lpwfs->hdr );
01021 
01022     return r;
01023 }
01024 
01025 
01026 /***********************************************************************
01027  *           FTP_FtpGetCurrentDirectoryW (Internal)
01028  *
01029  * Retrieves the current directory
01030  *
01031  * RETURNS
01032  *    TRUE on success
01033  *    FALSE on failure
01034  *
01035  */
01036 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
01037     LPDWORD lpdwCurrentDirectory)
01038 {
01039     INT nResCode;
01040     appinfo_t *hIC = NULL;
01041     DWORD bSuccess = FALSE;
01042 
01043     /* Clear any error information */
01044     INTERNET_SetLastError(0);
01045 
01046     hIC = lpwfs->lpAppInfo;
01047     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
01048         lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
01049         goto lend;
01050 
01051     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
01052     if (nResCode)
01053     {
01054         if (nResCode == 257) /* Extract directory name */
01055         {
01056             DWORD firstpos, lastpos, len;
01057             LPWSTR lpszResponseBuffer = heap_strdupAtoW(INTERNET_GetResponseBuffer());
01058 
01059             for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
01060             {
01061                 if ('"' == lpszResponseBuffer[lastpos])
01062                 {
01063                     if (!firstpos)
01064                         firstpos = lastpos;
01065                     else
01066                         break;
01067                 }
01068             }
01069             len = lastpos - firstpos;
01070             if (*lpdwCurrentDirectory >= len)
01071             {
01072                 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
01073                 lpszCurrentDirectory[len - 1] = 0;
01074                 *lpdwCurrentDirectory = len;
01075                 bSuccess = TRUE;
01076             }
01077             else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
01078 
01079             HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
01080         }
01081         else
01082             FTP_SetResponseError(nResCode);
01083     }
01084 
01085 lend:
01086     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
01087     {
01088         INTERNET_ASYNC_RESULT iar;
01089 
01090         iar.dwResult = bSuccess;
01091         iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
01092         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
01093             &iar, sizeof(INTERNET_ASYNC_RESULT));
01094     }
01095 
01096     return bSuccess;
01097 }
01098 
01099 
01100 /***********************************************************************
01101  *           FTPFILE_Destroy(internal)
01102  *
01103  * Closes the file transfer handle. This also 'cleans' the data queue of
01104  * the 'transfer complete' message (this is a bit of a hack though :-/ )
01105  *
01106  */
01107 static void FTPFILE_Destroy(object_header_t *hdr)
01108 {
01109     ftp_file_t *lpwh = (ftp_file_t*) hdr;
01110     ftp_session_t *lpwfs = lpwh->lpFtpSession;
01111     INT nResCode;
01112 
01113     TRACE("\n");
01114 
01115     if (lpwh->cache_file_handle != INVALID_HANDLE_VALUE)
01116         CloseHandle(lpwh->cache_file_handle);
01117 
01118     HeapFree(GetProcessHeap(), 0, lpwh->cache_file);
01119 
01120     if (!lpwh->session_deleted)
01121         lpwfs->download_in_progress = NULL;
01122 
01123     if (lpwh->nDataSocket != -1)
01124         closesocket(lpwh->nDataSocket);
01125 
01126     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
01127     if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
01128 
01129     WININET_Release(&lpwh->lpFtpSession->hdr);
01130 }
01131 
01132 static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
01133 {
01134     switch(option) {
01135     case INTERNET_OPTION_HANDLE_TYPE:
01136         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
01137 
01138         if (*size < sizeof(ULONG))
01139             return ERROR_INSUFFICIENT_BUFFER;
01140 
01141         *size = sizeof(DWORD);
01142         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
01143         return ERROR_SUCCESS;
01144     case INTERNET_OPTION_DATAFILE_NAME:
01145     {
01146         DWORD required;
01147         ftp_file_t *file = (ftp_file_t *)hdr;
01148 
01149         TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
01150 
01151         if (!file->cache_file)
01152         {
01153             *size = 0;
01154             return ERROR_INTERNET_ITEM_NOT_FOUND;
01155         }
01156         if (unicode)
01157         {
01158             required = (lstrlenW(file->cache_file) + 1) * sizeof(WCHAR);
01159             if (*size < required)
01160                 return ERROR_INSUFFICIENT_BUFFER;
01161 
01162             *size = required;
01163             memcpy(buffer, file->cache_file, *size);
01164             return ERROR_SUCCESS;
01165         }
01166         else
01167         {
01168             required = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, NULL, 0, NULL, NULL);
01169             if (required > *size)
01170                 return ERROR_INSUFFICIENT_BUFFER;
01171 
01172             *size = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, buffer, *size, NULL, NULL);
01173             return ERROR_SUCCESS;
01174         }
01175     }
01176     }
01177     return INET_QueryOption(hdr, option, buffer, size, unicode);
01178 }
01179 
01180 static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
01181 {
01182     ftp_file_t *file = (ftp_file_t*)hdr;
01183     int res;
01184     DWORD error;
01185 
01186     if (file->nDataSocket == -1)
01187         return ERROR_INTERNET_DISCONNECTED;
01188 
01189     /* FIXME: FTP should use NETCON_ stuff */
01190     res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
01191     *read = res>0 ? res : 0;
01192 
01193     error = res >= 0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME */
01194     if (error == ERROR_SUCCESS && file->cache_file)
01195     {
01196         DWORD bytes_written;
01197 
01198         if (!WriteFile(file->cache_file_handle, buffer, *read, &bytes_written, NULL))
01199             WARN("WriteFile failed: %u\n", GetLastError());
01200     }
01201     return error;
01202 }
01203 
01204 static DWORD FTPFILE_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
01205     DWORD flags, DWORD_PTR context)
01206 {
01207     return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
01208 }
01209 
01210 static DWORD FTPFILE_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
01211     DWORD flags, DWORD_PTR context)
01212 {
01213     return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
01214 }
01215 
01216 static DWORD FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
01217 {
01218     ftp_file_t *lpwh = (ftp_file_t*) hdr;
01219     int res;
01220 
01221     res = send(lpwh->nDataSocket, buffer, size, 0);
01222 
01223     *written = res>0 ? res : 0;
01224     return res >= 0 ? ERROR_SUCCESS : sock_get_error(errno);
01225 }
01226 
01227 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
01228 {
01229     INTERNET_ASYNC_RESULT iar;
01230     BYTE buffer[4096];
01231     int available;
01232 
01233     TRACE("%p\n", file);
01234 
01235     available = recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
01236 
01237     if(available != -1) {
01238         iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
01239         iar.dwError = first_notif ? 0 : available;
01240     }else {
01241         iar.dwResult = 0;
01242         iar.dwError = INTERNET_GetLastError();
01243     }
01244 
01245     INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
01246                           sizeof(INTERNET_ASYNC_RESULT));
01247 }
01248 
01249 static void FTPFILE_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
01250 {
01251     ftp_file_t *file = (ftp_file_t*)workRequest->hdr;
01252 
01253     FTP_ReceiveRequestData(file, FALSE);
01254 }
01255 
01256 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
01257 {
01258     ftp_file_t *file = (ftp_file_t*) hdr;
01259     int retval, unread = 0;
01260 
01261     TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
01262 
01263 #ifdef FIONREAD
01264     retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
01265     if (!retval)
01266         TRACE("%d bytes of queued, but unread data\n", unread);
01267 #else
01268     FIXME("FIONREAD not available\n");
01269 #endif
01270 
01271     *available = unread;
01272 
01273     if(!unread) {
01274         BYTE byte;
01275 
01276         *available = 0;
01277 
01278         retval = recv(file->nDataSocket, &byte, 1, MSG_PEEK);
01279         if(retval > 0) {
01280             WORKREQUEST workRequest;
01281 
01282             *available = 0;
01283             workRequest.asyncproc = FTPFILE_AsyncQueryDataAvailableProc;
01284             workRequest.hdr = WININET_AddRef( &file->hdr );
01285 
01286             INTERNET_AsyncCall(&workRequest);
01287 
01288             return ERROR_IO_PENDING;
01289         }
01290     }
01291 
01292     return ERROR_SUCCESS;
01293 }
01294 
01295 
01296 static const object_vtbl_t FTPFILEVtbl = {
01297     FTPFILE_Destroy,
01298     NULL,
01299     FTPFILE_QueryOption,
01300     NULL,
01301     FTPFILE_ReadFile,
01302     FTPFILE_ReadFileExA,
01303     FTPFILE_ReadFileExW,
01304     FTPFILE_WriteFile,
01305     FTPFILE_QueryDataAvailable,
01306     NULL
01307 };
01308 
01309 /***********************************************************************
01310  *           FTP_FtpOpenFileW (Internal)
01311  *
01312  * Open a remote file for writing or reading
01313  *
01314  * RETURNS
01315  *    HINTERNET handle on success
01316  *    NULL on failure
01317  *
01318  */
01319 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
01320     LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
01321     DWORD_PTR dwContext)
01322 {
01323     INT nDataSocket;
01324     BOOL bSuccess = FALSE;
01325     ftp_file_t *lpwh = NULL;
01326     appinfo_t *hIC = NULL;
01327 
01328     TRACE("\n");
01329 
01330     /* Clear any error information */
01331     INTERNET_SetLastError(0);
01332 
01333     if (GENERIC_READ == fdwAccess)
01334     {
01335         /* Set up socket to retrieve data */
01336         bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
01337     }
01338     else if (GENERIC_WRITE == fdwAccess)
01339     {
01340         /* Set up socket to send data */
01341         bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
01342     }
01343 
01344     /* Get data socket to server */
01345     if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
01346     {
01347         lpwh = alloc_object(&lpwfs->hdr, &FTPFILEVtbl, sizeof(ftp_file_t));
01348         lpwh->hdr.htype = WH_HFILE;
01349         lpwh->hdr.dwFlags = dwFlags;
01350         lpwh->hdr.dwContext = dwContext;
01351         lpwh->nDataSocket = nDataSocket;
01352         lpwh->cache_file = NULL;
01353         lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
01354         lpwh->session_deleted = FALSE;
01355 
01356         WININET_AddRef( &lpwfs->hdr );
01357         lpwh->lpFtpSession = lpwfs;
01358         list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
01359     
01360     /* Indicate that a download is currently in progress */
01361     lpwfs->download_in_progress = lpwh;
01362     }
01363 
01364     if (lpwfs->lstnSocket != -1)
01365     {
01366         closesocket(lpwfs->lstnSocket);
01367         lpwfs->lstnSocket = -1;
01368     }
01369 
01370     if (bSuccess && fdwAccess == GENERIC_READ)
01371     {
01372         WCHAR filename[MAX_PATH + 1];
01373         URL_COMPONENTSW uc;
01374         DWORD len;
01375 
01376         memset(&uc, 0, sizeof(uc));
01377         uc.dwStructSize = sizeof(uc);
01378         uc.nScheme      = INTERNET_SCHEME_FTP;
01379         uc.lpszHostName = lpwfs->servername;
01380         uc.nPort        = lpwfs->serverport;
01381         uc.lpszUserName = lpwfs->lpszUserName;
01382         uc.lpszUrlPath  = heap_strdupW(lpszFileName);
01383 
01384         if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
01385         {
01386             WCHAR *url = heap_alloc(len * sizeof(WCHAR));
01387 
01388             if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
01389             {
01390                 lpwh->cache_file = heap_strdupW(filename);
01391                 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
01392                                                       NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
01393                 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
01394                 {
01395                     WARN("Could not create cache file: %u\n", GetLastError());
01396                     HeapFree(GetProcessHeap(), 0, lpwh->cache_file);
01397                     lpwh->cache_file = NULL;
01398                 }
01399             }
01400             HeapFree(GetProcessHeap(), 0, url);
01401         }
01402         HeapFree(GetProcessHeap(), 0, uc.lpszUrlPath);
01403     }
01404 
01405     hIC = lpwfs->lpAppInfo;
01406     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
01407     {
01408         INTERNET_ASYNC_RESULT iar;
01409 
01410     if (lpwh)
01411     {
01412             iar.dwResult = (DWORD_PTR)lpwh->hdr.hInternet;
01413             iar.dwError = ERROR_SUCCESS;
01414             SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
01415                 &iar, sizeof(INTERNET_ASYNC_RESULT));
01416     }
01417 
01418         if(bSuccess) {
01419             FTP_ReceiveRequestData(lpwh, TRUE);
01420         }else {
01421             iar.dwResult = 0;
01422             iar.dwError = INTERNET_GetLastError();
01423             SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
01424                     &iar, sizeof(INTERNET_ASYNC_RESULT));
01425         }
01426     }
01427 
01428     if(!bSuccess) {
01429         if(lpwh)
01430             WININET_Release( &lpwh->hdr );
01431         return FALSE;
01432     }
01433 
01434     return lpwh->hdr.hInternet;
01435 }
01436 
01437 
01438 /***********************************************************************
01439  *           FtpOpenFileA (WININET.@)
01440  *
01441  * Open a remote file for writing or reading
01442  *
01443  * RETURNS
01444  *    HINTERNET handle on success
01445  *    NULL on failure
01446  *
01447  */
01448 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
01449     LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
01450     DWORD_PTR dwContext)
01451 {
01452     LPWSTR lpwzFileName;
01453     HINTERNET ret;
01454 
01455     lpwzFileName = heap_strdupAtoW(lpszFileName);
01456     ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
01457     HeapFree(GetProcessHeap(), 0, lpwzFileName);
01458     return ret;
01459 }
01460 
01461 
01462 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
01463 {
01464     struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
01465     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
01466 
01467     TRACE("%p\n", lpwfs);
01468 
01469     FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
01470         req->dwAccess, req->dwFlags, req->dwContext);
01471     HeapFree(GetProcessHeap(), 0, req->lpszFilename);
01472 }
01473 
01474 /***********************************************************************
01475  *           FtpOpenFileW (WININET.@)
01476  *
01477  * Open a remote file for writing or reading
01478  *
01479  * RETURNS
01480  *    HINTERNET handle on success
01481  *    NULL on failure
01482  *
01483  */
01484 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
01485     LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
01486     DWORD_PTR dwContext)
01487 {
01488     ftp_session_t *lpwfs;
01489     appinfo_t *hIC = NULL;
01490     HINTERNET r = NULL;
01491 
01492     TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
01493         debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
01494 
01495     lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
01496     if (!lpwfs)
01497     {
01498         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
01499         return FALSE;
01500     }
01501 
01502     if (WH_HFTPSESSION != lpwfs->hdr.htype)
01503     {
01504         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
01505         goto lend;
01506     }
01507 
01508     if ((!lpszFileName) ||
01509         ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
01510         ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
01511     {
01512         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
01513         goto lend;
01514     }
01515 
01516     if (lpwfs->download_in_progress != NULL)
01517     {
01518         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
01519         goto lend;
01520     }
01521 
01522     hIC = lpwfs->lpAppInfo;
01523     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
01524     {
01525         WORKREQUEST workRequest;
01526         struct WORKREQ_FTPOPENFILEW *req;
01527 
01528         workRequest.asyncproc = AsyncFtpOpenFileProc;
01529         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
01530         req = &workRequest.u.FtpOpenFileW;
01531     req->lpszFilename = heap_strdupW(lpszFileName);
01532     req->dwAccess = fdwAccess;
01533     req->dwFlags = dwFlags;
01534     req->dwContext = dwContext;
01535 
01536     INTERNET_AsyncCall(&workRequest);
01537     r = NULL;
01538     }
01539     else
01540     {
01541     r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
01542     }
01543 
01544 lend:
01545     WININET_Release( &lpwfs->hdr );
01546 
01547     return r;
01548 }
01549 
01550 
01551 /***********************************************************************
01552  *           FtpGetFileA (WININET.@)
01553  *
01554  * Retrieve file from the FTP server
01555  *
01556  * RETURNS
01557  *    TRUE on success
01558  *    FALSE on failure
01559  *
01560  */
01561 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
01562     BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
01563     DWORD_PTR dwContext)
01564 {
01565     LPWSTR lpwzRemoteFile;
01566     LPWSTR lpwzNewFile;
01567     BOOL ret;
01568     
01569     lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
01570     lpwzNewFile = heap_strdupAtoW(lpszNewFile);
01571     ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
01572         dwLocalFlagsAttribute, dwInternetFlags, dwContext);
01573     HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
01574     HeapFree(GetProcessHeap(), 0, lpwzNewFile);
01575     return ret;
01576 }
01577 
01578 
01579 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
01580 {
01581     struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
01582     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
01583 
01584     TRACE("%p\n", lpwfs);
01585 
01586     FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
01587              req->lpszNewFile, req->fFailIfExists,
01588              req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
01589     HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
01590     HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
01591 }
01592 
01593 
01594 /***********************************************************************
01595  *           FtpGetFileW (WININET.@)
01596  *
01597  * Retrieve file from the FTP server
01598  *
01599  * RETURNS
01600  *    TRUE on success
01601  *    FALSE on failure
01602  *
01603  */
01604 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
01605     BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
01606     DWORD_PTR dwContext)
01607 {
01608     ftp_session_t *lpwfs;
01609     appinfo_t *hIC = NULL;
01610     BOOL r = FALSE;
01611 
01612     if (!lpszRemoteFile || !lpszNewFile)
01613     {
01614         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
01615         return FALSE;
01616     }
01617 
01618     lpwfs = (ftp_session_t*) get_handle_object( hInternet );
01619     if (!lpwfs)
01620     {
01621         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
01622         return FALSE;
01623     }
01624 
01625     if (WH_HFTPSESSION != lpwfs->hdr.htype)
01626     {
01627         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
01628         goto lend;
01629     }
01630 
01631     if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
01632     {
01633         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
01634         goto lend;
01635     }
01636 
01637     if (lpwfs->download_in_progress != NULL)
01638     {
01639         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
01640         goto lend;
01641     }
01642     
01643     hIC = lpwfs->lpAppInfo;
01644     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
01645     {
01646         WORKREQUEST workRequest;
01647         struct WORKREQ_FTPGETFILEW *req;
01648 
01649         workRequest.asyncproc = AsyncFtpGetFileProc;
01650         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
01651         req = &workRequest.u.FtpGetFileW;
01652         req->lpszRemoteFile = heap_strdupW(lpszRemoteFile);
01653         req->lpszNewFile = heap_strdupW(lpszNewFile);
01654     req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
01655     req->fFailIfExists = fFailIfExists;
01656     req->dwFlags = dwInternetFlags;
01657     req->dwContext = dwContext;
01658 
01659     r = res_to_le(INTERNET_AsyncCall(&workRequest));
01660     }
01661     else
01662     {
01663         r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
01664            fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
01665     }
01666 
01667 lend:
01668     WININET_Release( &lpwfs->hdr );
01669 
01670     return r;
01671 }
01672 
01673 
01674 /***********************************************************************
01675  *           FTP_FtpGetFileW (Internal)
01676  *
01677  * Retrieve file from the FTP server
01678  *
01679  * RETURNS
01680  *    TRUE on success
01681  *    FALSE on failure
01682  *
01683  */
01684 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
01685     BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
01686     DWORD_PTR dwContext)
01687 {
01688     BOOL bSuccess = FALSE;
01689     HANDLE hFile;
01690     appinfo_t *hIC = NULL;
01691 
01692     TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
01693 
01694     /* Clear any error information */
01695     INTERNET_SetLastError(0);
01696 
01697     /* Ensure we can write to lpszNewfile by opening it */
01698     hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
01699         CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
01700     if (INVALID_HANDLE_VALUE == hFile)
01701         return FALSE;
01702 
01703     /* Set up socket to retrieve data */
01704     if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
01705     {
01706         INT nDataSocket;
01707 
01708         /* Get data socket to server */
01709         if (FTP_GetDataSocket(lpwfs, &nDataSocket))
01710         {
01711             INT nResCode;
01712 
01713             /* Receive data */
01714             FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
01715             closesocket(nDataSocket);
01716 
01717             nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
01718             if (nResCode)
01719             {
01720                 if (nResCode == 226)
01721                     bSuccess = TRUE;
01722                 else
01723                     FTP_SetResponseError(nResCode);
01724             }
01725         }
01726     }
01727 
01728     if (lpwfs->lstnSocket != -1)
01729     {
01730         closesocket(lpwfs->lstnSocket);
01731         lpwfs->lstnSocket = -1;
01732     }
01733 
01734     CloseHandle(hFile);
01735 
01736     hIC = lpwfs->lpAppInfo;
01737     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
01738     {
01739         INTERNET_ASYNC_RESULT iar;
01740 
01741         iar.dwResult = (DWORD)bSuccess;
01742         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
01743         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
01744             &iar, sizeof(INTERNET_ASYNC_RESULT));
01745     }
01746 
01747     if (!bSuccess) DeleteFileW(lpszNewFile);
01748     return bSuccess;
01749 }
01750 
01751 /***********************************************************************
01752  *           FtpGetFileSize  (WININET.@)
01753  */
01754 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
01755 {
01756     FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
01757 
01758     if (lpdwFileSizeHigh)
01759         *lpdwFileSizeHigh = 0;
01760 
01761     return 0;
01762 }
01763 
01764 /***********************************************************************
01765  *           FtpDeleteFileA  (WININET.@)
01766  *
01767  * Delete a file on the ftp server
01768  *
01769  * RETURNS
01770  *    TRUE on success
01771  *    FALSE on failure
01772  *
01773  */
01774 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
01775 {
01776     LPWSTR lpwzFileName;
01777     BOOL ret;
01778     
01779     lpwzFileName = heap_strdupAtoW(lpszFileName);
01780     ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
01781     HeapFree(GetProcessHeap(), 0, lpwzFileName);
01782     return ret;
01783 }
01784 
01785 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
01786 {
01787     struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
01788     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
01789 
01790     TRACE("%p\n", lpwfs);
01791 
01792     FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
01793     HeapFree(GetProcessHeap(), 0, req->lpszFilename);
01794 }
01795 
01796 /***********************************************************************
01797  *           FtpDeleteFileW  (WININET.@)
01798  *
01799  * Delete a file on the ftp server
01800  *
01801  * RETURNS
01802  *    TRUE on success
01803  *    FALSE on failure
01804  *
01805  */
01806 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
01807 {
01808     ftp_session_t *lpwfs;
01809     appinfo_t *hIC = NULL;
01810     BOOL r = FALSE;
01811 
01812     lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
01813     if (!lpwfs)
01814     {
01815         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
01816         return FALSE;
01817     }
01818 
01819     if (WH_HFTPSESSION != lpwfs->hdr.htype)
01820     {
01821         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
01822         goto lend;
01823     }
01824 
01825     if (lpwfs->download_in_progress != NULL)
01826     {
01827         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
01828         goto lend;
01829     }
01830 
01831     if (!lpszFileName)
01832     {
01833         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
01834         goto lend;
01835     }
01836 
01837     hIC = lpwfs->lpAppInfo;
01838     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
01839     {
01840         WORKREQUEST workRequest;
01841         struct WORKREQ_FTPDELETEFILEW *req;
01842 
01843         workRequest.asyncproc = AsyncFtpDeleteFileProc;
01844         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
01845         req = &workRequest.u.FtpDeleteFileW;
01846         req->lpszFilename = heap_strdupW(lpszFileName);
01847 
01848     r = res_to_le(INTERNET_AsyncCall(&workRequest));
01849     }
01850     else
01851     {
01852         r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
01853     }
01854 
01855 lend:
01856     WININET_Release( &lpwfs->hdr );
01857 
01858     return r;
01859 }
01860 
01861 /***********************************************************************
01862  *           FTP_FtpDeleteFileW  (Internal)
01863  *
01864  * Delete a file on the ftp server
01865  *
01866  * RETURNS
01867  *    TRUE on success
01868  *    FALSE on failure
01869  *
01870  */
01871 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
01872 {
01873     INT nResCode;
01874     BOOL bSuccess = FALSE;
01875     appinfo_t *hIC = NULL;
01876 
01877     TRACE("%p\n", lpwfs);
01878 
01879     /* Clear any error information */
01880     INTERNET_SetLastError(0);
01881 
01882     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
01883         goto lend;
01884 
01885     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
01886     if (nResCode)
01887     {
01888         if (nResCode == 250)
01889             bSuccess = TRUE;
01890         else
01891             FTP_SetResponseError(nResCode);
01892     }
01893 lend:
01894     hIC = lpwfs->lpAppInfo;
01895     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
01896     {
01897         INTERNET_ASYNC_RESULT iar;
01898 
01899         iar.dwResult = (DWORD)bSuccess;
01900         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
01901         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
01902             &iar, sizeof(INTERNET_ASYNC_RESULT));
01903     }
01904 
01905     return bSuccess;
01906 }
01907 
01908 
01909 /***********************************************************************
01910  *           FtpRemoveDirectoryA  (WININET.@)
01911  *
01912  * Remove a directory on the ftp server
01913  *
01914  * RETURNS
01915  *    TRUE on success
01916  *    FALSE on failure
01917  *
01918  */
01919 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
01920 {
01921     LPWSTR lpwzDirectory;
01922     BOOL ret;
01923     
01924     lpwzDirectory = heap_strdupAtoW(lpszDirectory);
01925     ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
01926     HeapFree(GetProcessHeap(), 0, lpwzDirectory);
01927     return ret;
01928 }
01929 
01930 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
01931 {
01932     struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
01933     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
01934 
01935     TRACE("%p\n", lpwfs);
01936 
01937     FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
01938     HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
01939 }
01940 
01941 /***********************************************************************
01942  *           FtpRemoveDirectoryW  (WININET.@)
01943  *
01944  * Remove a directory on the ftp server
01945  *
01946  * RETURNS
01947  *    TRUE on success
01948  *    FALSE on failure
01949  *
01950  */
01951 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
01952 {
01953     ftp_session_t *lpwfs;
01954     appinfo_t *hIC = NULL;
01955     BOOL r = FALSE;
01956 
01957     lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
01958     if (!lpwfs)
01959     {
01960         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
01961         return FALSE;
01962     }
01963 
01964     if (WH_HFTPSESSION != lpwfs->hdr.htype)
01965     {
01966         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
01967         goto lend;
01968     }
01969 
01970     if (lpwfs->download_in_progress != NULL)
01971     {
01972         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
01973         goto lend;
01974     }
01975 
01976     if (!lpszDirectory)
01977     {
01978         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
01979         goto lend;
01980     }
01981 
01982     hIC = lpwfs->lpAppInfo;
01983     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
01984     {
01985         WORKREQUEST workRequest;
01986         struct WORKREQ_FTPREMOVEDIRECTORYW *req;
01987 
01988         workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
01989         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
01990         req = &workRequest.u.FtpRemoveDirectoryW;
01991         req->lpszDirectory = heap_strdupW(lpszDirectory);
01992 
01993     r = res_to_le(INTERNET_AsyncCall(&workRequest));
01994     }
01995     else
01996     {
01997         r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
01998     }
01999 
02000 lend:
02001     WININET_Release( &lpwfs->hdr );
02002 
02003     return r;
02004 }
02005 
02006 /***********************************************************************
02007  *           FTP_FtpRemoveDirectoryW  (Internal)
02008  *
02009  * Remove a directory on the ftp server
02010  *
02011  * RETURNS
02012  *    TRUE on success
02013  *    FALSE on failure
02014  *
02015  */
02016 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
02017 {
02018     INT nResCode;
02019     BOOL bSuccess = FALSE;
02020     appinfo_t *hIC = NULL;
02021 
02022     TRACE("\n");
02023 
02024     /* Clear any error information */
02025     INTERNET_SetLastError(0);
02026 
02027     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
02028         goto lend;
02029 
02030     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
02031     if (nResCode)
02032     {
02033         if (nResCode == 250)
02034             bSuccess = TRUE;
02035         else
02036             FTP_SetResponseError(nResCode);
02037     }
02038 
02039 lend:
02040     hIC = lpwfs->lpAppInfo;
02041     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
02042     {
02043         INTERNET_ASYNC_RESULT iar;
02044 
02045         iar.dwResult = (DWORD)bSuccess;
02046         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
02047         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
02048             &iar, sizeof(INTERNET_ASYNC_RESULT));
02049     }
02050 
02051     return bSuccess;
02052 }
02053 
02054 
02055 /***********************************************************************
02056  *           FtpRenameFileA  (WININET.@)
02057  *
02058  * Rename a file on the ftp server
02059  *
02060  * RETURNS
02061  *    TRUE on success
02062  *    FALSE on failure
02063  *
02064  */
02065 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
02066 {
02067     LPWSTR lpwzSrc;
02068     LPWSTR lpwzDest;
02069     BOOL ret;
02070     
02071     lpwzSrc = heap_strdupAtoW(lpszSrc);
02072     lpwzDest = heap_strdupAtoW(lpszDest);
02073     ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
02074     HeapFree(GetProcessHeap(), 0, lpwzSrc);
02075     HeapFree(GetProcessHeap(), 0, lpwzDest);
02076     return ret;
02077 }
02078 
02079 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
02080 {
02081     struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
02082     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
02083 
02084     TRACE("%p\n", lpwfs);
02085 
02086     FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
02087     HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
02088     HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
02089 }
02090 
02091 /***********************************************************************
02092  *           FtpRenameFileW  (WININET.@)
02093  *
02094  * Rename a file on the ftp server
02095  *
02096  * RETURNS
02097  *    TRUE on success
02098  *    FALSE on failure
02099  *
02100  */
02101 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
02102 {
02103     ftp_session_t *lpwfs;
02104     appinfo_t *hIC = NULL;
02105     BOOL r = FALSE;
02106 
02107     lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
02108     if (!lpwfs)
02109     {
02110         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
02111         return FALSE;
02112     }
02113 
02114     if (WH_HFTPSESSION != lpwfs->hdr.htype)
02115     {
02116         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
02117         goto lend;
02118     }
02119 
02120     if (lpwfs->download_in_progress != NULL)
02121     {
02122         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
02123         goto lend;
02124     }
02125 
02126     if (!lpszSrc || !lpszDest)
02127     {
02128         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
02129         goto lend;
02130     }
02131 
02132     hIC = lpwfs->lpAppInfo;
02133     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
02134     {
02135         WORKREQUEST workRequest;
02136         struct WORKREQ_FTPRENAMEFILEW *req;
02137 
02138         workRequest.asyncproc = AsyncFtpRenameFileProc;
02139         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
02140         req = &workRequest.u.FtpRenameFileW;
02141         req->lpszSrcFile = heap_strdupW(lpszSrc);
02142         req->lpszDestFile = heap_strdupW(lpszDest);
02143 
02144     r = res_to_le(INTERNET_AsyncCall(&workRequest));
02145     }
02146     else
02147     {
02148         r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
02149     }
02150 
02151 lend:
02152     WININET_Release( &lpwfs->hdr );
02153 
02154     return r;
02155 }
02156 
02157 /***********************************************************************
02158  *           FTP_FtpRenameFileW  (Internal)
02159  *
02160  * Rename a file on the ftp server
02161  *
02162  * RETURNS
02163  *    TRUE on success
02164  *    FALSE on failure
02165  *
02166  */
02167 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
02168 {
02169     INT nResCode;
02170     BOOL bSuccess = FALSE;
02171     appinfo_t *hIC = NULL;
02172 
02173     TRACE("\n");
02174 
02175     /* Clear any error information */
02176     INTERNET_SetLastError(0);
02177 
02178     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
02179         goto lend;
02180 
02181     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
02182     if (nResCode == 350)
02183     {
02184         if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
02185             goto lend;
02186 
02187         nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
02188     }
02189 
02190     if (nResCode == 250)
02191         bSuccess = TRUE;
02192     else
02193         FTP_SetResponseError(nResCode);
02194 
02195 lend:
02196     hIC = lpwfs->lpAppInfo;
02197     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
02198     {
02199         INTERNET_ASYNC_RESULT iar;
02200 
02201         iar.dwResult = (DWORD)bSuccess;
02202         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
02203         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
02204             &iar, sizeof(INTERNET_ASYNC_RESULT));
02205     }
02206 
02207     return bSuccess;
02208 }
02209 
02210 /***********************************************************************
02211  *           FtpCommandA  (WININET.@)
02212  */
02213 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
02214                          LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
02215 {
02216     BOOL r;
02217     WCHAR *cmdW;
02218 
02219     TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
02220           debugstr_a(lpszCommand), dwContext, phFtpCommand);
02221 
02222     if (fExpectResponse)
02223     {
02224         FIXME("data connection not supported\n");
02225         return FALSE;
02226     }
02227 
02228     if (!lpszCommand || !lpszCommand[0])
02229     {
02230         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
02231         return FALSE;
02232     }
02233 
02234     if (!(cmdW = heap_strdupAtoW(lpszCommand)))
02235     {
02236         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
02237         return FALSE;
02238     }
02239 
02240     r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
02241 
02242     HeapFree(GetProcessHeap(), 0, cmdW);
02243     return r;
02244 }
02245 
02246 /***********************************************************************
02247  *           FtpCommandW  (WININET.@)
02248  */
02249 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
02250                          LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
02251 {
02252     BOOL r = FALSE;
02253     ftp_session_t *lpwfs;
02254     LPSTR cmd = NULL;
02255     DWORD len, nBytesSent= 0;
02256     INT nResCode, nRC = 0;
02257 
02258     TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
02259            debugstr_w(lpszCommand), dwContext, phFtpCommand);
02260 
02261     if (!lpszCommand || !lpszCommand[0])
02262     {
02263         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
02264         return FALSE;
02265     }
02266 
02267     if (fExpectResponse)
02268     {
02269         FIXME("data connection not supported\n");
02270         return FALSE;
02271     }
02272 
02273     lpwfs = (ftp_session_t*) get_handle_object( hConnect );
02274     if (!lpwfs)
02275     {
02276         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
02277         return FALSE;
02278     }
02279 
02280     if (WH_HFTPSESSION != lpwfs->hdr.htype)
02281     {
02282         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
02283         goto lend;
02284     }
02285 
02286     if (lpwfs->download_in_progress != NULL)
02287     {
02288         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
02289         goto lend;
02290     }
02291 
02292     len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
02293     if ((cmd = heap_alloc(len)))
02294         WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
02295     else
02296     {
02297         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
02298         goto lend;
02299     }
02300 
02301     strcat(cmd, szCRLF);
02302     len--;
02303 
02304     TRACE("Sending (%s) len(%d)\n", cmd, len);
02305     while ((nBytesSent < len) && (nRC != -1))
02306     {
02307         nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
02308         if (nRC != -1)
02309         {
02310             nBytesSent += nRC;
02311             TRACE("Sent %d bytes\n", nRC);
02312         }
02313     }
02314 
02315     if (nBytesSent)
02316     {
02317         nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
02318         if (nResCode > 0 && nResCode < 400)
02319             r = TRUE;
02320         else
02321             FTP_SetResponseError(nResCode);
02322     }
02323 
02324 lend:
02325     WININET_Release( &lpwfs->hdr );
02326     HeapFree(GetProcessHeap(), 0, cmd);
02327     return r;
02328 }
02329 
02330 
02331 /***********************************************************************
02332  *           FTPSESSION_Destroy (internal)
02333  *
02334  * Deallocate session handle
02335  */
02336 static void FTPSESSION_Destroy(object_header_t *hdr)
02337 {
02338     ftp_session_t *lpwfs = (ftp_session_t*) hdr;
02339 
02340     TRACE("\n");
02341 
02342     WININET_Release(&lpwfs->lpAppInfo->hdr);
02343 
02344     HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
02345     HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
02346     HeapFree(GetProcessHeap(), 0, lpwfs->servername);
02347 }
02348 
02349 static void FTPSESSION_CloseConnection(object_header_t *hdr)
02350 {
02351     ftp_session_t *lpwfs = (ftp_session_t*) hdr;
02352 
02353     TRACE("\n");
02354 
02355     SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
02356                       INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
02357 
02358     if (lpwfs->download_in_progress != NULL)
02359         lpwfs->download_in_progress->session_deleted = TRUE;
02360 
02361      if (lpwfs->sndSocket != -1)
02362          closesocket(lpwfs->sndSocket);
02363 
02364      if (lpwfs->lstnSocket != -1)
02365          closesocket(lpwfs->lstnSocket);
02366 
02367     if (lpwfs->pasvSocket != -1)
02368         closesocket(lpwfs->pasvSocket);
02369 
02370     SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
02371                       INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
02372 }
02373 
02374 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
02375 {
02376     switch(option) {
02377     case INTERNET_OPTION_HANDLE_TYPE:
02378         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
02379 
02380         if (*size < sizeof(ULONG))
02381             return ERROR_INSUFFICIENT_BUFFER;
02382 
02383         *size = sizeof(DWORD);
02384         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
02385         return ERROR_SUCCESS;
02386     }
02387 
02388     return INET_QueryOption(hdr, option, buffer, size, unicode);
02389 }
02390 
02391 static const object_vtbl_t FTPSESSIONVtbl = {
02392     FTPSESSION_Destroy,
02393     FTPSESSION_CloseConnection,
02394     FTPSESSION_QueryOption,
02395     NULL,
02396     NULL,
02397     NULL,
02398     NULL,
02399     NULL,
02400     NULL
02401 };
02402 
02403 
02404 /***********************************************************************
02405  *           FTP_Connect (internal)
02406  *
02407  * Connect to a ftp server
02408  *
02409  * RETURNS
02410  *   HINTERNET a session handle on success
02411  *   NULL on failure
02412  *
02413  * NOTES:
02414  *
02415  * Windows uses 'anonymous' as the username, when given a NULL username
02416  * and a NULL password. The password is first looked up in:
02417  *
02418  * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
02419  *
02420  * If this entry is not present it uses the current username as the password.
02421  *
02422  */
02423 
02424 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
02425     INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
02426     LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
02427     DWORD dwInternalFlags)
02428 {
02429     static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
02430                                    'M','i','c','r','o','s','o','f','t','\\',
02431                                    'W','i','n','d','o','w','s','\\',
02432                                    'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
02433                                    'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
02434     static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
02435     static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
02436     static const WCHAR szEmpty[] = {'\0'};
02437     struct sockaddr_in socketAddr;
02438     INT nsocket = -1;
02439     UINT sock_namelen;
02440     BOOL bSuccess = FALSE;
02441     ftp_session_t *lpwfs = NULL;
02442     char szaddr[INET_ADDRSTRLEN];
02443 
02444     TRACE("%p  Server(%s) Port(%d) User(%s) Paswd(%s)\n",
02445         hIC, debugstr_w(lpszServerName),
02446         nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
02447 
02448     assert( hIC->hdr.htype == WH_HINIT );
02449 
02450     if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
02451     {
02452     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
02453         return NULL;
02454     }
02455     
02456     lpwfs = alloc_object(&hIC->hdr, &FTPSESSIONVtbl, sizeof(ftp_session_t));
02457     if (NULL == lpwfs)
02458     {
02459         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
02460         return NULL;
02461     }
02462 
02463     if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
02464         lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
02465     else
02466         lpwfs->serverport = nServerPort;
02467 
02468     lpwfs->hdr.htype = WH_HFTPSESSION;
02469     lpwfs->hdr.dwFlags = dwFlags;
02470     lpwfs->hdr.dwContext = dwContext;
02471     lpwfs->hdr.dwInternalFlags |= dwInternalFlags;
02472     lpwfs->download_in_progress = NULL;
02473     lpwfs->sndSocket = -1;
02474     lpwfs->lstnSocket = -1;
02475     lpwfs->pasvSocket = -1;
02476 
02477     WININET_AddRef( &hIC->hdr );
02478     lpwfs->lpAppInfo = hIC;
02479     list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
02480 
02481     if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
02482         if(strchrW(hIC->proxy, ' '))
02483             FIXME("Several proxies not implemented.\n");
02484         if(hIC->proxyBypass)
02485             FIXME("Proxy bypass is ignored.\n");
02486     }
02487     if (!lpszUserName || !strlenW(lpszUserName)) {
02488         HKEY key;
02489         WCHAR szPassword[MAX_PATH];
02490         DWORD len = sizeof(szPassword);
02491 
02492         lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
02493 
02494         RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
02495         if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
02496             /* Nothing in the registry, get the username and use that as the password */
02497             if (!GetUserNameW(szPassword, &len)) {
02498                 /* Should never get here, but use an empty password as failsafe */
02499                 strcpyW(szPassword, szEmpty);
02500             }
02501         }
02502         RegCloseKey(key);
02503 
02504         TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
02505         lpwfs->lpszPassword = heap_strdupW(szPassword);
02506     }
02507     else {
02508         lpwfs->lpszUserName = heap_strdupW(lpszUserName);
02509         lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
02510     }
02511     lpwfs->servername = heap_strdupW(lpszServerName);
02512     
02513     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
02514     if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
02515     {
02516         INTERNET_ASYNC_RESULT iar;
02517 
02518         iar.dwResult = (DWORD_PTR)lpwfs->hdr.hInternet;
02519         iar.dwError = ERROR_SUCCESS;
02520 
02521         SendAsyncCallback(&hIC->hdr, dwContext,
02522                       INTERNET_STATUS_HANDLE_CREATED, &iar,
02523                       sizeof(INTERNET_ASYNC_RESULT));
02524     }
02525         
02526     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
02527         (LPWSTR) lpszServerName, (strlenW(lpszServerName)+1) * sizeof(WCHAR));
02528 
02529     sock_namelen = sizeof(socketAddr);
02530     if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen))
02531     {
02532     INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
02533         goto lerror;
02534     }
02535 
02536     if (socketAddr.sin_family != AF_INET)
02537     {
02538         WARN("unsupported address family %d\n", socketAddr.sin_family);
02539         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
02540         goto lerror;
02541     }
02542 
02543     inet_ntop(socketAddr.sin_family, &socketAddr.sin_addr, szaddr, sizeof(szaddr));
02544     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
02545                       szaddr, strlen(szaddr)+1);
02546 
02547     nsocket = socket(AF_INET,SOCK_STREAM,0);
02548     if (nsocket == -1)
02549     {
02550     INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
02551         goto lerror;
02552     }
02553 
02554     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
02555                       szaddr, strlen(szaddr)+1);
02556 
02557     if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
02558     {
02559     ERR("Unable to connect (%s)\n", strerror(errno));
02560     INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
02561     closesocket(nsocket);
02562     }
02563     else
02564     {
02565         TRACE("Connected to server\n");
02566     lpwfs->sndSocket = nsocket;
02567         SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
02568                           szaddr, strlen(szaddr)+1);
02569 
02570     sock_namelen = sizeof(lpwfs->socketAddress);
02571     getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
02572 
02573         if (FTP_ConnectToHost(lpwfs))
02574         {
02575             TRACE("Successfully logged into server\n");
02576             bSuccess = TRUE;
02577         }
02578     }
02579 
02580 lerror:
02581     if (!bSuccess)
02582     {
02583         if(lpwfs)
02584             WININET_Release( &lpwfs->hdr );
02585         return NULL;
02586     }
02587 
02588     return lpwfs->hdr.hInternet;
02589 }
02590 
02591 
02592 /***********************************************************************
02593  *           FTP_ConnectToHost (internal)
02594  *
02595  * Connect to a ftp server
02596  *
02597  * RETURNS
02598  *   TRUE on success
02599  *   NULL on failure
02600  *
02601  */
02602 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
02603 {
02604     INT nResCode;
02605     BOOL bSuccess = FALSE;
02606 
02607     TRACE("\n");
02608     FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
02609 
02610     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
02611         goto lend;
02612 
02613     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
02614     if (nResCode)
02615     {
02616         /* Login successful... */
02617         if (nResCode == 230)
02618             bSuccess = TRUE;
02619         /* User name okay, need password... */
02620         else if (nResCode == 331)
02621             bSuccess = FTP_SendPassword(lpwfs);
02622         /* Need account for login... */
02623         else if (nResCode == 332)
02624             bSuccess = FTP_SendAccount(lpwfs);
02625         else
02626             FTP_SetResponseError(nResCode);
02627     }
02628 
02629     TRACE("Returning %d\n", bSuccess);
02630 lend:
02631     return bSuccess;
02632 }
02633 
02634 
02635 /***********************************************************************
02636  *           FTP_SendCommandA (internal)
02637  *
02638  * Send command to server
02639  *
02640  * RETURNS
02641  *   TRUE on success
02642  *   NULL on failure
02643  *
02644  */
02645 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
02646     INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
02647 {
02648         DWORD len;
02649     CHAR *buf;
02650     DWORD nBytesSent = 0;
02651     int nRC = 0;
02652     DWORD dwParamLen;
02653 
02654     TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
02655 
02656     if (lpfnStatusCB)
02657         {
02658             lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
02659         }
02660 
02661     dwParamLen = lpszParam?strlen(lpszParam)+1:0;
02662     len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
02663     if (NULL == (buf = heap_alloc(len+1)))
02664     {
02665         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
02666         return FALSE;
02667     }
02668     sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
02669         dwParamLen ? lpszParam : "", szCRLF);
02670 
02671     TRACE("Sending (%s) len(%d)\n", buf, len);
02672     while((nBytesSent < len) && (nRC != -1))
02673     {
02674         nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
02675         nBytesSent += nRC;
02676     }
02677 
02678     HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
02679 
02680     if (lpfnStatusCB)
02681         {
02682             lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
02683                          &nBytesSent, sizeof(DWORD));
02684         }
02685 
02686     TRACE("Sent %d bytes\n", nBytesSent);
02687     return (nRC != -1);
02688 }
02689 
02690 /***********************************************************************
02691  *           FTP_SendCommand (internal)
02692  *
02693  * Send command to server
02694  *
02695  * RETURNS
02696  *   TRUE on success
02697  *   NULL on failure
02698  *
02699  */
02700 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
02701     INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
02702 {
02703     BOOL ret;
02704     LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
02705     ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
02706     HeapFree(GetProcessHeap(), 0, lpszParamA);
02707     return ret;
02708 }
02709 
02710 /***********************************************************************
02711  *           FTP_ReceiveResponse (internal)
02712  *
02713  * Receive response from server
02714  *
02715  * RETURNS
02716  *   Reply code on success
02717  *   0 on failure
02718  *
02719  */
02720 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
02721 {
02722     LPSTR lpszResponse = INTERNET_GetResponseBuffer();
02723     DWORD nRecv;
02724     INT rc = 0;
02725     char firstprefix[5];
02726     BOOL multiline = FALSE;
02727 
02728     TRACE("socket(%d)\n", lpwfs->sndSocket);
02729 
02730     SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
02731 
02732     while(1)
02733     {
02734     if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
02735         goto lerror;
02736 
02737         if (nRecv >= 3)
02738     {
02739         if(!multiline)
02740         {
02741             if(lpszResponse[3] != '-')
02742             break;
02743         else
02744         {  /* Start of multiline response.  Loop until we get "nnn " */
02745             multiline = TRUE;
02746             memcpy(firstprefix, lpszResponse, 3);
02747             firstprefix[3] = ' ';
02748             firstprefix[4] = '\0';
02749         }
02750         }
02751         else
02752         {
02753             if(!memcmp(firstprefix, lpszResponse, 4))
02754             break;
02755         }
02756     }
02757     }
02758 
02759     if (nRecv >= 3)
02760     {
02761         rc = atoi(lpszResponse);
02762 
02763         SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
02764             &nRecv, sizeof(DWORD));
02765     }
02766 
02767 lerror:
02768     TRACE("return %d\n", rc);
02769     return rc;
02770 }
02771 
02772 
02773 /***********************************************************************
02774  *           FTP_SendPassword (internal)
02775  *
02776  * Send password to ftp server
02777  *
02778  * RETURNS
02779  *   TRUE on success
02780  *   NULL on failure
02781  *
02782  */
02783 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
02784 {
02785     INT nResCode;
02786     BOOL bSuccess = FALSE;
02787 
02788     TRACE("\n");
02789     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
02790         goto lend;
02791 
02792     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
02793     if (nResCode)
02794     {
02795         TRACE("Received reply code %d\n", nResCode);
02796         /* Login successful... */
02797         if (nResCode == 230)
02798             bSuccess = TRUE;
02799         /* Command not implemented, superfluous at the server site... */
02800         /* Need account for login... */
02801         else if (nResCode == 332)
02802             bSuccess = FTP_SendAccount(lpwfs);
02803         else
02804             FTP_SetResponseError(nResCode);
02805     }
02806 
02807 lend:
02808     TRACE("Returning %d\n", bSuccess);
02809     return bSuccess;
02810 }
02811 
02812 
02813 /***********************************************************************
02814  *           FTP_SendAccount (internal)
02815  *
02816  *
02817  *
02818  * RETURNS
02819  *   TRUE on success
02820  *   FALSE on failure
02821  *
02822  */
02823 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
02824 {
02825     INT nResCode;
02826     BOOL bSuccess = FALSE;
02827 
02828     TRACE("\n");
02829     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
02830         goto lend;
02831 
02832     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
02833     if (nResCode)
02834         bSuccess = TRUE;
02835     else
02836         FTP_SetResponseError(nResCode);
02837 
02838 lend:
02839     return bSuccess;
02840 }
02841 
02842 
02843 /***********************************************************************
02844  *           FTP_SendStore (internal)
02845  *
02846  * Send request to upload file to ftp server
02847  *
02848  * RETURNS
02849  *   TRUE on success
02850  *   FALSE on failure
02851  *
02852  */
02853 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
02854 {
02855     INT nResCode;
02856     BOOL bSuccess = FALSE;
02857 
02858     TRACE("\n");
02859     if (!FTP_InitListenSocket(lpwfs))
02860         goto lend;
02861 
02862     if (!FTP_SendType(lpwfs, dwType))
02863         goto lend;
02864 
02865     if (!FTP_SendPortOrPasv(lpwfs))
02866         goto lend;
02867 
02868     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
02869         goto lend;
02870     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
02871     if (nResCode)
02872     {
02873         if (nResCode == 150 || nResCode == 125)
02874             bSuccess = TRUE;
02875     else
02876             FTP_SetResponseError(nResCode);
02877     }
02878 
02879 lend:
02880     if (!bSuccess && lpwfs->lstnSocket != -1)
02881     {
02882         closesocket(lpwfs->lstnSocket);
02883         lpwfs->lstnSocket = -1;
02884     }
02885 
02886     return bSuccess;
02887 }
02888 
02889 
02890 /***********************************************************************
02891  *           FTP_InitListenSocket (internal)
02892  *
02893  * Create a socket to listen for server response
02894  *
02895  * RETURNS
02896  *   TRUE on success
02897  *   FALSE on failure
02898  *
02899  */
02900 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
02901 {
02902     BOOL bSuccess = FALSE;
02903     socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
02904 
02905     TRACE("\n");
02906 
02907     lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
02908     if (lpwfs->lstnSocket == -1)
02909     {
02910         TRACE("Unable to create listening socket\n");
02911             goto lend;
02912     }
02913 
02914     /* We obtain our ip addr from the name of the command channel socket */
02915     lpwfs->lstnSocketAddress = lpwfs->socketAddress;
02916 
02917     /* and get the system to assign us a port */
02918     lpwfs->lstnSocketAddress.sin_port = htons(0);
02919 
02920     if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
02921     {
02922         TRACE("Unable to bind socket\n");
02923         goto lend;
02924     }
02925 
02926     if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
02927     {
02928         TRACE("listen failed\n");
02929         goto lend;
02930     }
02931 
02932     if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
02933         bSuccess = TRUE;
02934 
02935 lend:
02936     if (!bSuccess && lpwfs->lstnSocket != -1)
02937     {
02938         closesocket(lpwfs->lstnSocket);
02939         lpwfs->lstnSocket = -1;
02940     }
02941 
02942     return bSuccess;
02943 }
02944 
02945 
02946 /***********************************************************************
02947  *           FTP_SendType (internal)
02948  *
02949  * Tell server type of data being transferred
02950  *
02951  * RETURNS
02952  *   TRUE on success
02953  *   FALSE on failure
02954  *
02955  * W98SE doesn't cache the type that's currently set
02956  * (i.e. it sends it always),
02957  * so we probably don't want to do that either.
02958  */
02959 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
02960 {
02961     INT nResCode;
02962     WCHAR type[] = { 'I','\0' };
02963     BOOL bSuccess = FALSE;
02964 
02965     TRACE("\n");
02966     if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
02967         type[0] = 'A';
02968 
02969     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
02970         goto lend;
02971 
02972     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
02973     if (nResCode)
02974     {
02975         if (nResCode == 2)
02976             bSuccess = TRUE;
02977     else
02978             FTP_SetResponseError(nResCode);
02979     }
02980 
02981 lend:
02982     return bSuccess;
02983 }
02984 
02985 
02986 #if 0  /* FIXME: should probably be used for FtpGetFileSize */
02987 /***********************************************************************
02988  *           FTP_GetFileSize (internal)
02989  *
02990  * Retrieves from the server the size of the given file
02991  *
02992  * RETURNS
02993  *   TRUE on success
02994  *   FALSE on failure
02995  *
02996  */
02997 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
02998 {
02999     INT nResCode;
03000     BOOL bSuccess = FALSE;
03001 
03002     TRACE("\n");
03003 
03004     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
03005         goto lend;
03006 
03007     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
03008     if (nResCode)
03009     {
03010         if (nResCode == 213) {
03011         /* Now parses the output to get the actual file size */
03012         int i;
03013         LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
03014 
03015         for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
03016         if (lpszResponseBuffer[i] == '\0') return FALSE;
03017         *dwSize = atol(&(lpszResponseBuffer[i + 1]));
03018         
03019             bSuccess = TRUE;
03020     } else {
03021             FTP_SetResponseError(nResCode);
03022     }
03023     }
03024 
03025 lend:
03026     return bSuccess;
03027 }
03028 #endif
03029 
03030 
03031 /***********************************************************************
03032  *           FTP_SendPort (internal)
03033  *
03034  * Tell server which port to use
03035  *
03036  * RETURNS
03037  *   TRUE on success
03038  *   FALSE on failure
03039  *
03040  */
03041 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
03042 {
03043     static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
03044     INT nResCode;
03045     WCHAR szIPAddress[64];
03046     BOOL bSuccess = FALSE;
03047     TRACE("\n");
03048 
03049     sprintfW(szIPAddress, szIPFormat,
03050      lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
03051         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
03052         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
03053         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
03054         lpwfs->lstnSocketAddress.sin_port & 0xFF,
03055         (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
03056 
03057     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
03058         goto lend;
03059 
03060     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
03061     if (nResCode)
03062     {
03063         if (nResCode == 200)
03064             bSuccess = TRUE;
03065         else
03066             FTP_SetResponseError(nResCode);
03067     }
03068 
03069 lend:
03070     return bSuccess;
03071 }
03072 
03073 
03074 /***********************************************************************
03075  *           FTP_DoPassive (internal)
03076  *
03077  * Tell server that we want to do passive transfers
03078  * and connect data socket
03079  *
03080  * RETURNS
03081  *   TRUE on success
03082  *   FALSE on failure
03083  *
03084  */
03085 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
03086 {
03087     INT nResCode;
03088     BOOL bSuccess = FALSE;
03089 
03090     TRACE("\n");
03091     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
03092         goto lend;
03093 
03094     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
03095     if (nResCode)
03096     {
03097         if (nResCode == 227)
03098     {
03099         LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
03100         LPSTR p;
03101         int f[6];
03102         int i;
03103         char *pAddr, *pPort;
03104         INT nsocket = -1;
03105         struct sockaddr_in dataSocketAddress;
03106 
03107         p = lpszResponseBuffer+4; /* skip status code */
03108         while (*p != '\0' && (*p < '0' || *p > '9')) p++;
03109 
03110         if (*p == '\0')
03111         {
03112         ERR("no address found in response, aborting\n");
03113         goto lend;
03114         }
03115 
03116         if (sscanf(p, "%d,%d,%d,%d,%d,%d",  &f[0], &f[1], &f[2], &f[3],
03117                             &f[4], &f[5]) != 6)
03118         {
03119         ERR("unknown response address format '%s', aborting\n", p);
03120         goto lend;
03121         }
03122         for (i=0; i < 6; i++)
03123         f[i] = f[i] & 0xff;
03124 
03125         dataSocketAddress = lpwfs->socketAddress;
03126         pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
03127         pPort = (char *)&(dataSocketAddress.sin_port);
03128             pAddr[0] = f[0];
03129             pAddr[1] = f[1];
03130             pAddr[2] = f[2];
03131             pAddr[3] = f[3];
03132         pPort[0] = f[4];
03133         pPort[1] = f[5];
03134 
03135             nsocket = socket(AF_INET,SOCK_STREAM,0);
03136             if (nsocket == -1)
03137                 goto lend;
03138 
03139         if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
03140             {
03141             ERR("can't connect passive FTP data port.\n");
03142                 closesocket(nsocket);
03143             goto lend;
03144             }
03145         lpwfs->pasvSocket = nsocket;
03146             bSuccess = TRUE;
03147     }
03148         else
03149             FTP_SetResponseError(nResCode);
03150     }
03151 
03152 lend:
03153     return bSuccess;
03154 }
03155 
03156 
03157 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
03158 {
03159     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
03160     {
03161         if (!FTP_DoPassive(lpwfs))
03162             return FALSE;
03163     }
03164     else
03165     {
03166     if (!FTP_SendPort(lpwfs))
03167             return FALSE;
03168     }
03169     return TRUE;
03170 }
03171 
03172 
03173 /***********************************************************************
03174  *           FTP_GetDataSocket (internal)
03175  *
03176  * Either accepts an incoming data socket connection from the server
03177  * or just returns the already opened socket after a PASV command
03178  * in case of passive FTP.
03179  *
03180  *
03181  * RETURNS
03182  *   TRUE on success
03183  *   FALSE on failure
03184  *
03185  */
03186 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
03187 {
03188     struct sockaddr_in saddr;
03189     socklen_t addrlen = sizeof(struct sockaddr);
03190 
03191     TRACE("\n");
03192     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
03193     {
03194     *nDataSocket = lpwfs->pasvSocket;
03195     lpwfs->pasvSocket = -1;
03196     }
03197     else
03198     {
03199         *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
03200         closesocket(lpwfs->lstnSocket);
03201         lpwfs->lstnSocket = -1;
03202     }
03203     return *nDataSocket != -1;
03204 }
03205 
03206 
03207 /***********************************************************************
03208  *           FTP_SendData (internal)
03209  *
03210  * Send data to the server
03211  *
03212  * RETURNS
03213  *   TRUE on success
03214  *   FALSE on failure
03215  *
03216  */
03217 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
03218 {
03219     BY_HANDLE_FILE_INFORMATION fi;
03220     DWORD nBytesRead = 0;
03221     DWORD nBytesSent = 0;
03222     DWORD nTotalSent = 0;
03223     DWORD nBytesToSend, nLen;
03224     int nRC = 1;
03225     time_t s_long_time, e_long_time;
03226     LONG nSeconds;
03227     CHAR *lpszBuffer;
03228 
03229     TRACE("\n");
03230     lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
03231 
03232     /* Get the size of the file. */
03233     GetFileInformationByHandle(hFile, &fi);
03234     time(&s_long_time);
03235 
03236     do
03237     {
03238         nBytesToSend = nBytesRead - nBytesSent;
03239 
03240         if (nBytesToSend <= 0)
03241         {
03242             /* Read data from file. */
03243             nBytesSent = 0;
03244             if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
03245             ERR("Failed reading from file\n");
03246 
03247             if (nBytesRead > 0)
03248                 nBytesToSend = nBytesRead;
03249             else
03250                 break;
03251         }
03252 
03253         nLen = DATA_PACKET_SIZE < nBytesToSend ?
03254             DATA_PACKET_SIZE : nBytesToSend;
03255         nRC  = send(nDataSocket, lpszBuffer, nLen, 0);
03256 
03257         if (nRC != -1)
03258         {
03259             nBytesSent += nRC;
03260             nTotalSent += nRC;
03261         }
03262 
03263         /* Do some computation to display the status. */
03264         time(&e_long_time);
03265         nSeconds = e_long_time - s_long_time;
03266         if( nSeconds / 60 > 0 )
03267         {
03268             TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
03269             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
03270             nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
03271         }
03272         else
03273         {
03274             TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
03275             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
03276             (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
03277         }
03278     } while (nRC != -1);
03279 
03280     TRACE("file transfer complete!\n");
03281 
03282     HeapFree(GetProcessHeap(), 0, lpszBuffer);
03283 
03284     return nTotalSent;
03285 }
03286 
03287 
03288 /***********************************************************************
03289  *           FTP_SendRetrieve (internal)
03290  *
03291  * Send request to retrieve a file
03292  *
03293  * RETURNS
03294  *   Number of bytes to be received on success
03295  *   0 on failure
03296  *
03297  */
03298 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
03299 {
03300     INT nResCode;
03301     BOOL ret;
03302 
03303     TRACE("\n");
03304     if (!(ret = FTP_InitListenSocket(lpwfs)))
03305         goto lend;
03306 
03307     if (!(ret = FTP_SendType(lpwfs, dwType)))
03308         goto lend;
03309 
03310     if (!(ret = FTP_SendPortOrPasv(lpwfs)))
03311         goto lend;
03312 
03313     if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
03314         goto lend;
03315 
03316     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
03317     if ((nResCode != 125) && (nResCode != 150)) {
03318     /* That means that we got an error getting the file. */
03319         FTP_SetResponseError(nResCode);
03320     ret = FALSE;
03321     }
03322 
03323 lend:
03324     if (!ret && lpwfs->lstnSocket != -1)
03325     {
03326         closesocket(lpwfs->lstnSocket);
03327         lpwfs->lstnSocket = -1;
03328     }
03329 
03330     return ret;
03331 }
03332 
03333 
03334 /***********************************************************************
03335  *           FTP_RetrieveData  (internal)
03336  *
03337  * Retrieve data from server
03338  *
03339  * RETURNS
03340  *   TRUE on success
03341  *   FALSE on failure
03342  *
03343  */
03344 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
03345 {
03346     DWORD nBytesWritten;
03347     INT nRC = 0;
03348     CHAR *lpszBuffer;
03349 
03350     TRACE("\n");
03351 
03352     lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
03353     if (NULL == lpszBuffer)
03354     {
03355         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
03356         return FALSE;
03357     }
03358 
03359     while (nRC != -1)
03360     {
03361         nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
03362         if (nRC != -1)
03363         {
03364             /* other side closed socket. */
03365             if (nRC == 0)
03366                 goto recv_end;
03367             WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
03368         }
03369     }
03370 
03371     TRACE("Data transfer complete\n");
03372 
03373 recv_end:
03374     HeapFree(GetProcessHeap(), 0, lpszBuffer);
03375 
03376     return  (nRC != -1);
03377 }
03378 
03379 /***********************************************************************
03380  *           FTPFINDNEXT_Destroy (internal)
03381  *
03382  * Deallocate session handle
03383  */
03384 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
03385 {
03386     LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
03387     DWORD i;
03388 
03389     TRACE("\n");
03390 
03391     WININET_Release(&lpwfn->lpFtpSession->hdr);
03392 
03393     for (i = 0; i < lpwfn->size; i++)
03394     {
03395         HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
03396     }
03397 
03398     HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
03399 }
03400 
03401 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
03402 {
03403     WIN32_FIND_DATAW *find_data = data;
03404     DWORD res = ERROR_SUCCESS;
03405 
03406     TRACE("index(%d) size(%d)\n", find->index, find->size);
03407 
03408     ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
03409 
03410     if (find->index < find->size) {
03411         FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
03412         find->index++;
03413 
03414         TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
03415     }else {
03416         res = ERROR_NO_MORE_FILES;
03417     }
03418 
03419     if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
03420     {
03421         INTERNET_ASYNC_RESULT iar;
03422 
03423         iar.dwResult = (res == ERROR_SUCCESS);
03424         iar.dwError = res;
03425 
03426         INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
03427                               INTERNET_STATUS_REQUEST_COMPLETE, &iar,
03428                               sizeof(INTERNET_ASYNC_RESULT));
03429     }
03430 
03431     return res;
03432 }
03433 
03434 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
03435 {
03436     struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
03437 
03438     FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
03439 }
03440 
03441 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
03442 {
03443     switch(option) {
03444     case INTERNET_OPTION_HANDLE_TYPE:
03445         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
03446 
03447         if (*size < sizeof(ULONG))
03448             return ERROR_INSUFFICIENT_BUFFER;
03449 
03450         *size = sizeof(DWORD);
03451         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
03452         return ERROR_SUCCESS;
03453     }
03454 
03455     return INET_QueryOption(hdr, option, buffer, size, unicode);
03456 }
03457 
03458 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
03459 {
03460     WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
03461 
03462     if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
03463     {
03464         WORKREQUEST workRequest;
03465         struct WORKREQ_FTPFINDNEXTW *req;
03466 
03467         workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
03468         workRequest.hdr = WININET_AddRef( &find->hdr );
03469         req = &workRequest.u.FtpFindNextW;
03470         req->lpFindFileData = data;
03471 
03472     INTERNET_AsyncCall(&workRequest);
03473 
03474         return ERROR_SUCCESS;
03475     }
03476 
03477     return FTPFINDNEXT_FindNextFileProc(find, data);
03478 }
03479 
03480 static const object_vtbl_t FTPFINDNEXTVtbl = {
03481     FTPFINDNEXT_Destroy,
03482     NULL,
03483     FTPFINDNEXT_QueryOption,
03484     NULL,
03485     NULL,
03486     NULL,
03487     NULL,
03488     NULL,
03489     NULL,
03490     FTPFINDNEXT_FindNextFileW
03491 };
03492 
03493 /***********************************************************************
03494  *           FTP_ReceiveFileList (internal)
03495  *
03496  * Read file list from server
03497  *
03498  * RETURNS
03499  *   Handle to file list on success
03500  *   NULL on failure
03501  *
03502  */
03503 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
03504     LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
03505 {
03506     DWORD dwSize = 0;
03507     LPFILEPROPERTIESW lpafp = NULL;
03508     LPWININETFTPFINDNEXTW lpwfn = NULL;
03509 
03510     TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
03511 
03512     if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
03513     {
03514         if(lpFindFileData)
03515             FTP_ConvertFileProp(lpafp, lpFindFileData);
03516 
03517         lpwfn = alloc_object(&lpwfs->hdr, &FTPFINDNEXTVtbl, sizeof(WININETFTPFINDNEXTW));
03518         if (lpwfn)
03519         {
03520             lpwfn->hdr.htype = WH_HFTPFINDNEXT;
03521             lpwfn->hdr.dwContext = dwContext;
03522             lpwfn->index = 1; /* Next index is 1 since we return index 0 */
03523             lpwfn->size = dwSize;
03524             lpwfn->lpafp = lpafp;
03525 
03526             WININET_AddRef( &lpwfs->hdr );
03527             lpwfn->lpFtpSession = lpwfs;
03528             list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
03529         }
03530     }
03531 
03532     TRACE("Matched %d files\n", dwSize);
03533     return lpwfn ? lpwfn->hdr.hInternet : NULL;
03534 }
03535 
03536 
03537 /***********************************************************************
03538  *           FTP_ConvertFileProp (internal)
03539  *
03540  * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
03541  *
03542  * RETURNS
03543  *   TRUE on success
03544  *   FALSE on failure
03545  *
03546  */
03547 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
03548 {
03549     BOOL bSuccess = FALSE;
03550 
03551     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
03552 
03553     if (lpafp)
03554     {
03555         SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
03556     lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
03557     lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
03558     
03559         /* Not all fields are filled in */
03560         lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
03561         lpFindFileData->nFileSizeLow = lpafp->nSize;
03562 
03563     if (lpafp->bIsDirectory)
03564         lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
03565 
03566         if (lpafp->lpszName)
03567             lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
03568 
03569     bSuccess = TRUE;
03570     }
03571 
03572     return bSuccess;
03573 }
03574 
03575 /***********************************************************************
03576  *           FTP_ParseNextFile (internal)
03577  *
03578  * Parse the next line in file listing
03579  *
03580  * RETURNS
03581  *   TRUE on success
03582  *   FALSE on failure
03583  */
03584 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
03585 {
03586     static const char szSpace[] = " \t";
03587     DWORD nBufLen;
03588     char *pszLine;
03589     char *pszToken;
03590     char *pszTmp;
03591     BOOL found = FALSE;
03592     int i;
03593     
03594     lpfp->lpszName = NULL;
03595     do {
03596         if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
03597             return FALSE;
03598     
03599         pszToken = strtok(pszLine, szSpace);
03600         /* ls format
03601          * <Permissions> <NoLinks> <owner>   <group> <size> <date>  <time or year> <filename>
03602          *
03603          * For instance:
03604          * drwx--s---     2         pcarrier  ens     512    Sep 28  1995           pcarrier
03605          */
03606         if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
03607             if(!FTP_ParsePermission(pszToken, lpfp))
03608                 lpfp->bIsDirectory = FALSE;
03609             for(i=0; i<=3; i++) {
03610               if(!(pszToken = strtok(NULL, szSpace)))
03611                   break;
03612             }
03613             if(!pszToken) continue;
03614             if(lpfp->bIsDirectory) {
03615                 TRACE("Is directory\n");
03616                 lpfp->nSize = 0;
03617             }
03618             else {
03619                 TRACE("Size: %s\n", pszToken);
03620                 lpfp->nSize = atol(pszToken);
03621             }
03622             
03623             lpfp->tmLastModified.wSecond = 0;
03624             lpfp->tmLastModified.wMinute = 0;
03625             lpfp->tmLastModified.wHour   = 0;
03626             lpfp->tmLastModified.wDay    = 0;
03627             lpfp->tmLastModified.wMonth  = 0;
03628             lpfp->tmLastModified.wYear   = 0;
03629             
03630             /* Determine month */
03631             pszToken = strtok(NULL, szSpace);
03632             if(!pszToken) continue;
03633             if(strlen(pszToken) >= 3) {
03634                 pszToken[3] = 0;
03635                 if((pszTmp = StrStrIA(szMonths, pszToken)))
03636                     lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
03637             }
03638             /* Determine day */
03639             pszToken = strtok(NULL, szSpace);
03640             if(!pszToken) continue;
03641             lpfp->tmLastModified.wDay = atoi(pszToken);
03642             /* Determine time or year */
03643             pszToken = strtok(NULL, szSpace);
03644             if(!pszToken) continue;
03645             if((pszTmp = strchr(pszToken, ':'))) {
03646                 SYSTEMTIME curr_time;
03647                 *pszTmp = 0;
03648                 pszTmp++;
03649                 lpfp->tmLastModified.wMinute = atoi(pszTmp);
03650                 lpfp->tmLastModified.wHour = atoi(pszToken);
03651                 GetLocalTime( &curr_time );
03652                 lpfp->tmLastModified.wYear = curr_time.wYear;
03653             }
03654             else {
03655                 lpfp->tmLastModified.wYear = atoi(pszToken);
03656                 lpfp->tmLastModified.wHour = 12;
03657             }
03658             TRACE("Mod time: %02d:%02d:%02d  %04d/%02d/%02d\n",
03659                   lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
03660                   lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
03661 
03662             pszToken = strtok(NULL, szSpace);
03663             if(!pszToken) continue;
03664             lpfp->lpszName = heap_strdupAtoW(pszToken);
03665             TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
03666         }
03667         /* NT way of parsing ... :
03668             
03669                 07-13-03  08:55PM       <DIR>          sakpatch
03670                 05-09-03  06:02PM             12656686 2003-04-21bgm_cmd_e.rgz
03671         */
03672         else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
03673             int mon, mday, year, hour, min;
03674             lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
03675             
03676             sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
03677             lpfp->tmLastModified.wDay   = mday;
03678             lpfp->tmLastModified.wMonth = mon;
03679             lpfp->tmLastModified.wYear  = year;
03680 
03681             /* Hacky and bad Y2K protection :-) */
03682             if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
03683 
03684             pszToken = strtok(NULL, szSpace);
03685             if(!pszToken) continue;
03686             sscanf(pszToken, "%d:%d", &hour, &min);
03687             lpfp->tmLastModified.wHour   = hour;
03688             lpfp->tmLastModified.wMinute = min;
03689             if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
03690                 lpfp->tmLastModified.wHour += 12;
03691             }
03692             lpfp->tmLastModified.wSecond = 0;
03693 
03694             TRACE("Mod time: %02d:%02d:%02d  %04d/%02d/%02d\n",
03695                   lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
03696                   lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
03697 
03698             pszToken = strtok(NULL, szSpace);
03699             if(!pszToken) continue;
03700             if(!strcasecmp(pszToken, "<DIR>")) {
03701                 lpfp->bIsDirectory = TRUE;
03702                 lpfp->nSize = 0;
03703                 TRACE("Is directory\n");
03704             }
03705             else {
03706                 lpfp->bIsDirectory = FALSE;
03707                 lpfp->nSize = atol(pszToken);
03708                 TRACE("Size: %d\n", lpfp->nSize);
03709             }
03710             
03711             pszToken = strtok(NULL, szSpace);
03712             if(!pszToken) continue;
03713             lpfp->lpszName = heap_strdupAtoW(pszToken);
03714             TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
03715         }
03716         /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
03717         else if(pszToken[0] == '+') {
03718             FIXME("EPLF Format not implemented\n");
03719         }
03720         
03721         if(lpfp->lpszName) {
03722             if((lpszSearchFile == NULL) ||
03723            (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
03724                 found = TRUE;
03725                 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
03726             }
03727             else {
03728                 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
03729                 lpfp->lpszName = NULL;
03730             }
03731         }
03732     } while(!found);
03733     return TRUE;
03734 }
03735 
03736 /***********************************************************************
03737  *           FTP_ParseDirectory (internal)
03738  *
03739  * Parse string of directory information
03740  *
03741  * RETURNS
03742  *   TRUE on success
03743  *   FALSE on failure
03744  */
03745 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
03746     LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
03747 {
03748     BOOL bSuccess = TRUE;
03749     INT sizeFilePropArray = 500;/*20; */
03750     INT indexFilePropArray = -1;
03751 
03752     TRACE("\n");
03753 
03754     /* Allocate initial file properties array */
03755     *lpafp = heap_alloc_zero(sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
03756     if (!*lpafp)
03757         return FALSE;
03758 
03759     do {
03760         if (indexFilePropArray+1 >= sizeFilePropArray)
03761         {
03762             LPFILEPROPERTIESW tmpafp;
03763             
03764             sizeFilePropArray *= 2;
03765             tmpafp = heap_realloc_zero(*lpafp, sizeof(FILEPROPERTIESW)*sizeFilePropArray);
03766             if (NULL == tmpafp)
03767             {
03768                 bSuccess = FALSE;
03769                 break;
03770             }
03771 
03772             *lpafp = tmpafp;
03773         }
03774         indexFilePropArray++;
03775     } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
03776 
03777     if (bSuccess && indexFilePropArray)
03778     {
03779         if (indexFilePropArray < sizeFilePropArray - 1)
03780         {
03781             LPFILEPROPERTIESW tmpafp;
03782 
03783             tmpafp = heap_realloc(*lpafp, sizeof(FILEPROPERTIESW)*indexFilePropArray);
03784             if (NULL != tmpafp)
03785                 *lpafp = tmpafp;
03786         }
03787         *dwfp = indexFilePropArray;
03788     }
03789     else
03790     {
03791         HeapFree(GetProcessHeap(), 0, *lpafp);
03792         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
03793         bSuccess = FALSE;
03794     }
03795 
03796     return bSuccess;
03797 }
03798 
03799 
03800 /***********************************************************************
03801  *           FTP_ParsePermission (internal)
03802  *
03803  * Parse permission string of directory information
03804  *
03805  * RETURNS
03806  *   TRUE on success
03807  *   FALSE on failure
03808  *
03809  */
03810 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
03811 {
03812     BOOL bSuccess = TRUE;
03813     unsigned short nPermission = 0;
03814     INT nPos = 1;
03815     INT nLast  = 9;
03816 
03817     TRACE("\n");
03818     if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
03819     {
03820         bSuccess = FALSE;
03821         return bSuccess;
03822     }
03823 
03824     lpfp->bIsDirectory = (*lpszPermission == 'd');
03825     do
03826     {
03827         switch (nPos)
03828         {
03829             case 1:
03830                 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
03831                 break;
03832             case 2:
03833                 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
03834                 break;
03835             case 3:
03836                 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
03837                 break;
03838             case 4:
03839                 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
03840                 break;
03841             case 5:
03842                 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
03843                 break;
03844             case 6:
03845                 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
03846                 break;
03847             case 7:
03848                 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
03849                 break;
03850             case 8:
03851                 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
03852                 break;
03853             case 9:
03854                 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
03855                 break;
03856         }
03857         nPos++;
03858     }while (nPos <= nLast);
03859 
03860     lpfp->permissions = nPermission;
03861     return bSuccess;
03862 }
03863 
03864 
03865 /***********************************************************************
03866  *           FTP_SetResponseError (internal)
03867  *
03868  * Set the appropriate error code for a given response from the server
03869  *
03870  * RETURNS
03871  *
03872  */
03873 static DWORD FTP_SetResponseError(DWORD dwResponse)
03874 {
03875     DWORD dwCode = 0;
03876 
03877     switch(dwResponse)
03878     {
03879     case 425: /* Cannot open data connection. */
03880         dwCode = ERROR_INTERNET_CANNOT_CONNECT;
03881         break;
03882 
03883     case 426: /* Connection closed, transer aborted. */
03884         dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
03885         break;
03886 
03887     case 530: /* Not logged in. Login incorrect. */
03888         dwCode = ERROR_INTERNET_LOGIN_FAILURE;
03889         break;
03890 
03891     case 421: /* Service not available - Server may be shutting down. */
03892     case 450: /* File action not taken. File may be busy. */
03893     case 451: /* Action aborted. Server error. */
03894     case 452: /* Action not taken. Insufficient storage space on server. */
03895     case 500: /* Syntax error. Command unrecognized. */
03896     case 501: /* Syntax error. Error in parameters or arguments. */
03897     case 502: /* Command not implemented. */
03898     case 503: /* Bad sequence of commands. */
03899     case 504: /* Command not implemented for that parameter. */
03900     case 532: /* Need account for storing files */
03901     case 550: /* File action not taken. File not found or no access. */
03902     case 551: /* Requested action aborted. Page type unknown */
03903     case 552: /* Action aborted. Exceeded storage allocation */
03904     case 553: /* Action not taken. File name not allowed. */
03905 
03906     default:
03907         dwCode = ERROR_INTERNET_EXTENDED_ERROR;
03908         break;
03909     }
03910 
03911     INTERNET_SetLastError(dwCode);
03912     return dwCode;
03913 }

Generated on Sat May 26 2012 04:16:09 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.