~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/wininet/ftp.c

Version: ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

  1 /*
  2  * WININET - Ftp implementation
  3  *
  4  * Copyright 1999 Corel Corporation
  5  * Copyright 2004 Mike McCormack for CodeWeavers
  6  * Copyright 2004 Kevin Koltzau
  7  * Copyright 2007 Hans Leidekker
  8  *
  9  * Ulrich Czekalla
 10  * Noureddine Jemmali
 11  *
 12  * Copyright 2000 Andreas Mohr
 13  * Copyright 2002 Jaco Greeff
 14  *
 15  * This library is free software; you can redistribute it and/or
 16  * modify it under the terms of the GNU Lesser General Public
 17  * License as published by the Free Software Foundation; either
 18  * version 2.1 of the License, or (at your option) any later version.
 19  *
 20  * This library is distributed in the hope that it will be useful,
 21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 23  * Lesser General Public License for more details.
 24  *
 25  * You should have received a copy of the GNU Lesser General Public
 26  * License along with this library; if not, write to the Free Software
 27  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 28  */
 29 
 30 #include "config.h"
 31 #include "wine/port.h"
 32 
 33 #if defined(__MINGW32__) || defined (_MSC_VER)
 34 #include <ws2tcpip.h>
 35 #endif
 36 
 37 #include <errno.h>
 38 #include <stdarg.h>
 39 #include <stdio.h>
 40 #include <stdlib.h>
 41 #include <string.h>
 42 #include <sys/types.h>
 43 #ifdef HAVE_SYS_SOCKET_H
 44 # include <sys/socket.h>
 45 #endif
 46 #ifdef HAVE_UNISTD_H
 47 # include <unistd.h>
 48 #endif
 49 #ifdef HAVE_SYS_IOCTL_H
 50 # include <sys/ioctl.h>
 51 #endif
 52 #include <time.h>
 53 #include <assert.h>
 54 
 55 #include "windef.h"
 56 #include "winbase.h"
 57 #include "wingdi.h"
 58 #include "winuser.h"
 59 #include "wininet.h"
 60 #include "winnls.h"
 61 #include "winerror.h"
 62 #include "winreg.h"
 63 #include "winternl.h"
 64 #include "shlwapi.h"
 65 
 66 #include "wine/debug.h"
 67 #include "internet.h"
 68 
 69 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
 70 
 71 typedef struct _ftp_session_t ftp_session_t;
 72 
 73 typedef struct
 74 {
 75     object_header_t hdr;
 76     ftp_session_t *lpFtpSession;
 77     BOOL session_deleted;
 78     int nDataSocket;
 79 } ftp_file_t;
 80 
 81 struct _ftp_session_t
 82 {
 83     object_header_t hdr;
 84     appinfo_t *lpAppInfo;
 85     int sndSocket;
 86     int lstnSocket;
 87     int pasvSocket; /* data socket connected by us in case of passive FTP */
 88     ftp_file_t *download_in_progress;
 89     struct sockaddr_in socketAddress;
 90     struct sockaddr_in lstnSocketAddress;
 91     LPWSTR  lpszPassword;
 92     LPWSTR  lpszUserName;
 93 };
 94 
 95 typedef struct
 96 {
 97     BOOL bIsDirectory;
 98     LPWSTR lpszName;
 99     DWORD nSize;
100     SYSTEMTIME tmLastModified;
101     unsigned short permissions;
102 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
103 
104 typedef struct
105 {
106     object_header_t hdr;
107     ftp_session_t *lpFtpSession;
108     DWORD index;
109     DWORD size;
110     LPFILEPROPERTIESW lpafp;
111 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
112 
113 #define DATA_PACKET_SIZE        0x2000
114 #define szCRLF                  "\r\n"
115 #define MAX_BACKLOG             5
116 
117 /* Testing shows that Windows only accepts dwFlags where the last
118  * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
119  */
120 #define FTP_CONDITION_MASK      0x0007
121 
122 typedef enum {
123   /* FTP commands with arguments. */
124   FTP_CMD_ACCT,
125   FTP_CMD_CWD,
126   FTP_CMD_DELE,
127   FTP_CMD_MKD,
128   FTP_CMD_PASS,
129   FTP_CMD_PORT,
130   FTP_CMD_RETR,
131   FTP_CMD_RMD,
132   FTP_CMD_RNFR,
133   FTP_CMD_RNTO,
134   FTP_CMD_STOR,
135   FTP_CMD_TYPE,
136   FTP_CMD_USER,
137   FTP_CMD_SIZE,
138 
139   /* FTP commands without arguments. */
140   FTP_CMD_ABOR,
141   FTP_CMD_LIST,
142   FTP_CMD_NLST,
143   FTP_CMD_PASV,
144   FTP_CMD_PWD,
145   FTP_CMD_QUIT,
146 } FTP_COMMAND;
147 
148 static const CHAR *const szFtpCommands[] = {
149   "ACCT",
150   "CWD",
151   "DELE",
152   "MKD",
153   "PASS",
154   "PORT",
155   "RETR",
156   "RMD",
157   "RNFR",
158   "RNTO",
159   "STOR",
160   "TYPE",
161   "USER",
162   "SIZE",
163   "ABOR",
164   "LIST",
165   "NLST",
166   "PASV",
167   "PWD",
168   "QUIT",
169 };
170 
171 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
172 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
173 
174 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
175         INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext);
176 static BOOL FTP_SendStore(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
177 static BOOL FTP_GetDataSocket(ftp_session_t*, LPINT nDataSocket);
178 static BOOL FTP_SendData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
179 static INT FTP_ReceiveResponse(ftp_session_t*, DWORD_PTR dwContext);
180 static BOOL FTP_SendRetrieve(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
181 static BOOL FTP_RetrieveFileData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
182 static BOOL FTP_InitListenSocket(ftp_session_t*);
183 static BOOL FTP_ConnectToHost(ftp_session_t*);
184 static BOOL FTP_SendPassword(ftp_session_t*);
185 static BOOL FTP_SendAccount(ftp_session_t*);
186 static BOOL FTP_SendType(ftp_session_t*, DWORD dwType);
187 static BOOL FTP_SendPort(ftp_session_t*);
188 static BOOL FTP_DoPassive(ftp_session_t*);
189 static BOOL FTP_SendPortOrPasv(ftp_session_t*);
190 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
191 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
192 static BOOL FTP_ParseDirectory(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
193         LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
194 static HINTERNET FTP_ReceiveFileList(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
195         LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
196 static DWORD FTP_SetResponseError(DWORD dwResponse);
197 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
198 static BOOL FTP_FtpPutFileW(ftp_session_t*, LPCWSTR lpszLocalFile,
199         LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
200 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
201 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
202 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t*,
203         LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
204 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t*, LPWSTR lpszCurrentDirectory,
205         LPDWORD lpdwCurrentDirectory);
206 static BOOL FTP_FtpRenameFileW(ftp_session_t*, LPCWSTR lpszSrc, LPCWSTR lpszDest);
207 static BOOL FTP_FtpRemoveDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
208 static BOOL FTP_FtpDeleteFileW(ftp_session_t*, LPCWSTR lpszFileName);
209 static BOOL FTP_FtpGetFileW(ftp_session_t*, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
210         BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
211         DWORD_PTR dwContext);
212 
213 
214 /***********************************************************************
215  *           FtpPutFileA (WININET.@)
216  *
217  * Uploads a file to the FTP server
218  *
219  * RETURNS
220  *    TRUE on success
221  *    FALSE on failure
222  *
223  */
224 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
225     LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
226 {
227     LPWSTR lpwzLocalFile;
228     LPWSTR lpwzNewRemoteFile;
229     BOOL ret;
230     
231     lpwzLocalFile = heap_strdupAtoW(lpszLocalFile);
232     lpwzNewRemoteFile = heap_strdupAtoW(lpszNewRemoteFile);
233     ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
234                       dwFlags, dwContext);
235     HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
236     HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
237     return ret;
238 }
239 
240 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
241 {
242     struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
243     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
244 
245     TRACE("%p\n", lpwfs);
246 
247     FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
248                req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
249 
250     HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
251     HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
252 }
253 
254 /***********************************************************************
255  *           FtpPutFileW (WININET.@)
256  *
257  * Uploads a file to the FTP server
258  *
259  * RETURNS
260  *    TRUE on success
261  *    FALSE on failure
262  *
263  */
264 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
265     LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
266 {
267     ftp_session_t *lpwfs;
268     appinfo_t *hIC = NULL;
269     BOOL r = FALSE;
270 
271     if (!lpszLocalFile || !lpszNewRemoteFile)
272     {
273         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
274         return FALSE;
275     }
276 
277     lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
278     if (!lpwfs)
279     {
280         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
281         return FALSE;
282     }
283 
284     if (WH_HFTPSESSION != lpwfs->hdr.htype)
285     {
286         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
287         goto lend;
288     }
289 
290     if (lpwfs->download_in_progress != NULL)
291     {
292         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
293         goto lend;
294     }
295 
296     if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
297     {
298         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
299         goto lend;
300     }
301 
302     hIC = lpwfs->lpAppInfo;
303     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
304     {
305         WORKREQUEST workRequest;
306         struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
307 
308         workRequest.asyncproc = AsyncFtpPutFileProc;
309         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
310         req->lpszLocalFile = heap_strdupW(lpszLocalFile);
311         req->lpszNewRemoteFile = heap_strdupW(lpszNewRemoteFile);
312         req->dwFlags = dwFlags;
313         req->dwContext = dwContext;
314 
315         r = INTERNET_AsyncCall(&workRequest);
316     }
317     else
318     {
319         r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
320             lpszNewRemoteFile, dwFlags, dwContext);
321     }
322 
323 lend:
324     WININET_Release( &lpwfs->hdr );
325 
326     return r;
327 }
328 
329 /***********************************************************************
330  *           FTP_FtpPutFileW (Internal)
331  *
332  * Uploads a file to the FTP server
333  *
334  * RETURNS
335  *    TRUE on success
336  *    FALSE on failure
337  *
338  */
339 static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
340     LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
341 {
342     HANDLE hFile;
343     BOOL bSuccess = FALSE;
344     appinfo_t *hIC = NULL;
345     INT nResCode;
346 
347     TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
348 
349     /* Clear any error information */
350     INTERNET_SetLastError(0);
351 
352     /* Open file to be uploaded */
353     if (INVALID_HANDLE_VALUE ==
354         (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
355         /* Let CreateFile set the appropriate error */
356         return FALSE;
357 
358     hIC = lpwfs->lpAppInfo;
359 
360     SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
361 
362     if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
363     {
364         INT nDataSocket;
365 
366         /* Get data socket to server */
367         if (FTP_GetDataSocket(lpwfs, &nDataSocket))
368         {
369             FTP_SendData(lpwfs, nDataSocket, hFile);
370             closesocket(nDataSocket);
371             nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
372             if (nResCode)
373             {
374                 if (nResCode == 226)
375                     bSuccess = TRUE;
376                 else
377                     FTP_SetResponseError(nResCode);
378             }
379         }
380     }
381 
382     if (lpwfs->lstnSocket != -1)
383     {
384         closesocket(lpwfs->lstnSocket);
385         lpwfs->lstnSocket = -1;
386     }
387 
388     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
389     {
390         INTERNET_ASYNC_RESULT iar;
391 
392         iar.dwResult = (DWORD)bSuccess;
393         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
394         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
395             &iar, sizeof(INTERNET_ASYNC_RESULT));
396     }
397 
398     CloseHandle(hFile);
399 
400     return bSuccess;
401 }
402 
403 
404 /***********************************************************************
405  *           FtpSetCurrentDirectoryA (WININET.@)
406  *
407  * Change the working directory on the FTP server
408  *
409  * RETURNS
410  *    TRUE on success
411  *    FALSE on failure
412  *
413  */
414 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
415 {
416     LPWSTR lpwzDirectory;
417     BOOL ret;
418     
419     lpwzDirectory = heap_strdupAtoW(lpszDirectory);
420     ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
421     HeapFree(GetProcessHeap(), 0, lpwzDirectory);
422     return ret;
423 }
424 
425 
426 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
427 {
428     struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
429     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
430 
431     TRACE("%p\n", lpwfs);
432 
433     FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
434     HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
435 }
436 
437 /***********************************************************************
438  *           FtpSetCurrentDirectoryW (WININET.@)
439  *
440  * Change the working directory on the FTP server
441  *
442  * RETURNS
443  *    TRUE on success
444  *    FALSE on failure
445  *
446  */
447 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
448 {
449     ftp_session_t *lpwfs = NULL;
450     appinfo_t *hIC = NULL;
451     BOOL r = FALSE;
452 
453     if (!lpszDirectory)
454     {
455         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
456         goto lend;
457     }
458 
459     lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
460     if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
461     {
462         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
463         goto lend;
464     }
465 
466     if (lpwfs->download_in_progress != NULL)
467     {
468         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
469         goto lend;
470     }
471 
472     TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
473 
474     hIC = lpwfs->lpAppInfo;
475     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
476     {
477         WORKREQUEST workRequest;
478         struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
479 
480         workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
481         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
482         req = &workRequest.u.FtpSetCurrentDirectoryW;
483         req->lpszDirectory = heap_strdupW(lpszDirectory);
484 
485         r = INTERNET_AsyncCall(&workRequest);
486     }
487     else
488     {
489         r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
490     }
491 
492 lend:
493     if( lpwfs )
494         WININET_Release( &lpwfs->hdr );
495 
496     return r;
497 }
498 
499 
500 /***********************************************************************
501  *           FTP_FtpSetCurrentDirectoryW (Internal)
502  *
503  * Change the working directory on the FTP server
504  *
505  * RETURNS
506  *    TRUE on success
507  *    FALSE on failure
508  *
509  */
510 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
511 {
512     INT nResCode;
513     appinfo_t *hIC = NULL;
514     DWORD bSuccess = FALSE;
515 
516     TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
517 
518     /* Clear any error information */
519     INTERNET_SetLastError(0);
520 
521     hIC = lpwfs->lpAppInfo;
522     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
523         lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
524         goto lend;
525 
526     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
527 
528     if (nResCode)
529     {
530         if (nResCode == 250)
531             bSuccess = TRUE;
532         else
533             FTP_SetResponseError(nResCode);
534     }
535 
536 lend:
537     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
538     {
539         INTERNET_ASYNC_RESULT iar;
540 
541         iar.dwResult = bSuccess;
542         iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
543         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
544             &iar, sizeof(INTERNET_ASYNC_RESULT));
545     }
546     return bSuccess;
547 }
548 
549 
550 /***********************************************************************
551  *           FtpCreateDirectoryA (WININET.@)
552  *
553  * Create new directory on the FTP server
554  *
555  * RETURNS
556  *    TRUE on success
557  *    FALSE on failure
558  *
559  */
560 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
561 {
562     LPWSTR lpwzDirectory;
563     BOOL ret;
564     
565     lpwzDirectory = heap_strdupAtoW(lpszDirectory);
566     ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
567     HeapFree(GetProcessHeap(), 0, lpwzDirectory);
568     return ret;
569 }
570 
571 
572 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
573 {
574     struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
575     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
576 
577     TRACE(" %p\n", lpwfs);
578 
579     FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
580     HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
581 }
582 
583 /***********************************************************************
584  *           FtpCreateDirectoryW (WININET.@)
585  *
586  * Create new directory on the FTP server
587  *
588  * RETURNS
589  *    TRUE on success
590  *    FALSE on failure
591  *
592  */
593 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
594 {
595     ftp_session_t *lpwfs;
596     appinfo_t *hIC = NULL;
597     BOOL r = FALSE;
598 
599     lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
600     if (!lpwfs)
601     {
602         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
603         return FALSE;
604     }
605 
606     if (WH_HFTPSESSION != lpwfs->hdr.htype)
607     {
608         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
609         goto lend;
610     }
611 
612     if (lpwfs->download_in_progress != NULL)
613     {
614         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
615         goto lend;
616     }
617 
618     if (!lpszDirectory)
619     {
620         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
621         goto lend;
622     }
623 
624     hIC = lpwfs->lpAppInfo;
625     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
626     {
627         WORKREQUEST workRequest;
628         struct WORKREQ_FTPCREATEDIRECTORYW *req;
629 
630         workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
631         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
632         req = &workRequest.u.FtpCreateDirectoryW;
633         req->lpszDirectory = heap_strdupW(lpszDirectory);
634 
635         r = INTERNET_AsyncCall(&workRequest);
636     }
637     else
638     {
639         r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
640     }
641 lend:
642     WININET_Release( &lpwfs->hdr );
643 
644     return r;
645 }
646 
647 
648 /***********************************************************************
649  *           FTP_FtpCreateDirectoryW (Internal)
650  *
651  * Create new directory on the FTP server
652  *
653  * RETURNS
654  *    TRUE on success
655  *    FALSE on failure
656  *
657  */
658 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
659 {
660     INT nResCode;
661     BOOL bSuccess = FALSE;
662     appinfo_t *hIC = NULL;
663 
664     TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
665 
666     /* Clear any error information */
667     INTERNET_SetLastError(0);
668 
669     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
670         goto lend;
671 
672     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
673     if (nResCode)
674     {
675         if (nResCode == 257)
676             bSuccess = TRUE;
677         else
678             FTP_SetResponseError(nResCode);
679     }
680 
681 lend:
682     hIC = lpwfs->lpAppInfo;
683     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
684     {
685         INTERNET_ASYNC_RESULT iar;
686 
687         iar.dwResult = (DWORD)bSuccess;
688         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
689         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
690             &iar, sizeof(INTERNET_ASYNC_RESULT));
691     }
692 
693     return bSuccess;
694 }
695 
696 /***********************************************************************
697  *           FtpFindFirstFileA (WININET.@)
698  *
699  * Search the specified directory
700  *
701  * RETURNS
702  *    HINTERNET on success
703  *    NULL on failure
704  *
705  */
706 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
707     LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
708 {
709     LPWSTR lpwzSearchFile;
710     WIN32_FIND_DATAW wfd;
711     LPWIN32_FIND_DATAW lpFindFileDataW;
712     HINTERNET ret;
713     
714     lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
715     lpFindFileDataW = lpFindFileData?&wfd:NULL;
716     ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
717     HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
718     
719     if(lpFindFileData) {
720         WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
721     }
722     return ret;
723 }
724 
725 
726 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
727 {
728     struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
729     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
730 
731     TRACE("%p\n", lpwfs);
732 
733     FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
734        req->lpFindFileData, req->dwFlags, req->dwContext);
735     HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
736 }
737 
738 /***********************************************************************
739  *           FtpFindFirstFileW (WININET.@)
740  *
741  * Search the specified directory
742  *
743  * RETURNS
744  *    HINTERNET on success
745  *    NULL on failure
746  *
747  */
748 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
749     LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
750 {
751     ftp_session_t *lpwfs;
752     appinfo_t *hIC = NULL;
753     HINTERNET r = NULL;
754 
755     lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
756     if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
757     {
758         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
759         goto lend;
760     }
761 
762     if (lpwfs->download_in_progress != NULL)
763     {
764         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
765         goto lend;
766     }
767 
768     hIC = lpwfs->lpAppInfo;
769     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
770     {
771         WORKREQUEST workRequest;
772         struct WORKREQ_FTPFINDFIRSTFILEW *req;
773 
774         workRequest.asyncproc = AsyncFtpFindFirstFileProc;
775         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
776         req = &workRequest.u.FtpFindFirstFileW;
777         req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : heap_strdupW(lpszSearchFile);
778         req->lpFindFileData = lpFindFileData;
779         req->dwFlags = dwFlags;
780         req->dwContext= dwContext;
781 
782         INTERNET_AsyncCall(&workRequest);
783         r = NULL;
784     }
785     else
786     {
787         r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
788             dwFlags, dwContext);
789     }
790 lend:
791     if( lpwfs )
792         WININET_Release( &lpwfs->hdr );
793 
794     return r;
795 }
796 
797 
798 /***********************************************************************
799  *           FTP_FtpFindFirstFileW (Internal)
800  *
801  * Search the specified directory
802  *
803  * RETURNS
804  *    HINTERNET on success
805  *    NULL on failure
806  *
807  */
808 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
809     LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
810 {
811     INT nResCode;
812     appinfo_t *hIC = NULL;
813     HINTERNET hFindNext = NULL;
814 
815     TRACE("\n");
816 
817     /* Clear any error information */
818     INTERNET_SetLastError(0);
819 
820     if (!FTP_InitListenSocket(lpwfs))
821         goto lend;
822 
823     if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
824         goto lend;
825 
826     if (!FTP_SendPortOrPasv(lpwfs))
827         goto lend;
828 
829     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
830         lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
831         goto lend;
832 
833     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
834     if (nResCode)
835     {
836         if (nResCode == 125 || nResCode == 150)
837         {
838             INT nDataSocket;
839 
840             /* Get data socket to server */
841             if (FTP_GetDataSocket(lpwfs, &nDataSocket))
842             {
843                 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
844                 closesocket(nDataSocket);
845                 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
846                 if (nResCode != 226 && nResCode != 250)
847                     INTERNET_SetLastError(ERROR_NO_MORE_FILES);
848             }
849         }
850         else
851             FTP_SetResponseError(nResCode);
852     }
853 
854 lend:
855     if (lpwfs->lstnSocket != -1)
856     {
857         closesocket(lpwfs->lstnSocket);
858         lpwfs->lstnSocket = -1;
859     }
860 
861     hIC = lpwfs->lpAppInfo;
862     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
863     {
864         INTERNET_ASYNC_RESULT iar;
865 
866         if (hFindNext)
867         {
868             iar.dwResult = (DWORD_PTR)hFindNext;
869             iar.dwError = ERROR_SUCCESS;
870             SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
871                 &iar, sizeof(INTERNET_ASYNC_RESULT));
872         }
873 
874         iar.dwResult = (DWORD_PTR)hFindNext;
875         iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
876         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
877             &iar, sizeof(INTERNET_ASYNC_RESULT));
878     }
879 
880     return hFindNext;
881 }
882 
883 
884 /***********************************************************************
885  *           FtpGetCurrentDirectoryA (WININET.@)
886  *
887  * Retrieves the current directory
888  *
889  * RETURNS
890  *    TRUE on success
891  *    FALSE on failure
892  *
893  */
894 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
895     LPDWORD lpdwCurrentDirectory)
896 {
897     WCHAR *dir = NULL;
898     DWORD len;
899     BOOL ret;
900 
901     if(lpdwCurrentDirectory) {
902         len = *lpdwCurrentDirectory;
903         if(lpszCurrentDirectory)
904         {
905             dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
906             if (NULL == dir)
907             {
908                 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
909                 return FALSE;
910             }
911         }
912     }
913     ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
914 
915     if (ret && lpszCurrentDirectory)
916         WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
917 
918     if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
919     HeapFree(GetProcessHeap(), 0, dir);
920     return ret;
921 }
922 
923 
924 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
925 {
926     struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
927     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
928 
929     TRACE("%p\n", lpwfs);
930 
931     FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
932 }
933 
934 /***********************************************************************
935  *           FtpGetCurrentDirectoryW (WININET.@)
936  *
937  * Retrieves the current directory
938  *
939  * RETURNS
940  *    TRUE on success
941  *    FALSE on failure
942  *
943  */
944 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
945     LPDWORD lpdwCurrentDirectory)
946 {
947     ftp_session_t *lpwfs;
948     appinfo_t *hIC = NULL;
949     BOOL r = FALSE;
950 
951     TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
952 
953     lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
954     if (NULL == lpwfs)
955     {
956         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
957         goto lend;
958     }
959 
960     if (WH_HFTPSESSION != lpwfs->hdr.htype)
961     {
962         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
963         goto lend;
964     }
965 
966     if (!lpdwCurrentDirectory)
967     {
968         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
969         goto lend;
970     }
971 
972     if (lpszCurrentDirectory == NULL)
973     {
974         INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
975         goto lend;
976     }
977 
978     if (lpwfs->download_in_progress != NULL)
979     {
980         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
981         goto lend;
982     }
983 
984     hIC = lpwfs->lpAppInfo;
985     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
986     {
987         WORKREQUEST workRequest;
988         struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
989 
990         workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
991         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
992         req = &workRequest.u.FtpGetCurrentDirectoryW;
993         req->lpszDirectory = lpszCurrentDirectory;
994         req->lpdwDirectory = lpdwCurrentDirectory;
995 
996         r = INTERNET_AsyncCall(&workRequest);
997     }
998     else
999     {
1000         r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
1001             lpdwCurrentDirectory);
1002     }
1003 
1004 lend:
1005     if( lpwfs )
1006         WININET_Release( &lpwfs->hdr );
1007 
1008     return r;
1009 }
1010 
1011 
1012 /***********************************************************************
1013  *           FTP_FtpGetCurrentDirectoryW (Internal)
1014  *
1015  * Retrieves the current directory
1016  *
1017  * RETURNS
1018  *    TRUE on success
1019  *    FALSE on failure
1020  *
1021  */
1022 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
1023         LPDWORD lpdwCurrentDirectory)
1024 {
1025     INT nResCode;
1026     appinfo_t *hIC = NULL;
1027     DWORD bSuccess = FALSE;
1028 
1029     /* Clear any error information */
1030     INTERNET_SetLastError(0);
1031 
1032     hIC = lpwfs->lpAppInfo;
1033     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1034         lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1035         goto lend;
1036 
1037     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1038     if (nResCode)
1039     {
1040         if (nResCode == 257) /* Extract directory name */
1041         {
1042             DWORD firstpos, lastpos, len;
1043             LPWSTR lpszResponseBuffer = heap_strdupAtoW(INTERNET_GetResponseBuffer());
1044 
1045             for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1046             {
1047                 if ('"' == lpszResponseBuffer[lastpos])
1048                 {
1049                     if (!firstpos)
1050                         firstpos = lastpos;
1051                     else
1052                         break;
1053                 }
1054             }
1055             len = lastpos - firstpos;
1056             if (*lpdwCurrentDirectory >= len)
1057             {
1058                 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1059                 lpszCurrentDirectory[len - 1] = 0;
1060                 *lpdwCurrentDirectory = len;
1061                 bSuccess = TRUE;
1062             }
1063             else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1064 
1065             HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
1066         }
1067         else
1068             FTP_SetResponseError(nResCode);
1069     }
1070 
1071 lend:
1072     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1073     {
1074         INTERNET_ASYNC_RESULT iar;
1075 
1076         iar.dwResult = bSuccess;
1077         iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1078         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1079             &iar, sizeof(INTERNET_ASYNC_RESULT));
1080     }
1081 
1082     return bSuccess;
1083 }
1084 
1085 
1086 /***********************************************************************
1087  *           FTPFILE_Destroy(internal)
1088  *
1089  * Closes the file transfer handle. This also 'cleans' the data queue of
1090  * the 'transfer complete' message (this is a bit of a hack though :-/ )
1091  *
1092  */
1093 static void FTPFILE_Destroy(object_header_t *hdr)
1094 {
1095     ftp_file_t *lpwh = (ftp_file_t*) hdr;
1096     ftp_session_t *lpwfs = lpwh->lpFtpSession;
1097     INT nResCode;
1098 
1099     TRACE("\n");
1100 
1101     if (!lpwh->session_deleted)
1102         lpwfs->download_in_progress = NULL;
1103 
1104     if (lpwh->nDataSocket != -1)
1105         closesocket(lpwh->nDataSocket);
1106 
1107     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1108     if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1109 
1110     WININET_Release(&lpwh->lpFtpSession->hdr);
1111 
1112     HeapFree(GetProcessHeap(), 0, lpwh);
1113 }
1114 
1115 static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1116 {
1117     switch(option) {
1118     case INTERNET_OPTION_HANDLE_TYPE:
1119         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1120 
1121         if (*size < sizeof(ULONG))
1122             return ERROR_INSUFFICIENT_BUFFER;
1123 
1124         *size = sizeof(DWORD);
1125         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1126         return ERROR_SUCCESS;
1127     }
1128 
1129     return INET_QueryOption(option, buffer, size, unicode);
1130 }
1131 
1132 static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
1133 {
1134     ftp_file_t *file = (ftp_file_t*)hdr;
1135     int res;
1136 
1137     if (file->nDataSocket == -1)
1138         return ERROR_INTERNET_DISCONNECTED;
1139 
1140     /* FIXME: FTP should use NETCON_ stuff */
1141     res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1142     *read = res>0 ? res : 0;
1143 
1144     return res>=0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME*/
1145 }
1146 
1147 static DWORD FTPFILE_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
1148     DWORD flags, DWORD_PTR context)
1149 {
1150     return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1151 }
1152 
1153 static DWORD FTPFILE_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
1154     DWORD flags, DWORD_PTR context)
1155 {
1156     return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1157 }
1158 
1159 static BOOL FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1160 {
1161     ftp_file_t *lpwh = (ftp_file_t*) hdr;
1162     int res;
1163 
1164     res = send(lpwh->nDataSocket, buffer, size, 0);
1165 
1166     *written = res>0 ? res : 0;
1167     return res >= 0;
1168 }
1169 
1170 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
1171 {
1172     INTERNET_ASYNC_RESULT iar;
1173     BYTE buffer[4096];
1174     int available;
1175 
1176     TRACE("%p\n", file);
1177 
1178     available = recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1179 
1180     if(available != -1) {
1181         iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1182         iar.dwError = first_notif ? 0 : available;
1183     }else {
1184         iar.dwResult = 0;
1185         iar.dwError = INTERNET_GetLastError();
1186     }
1187 
1188     INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1189                           sizeof(INTERNET_ASYNC_RESULT));
1190 }
1191 
1192 static void FTPFILE_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
1193 {
1194     ftp_file_t *file = (ftp_file_t*)workRequest->hdr;
1195 
1196     FTP_ReceiveRequestData(file, FALSE);
1197 }
1198 
1199 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1200 {
1201     ftp_file_t *file = (ftp_file_t*) hdr;
1202     int retval, unread = 0;
1203 
1204     TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
1205 
1206 #ifdef FIONREAD
1207     retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
1208     if (!retval)
1209         TRACE("%d bytes of queued, but unread data\n", unread);
1210 #else
1211     FIXME("FIONREAD not available\n");
1212 #endif
1213 
1214     *available = unread;
1215 
1216     if(!unread) {
1217         BYTE byte;
1218 
1219         *available = 0;
1220 
1221         retval = recv(file->nDataSocket, &byte, 1, MSG_PEEK);
1222         if(retval > 0) {
1223             WORKREQUEST workRequest;
1224 
1225             *available = 0;
1226             workRequest.asyncproc = FTPFILE_AsyncQueryDataAvailableProc;
1227             workRequest.hdr = WININET_AddRef( &file->hdr );
1228 
1229             INTERNET_AsyncCall(&workRequest);
1230 
1231             return ERROR_IO_PENDING;
1232         }
1233     }
1234 
1235     return ERROR_SUCCESS;
1236 }
1237 
1238 
1239 static const object_vtbl_t FTPFILEVtbl = {
1240     FTPFILE_Destroy,
1241     NULL,
1242     FTPFILE_QueryOption,
1243     NULL,
1244     FTPFILE_ReadFile,
1245     FTPFILE_ReadFileExA,
1246     FTPFILE_ReadFileExW,
1247     FTPFILE_WriteFile,
1248     FTPFILE_QueryDataAvailable,
1249     NULL
1250 };
1251 
1252 /***********************************************************************
1253  *           FTP_FtpOpenFileW (Internal)
1254  *
1255  * Open a remote file for writing or reading
1256  *
1257  * RETURNS
1258  *    HINTERNET handle on success
1259  *    NULL on failure
1260  *
1261  */
1262 HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1263         LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1264         DWORD_PTR dwContext)
1265 {
1266     INT nDataSocket;
1267     BOOL bSuccess = FALSE;
1268     ftp_file_t *lpwh = NULL;
1269     appinfo_t *hIC = NULL;
1270     HINTERNET handle = NULL;
1271 
1272     TRACE("\n");
1273 
1274     /* Clear any error information */
1275     INTERNET_SetLastError(0);
1276 
1277     if (GENERIC_READ == fdwAccess)
1278     {
1279         /* Set up socket to retrieve data */
1280         bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1281     }
1282     else if (GENERIC_WRITE == fdwAccess)
1283     {
1284         /* Set up socket to send data */
1285         bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1286     }
1287 
1288     /* Get data socket to server */
1289     if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1290     {
1291         lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(ftp_file_t));
1292         lpwh->hdr.htype = WH_HFILE;
1293         lpwh->hdr.vtbl = &FTPFILEVtbl;
1294         lpwh->hdr.dwFlags = dwFlags;
1295         lpwh->hdr.dwContext = dwContext;
1296         lpwh->hdr.refs = 1;
1297         lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1298         lpwh->nDataSocket = nDataSocket;
1299         lpwh->session_deleted = FALSE;
1300 
1301         WININET_AddRef( &lpwfs->hdr );
1302         lpwh->lpFtpSession = lpwfs;
1303         list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1304         
1305         handle = WININET_AllocHandle( &lpwh->hdr );
1306         if( !handle )
1307             goto lend;
1308 
1309         /* Indicate that a download is currently in progress */
1310         lpwfs->download_in_progress = lpwh;
1311     }
1312 
1313     if (lpwfs->lstnSocket != -1)
1314     {
1315         closesocket(lpwfs->lstnSocket);
1316         lpwfs->lstnSocket = -1;
1317     }
1318 
1319     hIC = lpwfs->lpAppInfo;
1320     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1321     {
1322         INTERNET_ASYNC_RESULT iar;
1323 
1324         if (lpwh)
1325         {
1326             iar.dwResult = (DWORD_PTR)handle;
1327             iar.dwError = ERROR_SUCCESS;
1328             SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1329                 &iar, sizeof(INTERNET_ASYNC_RESULT));
1330         }
1331 
1332         if(bSuccess) {
1333             FTP_ReceiveRequestData(lpwh, TRUE);
1334         }else {
1335             iar.dwResult = 0;
1336             iar.dwError = INTERNET_GetLastError();
1337             SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1338                     &iar, sizeof(INTERNET_ASYNC_RESULT));
1339         }
1340     }
1341 
1342 lend:
1343     if( lpwh )
1344         WININET_Release( &lpwh->hdr );
1345 
1346     return handle;
1347 }
1348 
1349 
1350 /***********************************************************************
1351  *           FtpOpenFileA (WININET.@)
1352  *
1353  * Open a remote file for writing or reading
1354  *
1355  * RETURNS
1356  *    HINTERNET handle on success
1357  *    NULL on failure
1358  *
1359  */
1360 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1361     LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1362     DWORD_PTR dwContext)
1363 {
1364     LPWSTR lpwzFileName;
1365     HINTERNET ret;
1366 
1367     lpwzFileName = heap_strdupAtoW(lpszFileName);
1368     ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1369     HeapFree(GetProcessHeap(), 0, lpwzFileName);
1370     return ret;
1371 }
1372 
1373 
1374 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1375 {
1376     struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1377     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1378 
1379     TRACE("%p\n", lpwfs);
1380 
1381     FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1382         req->dwAccess, req->dwFlags, req->dwContext);
1383     HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1384 }
1385 
1386 /***********************************************************************
1387  *           FtpOpenFileW (WININET.@)
1388  *
1389  * Open a remote file for writing or reading
1390  *
1391  * RETURNS
1392  *    HINTERNET handle on success
1393  *    NULL on failure
1394  *
1395  */
1396 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1397     LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1398     DWORD_PTR dwContext)
1399 {
1400     ftp_session_t *lpwfs;
1401     appinfo_t *hIC = NULL;
1402     HINTERNET r = NULL;
1403 
1404     TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1405         debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1406 
1407     lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1408     if (!lpwfs)
1409     {
1410         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1411         return FALSE;
1412     }
1413 
1414     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1415     {
1416         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1417         goto lend;
1418     }
1419 
1420     if ((!lpszFileName) ||
1421         ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1422         ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1423     {
1424         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1425         goto lend;
1426     }
1427 
1428     if (lpwfs->download_in_progress != NULL)
1429     {
1430         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1431         goto lend;
1432     }
1433 
1434     hIC = lpwfs->lpAppInfo;
1435     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1436     {
1437         WORKREQUEST workRequest;
1438         struct WORKREQ_FTPOPENFILEW *req;
1439 
1440         workRequest.asyncproc = AsyncFtpOpenFileProc;
1441         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1442         req = &workRequest.u.FtpOpenFileW;
1443         req->lpszFilename = heap_strdupW(lpszFileName);
1444         req->dwAccess = fdwAccess;
1445         req->dwFlags = dwFlags;
1446         req->dwContext = dwContext;
1447 
1448         INTERNET_AsyncCall(&workRequest);
1449         r = NULL;
1450     }
1451     else
1452     {
1453         r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1454     }
1455 
1456 lend:
1457     WININET_Release( &lpwfs->hdr );
1458 
1459     return r;
1460 }
1461 
1462 
1463 /***********************************************************************
1464  *           FtpGetFileA (WININET.@)
1465  *
1466  * Retrieve file from the FTP server
1467  *
1468  * RETURNS
1469  *    TRUE on success
1470  *    FALSE on failure
1471  *
1472  */
1473 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1474     BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1475     DWORD_PTR dwContext)
1476 {
1477     LPWSTR lpwzRemoteFile;
1478     LPWSTR lpwzNewFile;
1479     BOOL ret;
1480     
1481     lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1482     lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1483     ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1484         dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1485     HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1486     HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1487     return ret;
1488 }
1489 
1490 
1491 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1492 {
1493     struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1494     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1495 
1496     TRACE("%p\n", lpwfs);
1497 
1498     FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1499              req->lpszNewFile, req->fFailIfExists,
1500              req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1501     HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1502     HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1503 }
1504 
1505 
1506 /***********************************************************************
1507  *           FtpGetFileW (WININET.@)
1508  *
1509  * Retrieve file from the FTP server
1510  *
1511  * RETURNS
1512  *    TRUE on success
1513  *    FALSE on failure
1514  *
1515  */
1516 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1517     BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1518     DWORD_PTR dwContext)
1519 {
1520     ftp_session_t *lpwfs;
1521     appinfo_t *hIC = NULL;
1522     BOOL r = FALSE;
1523 
1524     if (!lpszRemoteFile || !lpszNewFile)
1525     {
1526         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1527         return FALSE;
1528     }
1529 
1530     lpwfs = (ftp_session_t*) WININET_GetObject( hInternet );
1531     if (!lpwfs)
1532     {
1533         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1534         return FALSE;
1535     }
1536 
1537     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1538     {
1539         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1540         goto lend;
1541     }
1542 
1543     if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1544     {
1545         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1546         goto lend;
1547     }
1548 
1549     if (lpwfs->download_in_progress != NULL)
1550     {
1551         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1552         goto lend;
1553     }
1554     
1555     hIC = lpwfs->lpAppInfo;
1556     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1557     {
1558         WORKREQUEST workRequest;
1559         struct WORKREQ_FTPGETFILEW *req;
1560 
1561         workRequest.asyncproc = AsyncFtpGetFileProc;
1562         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1563         req = &workRequest.u.FtpGetFileW;
1564         req->lpszRemoteFile = heap_strdupW(lpszRemoteFile);
1565         req->lpszNewFile = heap_strdupW(lpszNewFile);
1566         req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1567         req->fFailIfExists = fFailIfExists;
1568         req->dwFlags = dwInternetFlags;
1569         req->dwContext = dwContext;
1570 
1571         r = INTERNET_AsyncCall(&workRequest);
1572     }
1573     else
1574     {
1575         r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1576            fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1577     }
1578 
1579 lend:
1580     WININET_Release( &lpwfs->hdr );
1581 
1582     return r;
1583 }
1584 
1585 
1586 /***********************************************************************
1587  *           FTP_FtpGetFileW (Internal)
1588  *
1589  * Retrieve file from the FTP server
1590  *
1591  * RETURNS
1592  *    TRUE on success
1593  *    FALSE on failure
1594  *
1595  */
1596 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1597         BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1598         DWORD_PTR dwContext)
1599 {
1600     BOOL bSuccess = FALSE;
1601     HANDLE hFile;
1602     appinfo_t *hIC = NULL;
1603 
1604     TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1605 
1606     /* Clear any error information */
1607     INTERNET_SetLastError(0);
1608 
1609     /* Ensure we can write to lpszNewfile by opening it */
1610     hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1611         CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1612     if (INVALID_HANDLE_VALUE == hFile)
1613         return FALSE;
1614 
1615     /* Set up socket to retrieve data */
1616     if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1617     {
1618         INT nDataSocket;
1619 
1620         /* Get data socket to server */
1621         if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1622         {
1623             INT nResCode;
1624 
1625             /* Receive data */
1626             FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1627             closesocket(nDataSocket);
1628 
1629             nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1630             if (nResCode)
1631             {
1632                 if (nResCode == 226)
1633                     bSuccess = TRUE;
1634                 else
1635                     FTP_SetResponseError(nResCode);
1636             }
1637         }
1638     }
1639 
1640     if (lpwfs->lstnSocket != -1)
1641     {
1642         closesocket(lpwfs->lstnSocket);
1643         lpwfs->lstnSocket = -1;
1644     }
1645 
1646     CloseHandle(hFile);
1647 
1648     hIC = lpwfs->lpAppInfo;
1649     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1650     {
1651         INTERNET_ASYNC_RESULT iar;
1652 
1653         iar.dwResult = (DWORD)bSuccess;
1654         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1655         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1656             &iar, sizeof(INTERNET_ASYNC_RESULT));
1657     }
1658 
1659     if (!bSuccess) DeleteFileW(lpszNewFile);
1660     return bSuccess;
1661 }
1662 
1663 /***********************************************************************
1664  *           FtpGetFileSize  (WININET.@)
1665  */
1666 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1667 {
1668     FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1669 
1670     if (lpdwFileSizeHigh)
1671         *lpdwFileSizeHigh = 0;
1672 
1673     return 0;
1674 }
1675 
1676 /***********************************************************************
1677  *           FtpDeleteFileA  (WININET.@)
1678  *
1679  * Delete a file on the ftp server
1680  *
1681  * RETURNS
1682  *    TRUE on success
1683  *    FALSE on failure
1684  *
1685  */
1686 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1687 {
1688     LPWSTR lpwzFileName;
1689     BOOL ret;
1690     
1691     lpwzFileName = heap_strdupAtoW(lpszFileName);
1692     ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1693     HeapFree(GetProcessHeap(), 0, lpwzFileName);
1694     return ret;
1695 }
1696 
1697 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1698 {
1699     struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1700     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1701 
1702     TRACE("%p\n", lpwfs);
1703 
1704     FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1705     HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1706 }
1707 
1708 /***********************************************************************
1709  *           FtpDeleteFileW  (WININET.@)
1710  *
1711  * Delete a file on the ftp server
1712  *
1713  * RETURNS
1714  *    TRUE on success
1715  *    FALSE on failure
1716  *
1717  */
1718 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1719 {
1720     ftp_session_t *lpwfs;
1721     appinfo_t *hIC = NULL;
1722     BOOL r = FALSE;
1723 
1724     lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1725     if (!lpwfs)
1726     {
1727         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1728         return FALSE;
1729     }
1730 
1731     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1732     {
1733         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1734         goto lend;
1735     }
1736 
1737     if (lpwfs->download_in_progress != NULL)
1738     {
1739         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1740         goto lend;
1741     }
1742 
1743     if (!lpszFileName)
1744     {
1745         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1746         goto lend;
1747     }
1748 
1749     hIC = lpwfs->lpAppInfo;
1750     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1751     {
1752         WORKREQUEST workRequest;
1753         struct WORKREQ_FTPDELETEFILEW *req;
1754 
1755         workRequest.asyncproc = AsyncFtpDeleteFileProc;
1756         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1757         req = &workRequest.u.FtpDeleteFileW;
1758         req->lpszFilename = heap_strdupW(lpszFileName);
1759 
1760         r = INTERNET_AsyncCall(&workRequest);
1761     }
1762     else
1763     {
1764         r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1765     }
1766 
1767 lend:
1768     WININET_Release( &lpwfs->hdr );
1769 
1770     return r;
1771 }
1772 
1773 /***********************************************************************
1774  *           FTP_FtpDeleteFileW  (Internal)
1775  *
1776  * Delete a file on the ftp server
1777  *
1778  * RETURNS
1779  *    TRUE on success
1780  *    FALSE on failure
1781  *
1782  */
1783 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1784 {
1785     INT nResCode;
1786     BOOL bSuccess = FALSE;
1787     appinfo_t *hIC = NULL;
1788 
1789     TRACE("%p\n", lpwfs);
1790 
1791     /* Clear any error information */
1792     INTERNET_SetLastError(0);
1793 
1794     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1795         goto lend;
1796 
1797     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1798     if (nResCode)
1799     {
1800         if (nResCode == 250)
1801             bSuccess = TRUE;
1802         else
1803             FTP_SetResponseError(nResCode);
1804     }
1805 lend:
1806     hIC = lpwfs->lpAppInfo;
1807     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1808     {
1809         INTERNET_ASYNC_RESULT iar;
1810 
1811         iar.dwResult = (DWORD)bSuccess;
1812         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1813         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1814             &iar, sizeof(INTERNET_ASYNC_RESULT));
1815     }
1816 
1817     return bSuccess;
1818 }
1819 
1820 
1821 /***********************************************************************
1822  *           FtpRemoveDirectoryA  (WININET.@)
1823  *
1824  * Remove a directory on the ftp server
1825  *
1826  * RETURNS
1827  *    TRUE on success
1828  *    FALSE on failure
1829  *
1830  */
1831 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1832 {
1833     LPWSTR lpwzDirectory;
1834     BOOL ret;
1835     
1836     lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1837     ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1838     HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1839     return ret;
1840 }
1841 
1842 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1843 {
1844     struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1845     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1846 
1847     TRACE("%p\n", lpwfs);
1848 
1849     FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1850     HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1851 }
1852 
1853 /***********************************************************************
1854  *           FtpRemoveDirectoryW  (WININET.@)
1855  *
1856  * Remove a directory on the ftp server
1857  *
1858  * RETURNS
1859  *    TRUE on success
1860  *    FALSE on failure
1861  *
1862  */
1863 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1864 {
1865     ftp_session_t *lpwfs;
1866     appinfo_t *hIC = NULL;
1867     BOOL r = FALSE;
1868 
1869     lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1870     if (!lpwfs)
1871     {
1872         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1873         return FALSE;
1874     }
1875 
1876     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1877     {
1878         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1879         goto lend;
1880     }
1881 
1882     if (lpwfs->download_in_progress != NULL)
1883     {
1884         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1885         goto lend;
1886     }
1887 
1888     if (!lpszDirectory)
1889     {
1890         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1891         goto lend;
1892     }
1893 
1894     hIC = lpwfs->lpAppInfo;
1895     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1896     {
1897         WORKREQUEST workRequest;
1898         struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1899 
1900         workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1901         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1902         req = &workRequest.u.FtpRemoveDirectoryW;
1903         req->lpszDirectory = heap_strdupW(lpszDirectory);
1904 
1905         r = INTERNET_AsyncCall(&workRequest);
1906     }
1907     else
1908     {
1909         r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1910     }
1911 
1912 lend:
1913     WININET_Release( &lpwfs->hdr );
1914 
1915     return r;
1916 }
1917 
1918 /***********************************************************************
1919  *           FTP_FtpRemoveDirectoryW  (Internal)
1920  *
1921  * Remove a directory on the ftp server
1922  *
1923  * RETURNS
1924  *    TRUE on success
1925  *    FALSE on failure
1926  *
1927  */
1928 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
1929 {
1930     INT nResCode;
1931     BOOL bSuccess = FALSE;
1932     appinfo_t *hIC = NULL;
1933 
1934     TRACE("\n");
1935 
1936     /* Clear any error information */
1937     INTERNET_SetLastError(0);
1938 
1939     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1940         goto lend;
1941 
1942     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1943     if (nResCode)
1944     {
1945         if (nResCode == 250)
1946             bSuccess = TRUE;
1947         else
1948             FTP_SetResponseError(nResCode);
1949     }
1950 
1951 lend:
1952     hIC = lpwfs->lpAppInfo;
1953     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1954     {
1955         INTERNET_ASYNC_RESULT iar;
1956 
1957         iar.dwResult = (DWORD)bSuccess;
1958         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1959         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1960             &iar, sizeof(INTERNET_ASYNC_RESULT));
1961     }
1962 
1963     return bSuccess;
1964 }
1965 
1966 
1967 /***********************************************************************
1968  *           FtpRenameFileA  (WININET.@)
1969  *
1970  * Rename a file on the ftp server
1971  *
1972  * RETURNS
1973  *    TRUE on success
1974  *    FALSE on failure
1975  *
1976  */
1977 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1978 {
1979     LPWSTR lpwzSrc;
1980     LPWSTR lpwzDest;
1981     BOOL ret;
1982     
1983     lpwzSrc = heap_strdupAtoW(lpszSrc);
1984     lpwzDest = heap_strdupAtoW(lpszDest);
1985     ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1986     HeapFree(GetProcessHeap(), 0, lpwzSrc);
1987     HeapFree(GetProcessHeap(), 0, lpwzDest);
1988     return ret;
1989 }
1990 
1991 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
1992 {
1993     struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
1994     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1995 
1996     TRACE("%p\n", lpwfs);
1997 
1998     FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
1999     HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
2000     HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
2001 }
2002 
2003 /***********************************************************************
2004  *           FtpRenameFileW  (WININET.@)
2005  *
2006  * Rename a file on the ftp server
2007  *
2008  * RETURNS
2009  *    TRUE on success
2010  *    FALSE on failure
2011  *
2012  */
2013 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2014 {
2015     ftp_session_t *lpwfs;
2016     appinfo_t *hIC = NULL;
2017     BOOL r = FALSE;
2018 
2019     lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
2020     if (!lpwfs)
2021     {
2022         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2023         return FALSE;
2024     }
2025 
2026     if (WH_HFTPSESSION != lpwfs->hdr.htype)
2027     {
2028         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2029         goto lend;
2030     }
2031 
2032     if (lpwfs->download_in_progress != NULL)
2033     {
2034         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2035         goto lend;
2036     }
2037 
2038     if (!lpszSrc || !lpszDest)
2039     {
2040         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2041         goto lend;
2042     }
2043 
2044     hIC = lpwfs->lpAppInfo;
2045     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2046     {
2047         WORKREQUEST workRequest;
2048         struct WORKREQ_FTPRENAMEFILEW *req;
2049 
2050         workRequest.asyncproc = AsyncFtpRenameFileProc;
2051         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
2052         req = &workRequest.u.FtpRenameFileW;
2053         req->lpszSrcFile = heap_strdupW(lpszSrc);
2054         req->lpszDestFile = heap_strdupW(lpszDest);
2055 
2056         r = INTERNET_AsyncCall(&workRequest);
2057     }
2058     else
2059     {
2060         r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2061     }
2062 
2063 lend:
2064     WININET_Release( &lpwfs->hdr );
2065 
2066     return r;
2067 }
2068 
2069 /***********************************************************************
2070  *           FTP_FtpRenameFileW  (Internal)
2071  *
2072  * Rename a file on the ftp server
2073  *
2074  * RETURNS
2075  *    TRUE on success
2076  *    FALSE on failure
2077  *
2078  */
2079 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2080 {
2081     INT nResCode;
2082     BOOL bSuccess = FALSE;
2083     appinfo_t *hIC = NULL;
2084 
2085     TRACE("\n");
2086 
2087     /* Clear any error information */
2088     INTERNET_SetLastError(0);
2089 
2090     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2091         goto lend;
2092 
2093     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2094     if (nResCode == 350)
2095     {
2096         if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2097             goto lend;
2098 
2099         nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2100     }
2101 
2102     if (nResCode == 250)
2103         bSuccess = TRUE;
2104     else
2105         FTP_SetResponseError(nResCode);
2106 
2107 lend:
2108     hIC = lpwfs->lpAppInfo;
2109     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2110     {
2111         INTERNET_ASYNC_RESULT iar;
2112 
2113         iar.dwResult = (DWORD)bSuccess;
2114         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2115         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2116             &iar, sizeof(INTERNET_ASYNC_RESULT));
2117     }
2118 
2119     return bSuccess;
2120 }
2121 
2122 /***********************************************************************
2123  *           FtpCommandA  (WININET.@)
2124  */
2125 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2126                          LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2127 {
2128     BOOL r;
2129     WCHAR *cmdW;
2130 
2131     TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2132           debugstr_a(lpszCommand), dwContext, phFtpCommand);
2133 
2134     if (fExpectResponse)
2135     {
2136         FIXME("data connection not supported\n");
2137         return FALSE;
2138     }
2139 
2140     if (!lpszCommand || !lpszCommand[0])
2141     {
2142         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2143         return FALSE;
2144     }
2145 
2146     if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2147     {
2148         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2149         return FALSE;
2150     }
2151 
2152     r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2153 
2154     HeapFree(GetProcessHeap(), 0, cmdW);
2155     return r;
2156 }
2157 
2158 /***********************************************************************
2159  *           FtpCommandW  (WININET.@)
2160  */
2161 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2162                          LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2163 {
2164     BOOL r = FALSE;
2165     ftp_session_t *lpwfs;
2166     LPSTR cmd = NULL;
2167     DWORD len, nBytesSent= 0;
2168     INT nResCode, nRC = 0;
2169 
2170     TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2171            debugstr_w(lpszCommand), dwContext, phFtpCommand);
2172 
2173     if (!lpszCommand || !lpszCommand[0])
2174     {
2175         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2176         return FALSE;
2177     }
2178 
2179     if (fExpectResponse)
2180     {
2181         FIXME("data connection not supported\n");
2182         return FALSE;
2183     }
2184 
2185     lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
2186     if (!lpwfs)
2187     {
2188         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2189         return FALSE;
2190     }
2191 
2192     if (WH_HFTPSESSION != lpwfs->hdr.htype)
2193     {
2194         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2195         goto lend;
2196     }
2197 
2198     if (lpwfs->download_in_progress != NULL)
2199     {
2200         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2201         goto lend;
2202     }
2203 
2204     len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2205     if ((cmd = HeapAlloc(GetProcessHeap(), 0, len )))
2206         WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2207     else
2208     {
2209         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2210         goto lend;
2211     }
2212 
2213     strcat(cmd, szCRLF);
2214     len--;
2215 
2216     TRACE("Sending (%s) len(%d)\n", cmd, len);
2217     while ((nBytesSent < len) && (nRC != -1))
2218     {
2219         nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2220         if (nRC != -1)
2221         {
2222             nBytesSent += nRC;
2223             TRACE("Sent %d bytes\n", nRC);
2224         }
2225     }
2226 
2227     if (nBytesSent)
2228     {
2229         nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2230         if (nResCode > 0 && nResCode < 400)
2231             r = TRUE;
2232         else
2233             FTP_SetResponseError(nResCode);
2234     }
2235 
2236 lend:
2237     WININET_Release( &lpwfs->hdr );
2238     HeapFree(GetProcessHeap(), 0, cmd);
2239     return r;
2240 }
2241 
2242 
2243 /***********************************************************************
2244  *           FTPSESSION_Destroy (internal)
2245  *
2246  * Deallocate session handle
2247  */
2248 static void FTPSESSION_Destroy(object_header_t *hdr)
2249 {
2250     ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2251 
2252     TRACE("\n");
2253 
2254     WININET_Release(&lpwfs->lpAppInfo->hdr);
2255 
2256     HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2257     HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2258     HeapFree(GetProcessHeap(), 0, lpwfs);
2259 }
2260 
2261 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2262 {
2263     ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2264 
2265     TRACE("\n");
2266 
2267     SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2268                       INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2269 
2270     if (lpwfs->download_in_progress != NULL)
2271         lpwfs->download_in_progress->session_deleted = TRUE;
2272 
2273      if (lpwfs->sndSocket != -1)
2274          closesocket(lpwfs->sndSocket);
2275 
2276      if (lpwfs->lstnSocket != -1)
2277          closesocket(lpwfs->lstnSocket);
2278 
2279     if (lpwfs->pasvSocket != -1)
2280         closesocket(lpwfs->pasvSocket);
2281 
2282     SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2283                       INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2284 }
2285 
2286 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2287 {
2288     switch(option) {
2289     case INTERNET_OPTION_HANDLE_TYPE:
2290         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2291 
2292         if (*size < sizeof(ULONG))
2293             return ERROR_INSUFFICIENT_BUFFER;
2294 
2295         *size = sizeof(DWORD);
2296         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2297         return ERROR_SUCCESS;
2298     }
2299 
2300     return INET_QueryOption(option, buffer, size, unicode);
2301 }
2302 
2303 static const object_vtbl_t FTPSESSIONVtbl = {
2304     FTPSESSION_Destroy,
2305     FTPSESSION_CloseConnection,
2306     FTPSESSION_QueryOption,
2307     NULL,
2308     NULL,
2309     NULL,
2310     NULL,
2311     NULL,
2312     NULL
2313 };
2314 
2315 
2316 /***********************************************************************
2317  *           FTP_Connect (internal)
2318  *
2319  * Connect to a ftp server
2320  *
2321  * RETURNS
2322  *   HINTERNET a session handle on success
2323  *   NULL on failure
2324  *
2325  * NOTES:
2326  *
2327  * Windows uses 'anonymous' as the username, when given a NULL username
2328  * and a NULL password. The password is first looked up in:
2329  *
2330  * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2331  *
2332  * If this entry is not present it uses the current username as the password.
2333  *
2334  */
2335 
2336 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2337         INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2338         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2339         DWORD dwInternalFlags)
2340 {
2341     static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2342                                    'M','i','c','r','o','s','o','f','t','\\',
2343                                    'W','i','n','d','o','w','s','\\',
2344                                    'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2345                                    'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2346     static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2347     static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2348     static const WCHAR szEmpty[] = {'\0'};
2349     struct sockaddr_in socketAddr;
2350     INT nsocket = -1;
2351     UINT sock_namelen;
2352     BOOL bSuccess = FALSE;
2353     ftp_session_t *lpwfs = NULL;
2354     HINTERNET handle = NULL;
2355 
2356     TRACE("%p  Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2357             hIC, debugstr_w(lpszServerName),
2358             nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2359 
2360     assert( hIC->hdr.htype == WH_HINIT );
2361 
2362     if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2363     {
2364         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2365         goto lerror;
2366     }
2367     
2368     lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(ftp_session_t));
2369     if (NULL == lpwfs)
2370     {
2371         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2372         goto lerror;
2373     }
2374 
2375     if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2376         nServerPort = INTERNET_DEFAULT_FTP_PORT;
2377 
2378     lpwfs->hdr.htype = WH_HFTPSESSION;
2379     lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
2380     lpwfs->hdr.dwFlags = dwFlags;
2381     lpwfs->hdr.dwContext = dwContext;
2382     lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2383     lpwfs->hdr.refs = 1;
2384     lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2385     lpwfs->download_in_progress = NULL;
2386     lpwfs->sndSocket = -1;
2387     lpwfs->lstnSocket = -1;
2388     lpwfs->pasvSocket = -1;
2389 
2390     WININET_AddRef( &hIC->hdr );
2391     lpwfs->lpAppInfo = hIC;
2392     list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2393 
2394     handle = WININET_AllocHandle( &lpwfs->hdr );
2395     if( !handle )
2396     {
2397         ERR("Failed to alloc handle\n");
2398         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2399         goto lerror;
2400     }
2401 
2402     if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2403         if(strchrW(hIC->lpszProxy, ' '))
2404             FIXME("Several proxies not implemented.\n");
2405         if(hIC->lpszProxyBypass)
2406             FIXME("Proxy bypass is ignored.\n");
2407     }
2408     if (!lpszUserName || !strlenW(lpszUserName)) {
2409         HKEY key;
2410         WCHAR szPassword[MAX_PATH];
2411         DWORD len = sizeof(szPassword);
2412 
2413         lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2414 
2415         RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2416         if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2417             /* Nothing in the registry, get the username and use that as the password */
2418             if (!GetUserNameW(szPassword, &len)) {
2419                 /* Should never get here, but use an empty password as failsafe */
2420                 strcpyW(szPassword, szEmpty);
2421             }
2422         }
2423         RegCloseKey(key);
2424 
2425         TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2426         lpwfs->lpszPassword = heap_strdupW(szPassword);
2427     }
2428     else {
2429         lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2430         lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2431     }
2432     
2433     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2434     if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2435     {
2436         INTERNET_ASYNC_RESULT iar;
2437 
2438         iar.dwResult = (DWORD_PTR)handle;
2439         iar.dwError = ERROR_SUCCESS;
2440 
2441         SendAsyncCallback(&hIC->hdr, dwContext,
2442                       INTERNET_STATUS_HANDLE_CREATED, &iar,
2443                       sizeof(INTERNET_ASYNC_RESULT));
2444     }
2445         
2446     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2447         (LPWSTR) lpszServerName, strlenW(lpszServerName));
2448 
2449     sock_namelen = sizeof(socketAddr);
2450     if (!GetAddress(lpszServerName, nServerPort,
2451         (struct sockaddr *)&socketAddr, &sock_namelen))
2452     {
2453         INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2454         goto lerror;
2455     }
2456 
2457     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2458         (LPWSTR) lpszServerName, strlenW(lpszServerName));
2459 
2460     if (socketAddr.sin_family != AF_INET)
2461     {
2462         WARN("unsupported address family %d\n", socketAddr.sin_family);
2463         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2464         goto lerror;
2465     }
2466     nsocket = socket(AF_INET,SOCK_STREAM,0);
2467     if (nsocket == -1)
2468     {
2469         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2470         goto lerror;
2471     }
2472 
2473     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2474         &socketAddr, sock_namelen);
2475 
2476     if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2477     {
2478         ERR("Unable to connect (%s)\n", strerror(errno));
2479         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2480         closesocket(nsocket);
2481     }
2482     else
2483     {
2484         TRACE("Connected to server\n");
2485         lpwfs->sndSocket = nsocket;
2486         SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2487             &socketAddr, sock_namelen);
2488 
2489         sock_namelen = sizeof(lpwfs->socketAddress);
2490         getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2491 
2492         if (FTP_ConnectToHost(lpwfs))
2493         {
2494             TRACE("Successfully logged into server\n");
2495             bSuccess = TRUE;
2496         }
2497     }
2498 
2499 lerror:
2500     if (lpwfs) WININET_Release( &lpwfs->hdr );
2501 
2502     if (!bSuccess && handle)
2503     {
2504         WININET_FreeHandle( handle );
2505         handle = NULL;
2506     }
2507 
2508     return handle;
2509 }
2510 
2511 
2512 /***********************************************************************
2513  *           FTP_ConnectToHost (internal)
2514  *
2515  * Connect to a ftp server
2516  *
2517  * RETURNS
2518  *   TRUE on success
2519  *   NULL on failure
2520  *
2521  */
2522 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2523 {
2524     INT nResCode;
2525     BOOL bSuccess = FALSE;
2526 
2527     TRACE("\n");
2528     FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2529 
2530     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2531         goto lend;
2532 
2533     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2534     if (nResCode)
2535     {
2536         /* Login successful... */
2537         if (nResCode == 230)
2538             bSuccess = TRUE;
2539         /* User name okay, need password... */
2540         else if (nResCode == 331)
2541             bSuccess = FTP_SendPassword(lpwfs);
2542         /* Need account for login... */
2543         else if (nResCode == 332)
2544             bSuccess = FTP_SendAccount(lpwfs);
2545         else
2546             FTP_SetResponseError(nResCode);
2547     }
2548 
2549     TRACE("Returning %d\n", bSuccess);
2550 lend:
2551     return bSuccess;
2552 }
2553 
2554 
2555 /***********************************************************************
2556  *           FTP_SendCommandA (internal)
2557  *
2558  * Send command to server
2559  *
2560  * RETURNS
2561  *   TRUE on success
2562  *   NULL on failure
2563  *
2564  */
2565 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2566         INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2567 {
2568         DWORD len;
2569         CHAR *buf;
2570         DWORD nBytesSent = 0;
2571         int nRC = 0;
2572         DWORD dwParamLen;
2573 
2574         TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2575 
2576         if (lpfnStatusCB)
2577         {
2578             lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2579         }
2580 
2581         dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2582         len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2583         if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2584         {
2585             INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2586             return FALSE;
2587         }
2588         sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2589                 dwParamLen ? lpszParam : "", szCRLF);
2590 
2591         TRACE("Sending (%s) len(%d)\n", buf, len);
2592         while((nBytesSent < len) && (nRC != -1))
2593         {
2594                 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2595                 nBytesSent += nRC;
2596         }
2597 
2598         HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2599 
2600         if (lpfnStatusCB)
2601         {
2602             lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2603                          &nBytesSent, sizeof(DWORD));
2604         }
2605 
2606         TRACE("Sent %d bytes\n", nBytesSent);
2607         return (nRC != -1);
2608 }
2609 
2610 /***********************************************************************
2611  *           FTP_SendCommand (internal)
2612  *
2613  * Send command to server
2614  *
2615  * RETURNS
2616  *   TRUE on success
2617  *   NULL on failure
2618  *
2619  */
2620 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2621         INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2622 {
2623     BOOL ret;
2624     LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2625     ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2626     HeapFree(GetProcessHeap(), 0, lpszParamA);
2627     return ret;
2628 }
2629 
2630 /***********************************************************************
2631  *           FTP_ReceiveResponse (internal)
2632  *
2633  * Receive response from server
2634  *
2635  * RETURNS
2636  *   Reply code on success
2637  *   0 on failure
2638  *
2639  */
2640 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2641 {
2642     LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2643     DWORD nRecv;
2644     INT rc = 0;
2645     char firstprefix[5];
2646     BOOL multiline = FALSE;
2647 
2648     TRACE("socket(%d)\n", lpwfs->sndSocket);
2649 
2650     SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2651 
2652     while(1)
2653     {
2654         if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2655             goto lerror;
2656 
2657         if (nRecv >= 3)
2658         {
2659             if(!multiline)
2660             {
2661                 if(lpszResponse[3] != '-')
2662                     break;
2663                 else
2664                 {  /* Start of multiline response.  Loop until we get "nnn " */
2665                     multiline = TRUE;
2666                     memcpy(firstprefix, lpszResponse, 3);
2667                     firstprefix[3] = ' ';
2668                     firstprefix[4] = '\0';
2669                 }
2670             }
2671             else
2672             {
2673                 if(!memcmp(firstprefix, lpszResponse, 4))
2674                     break;
2675             }
2676         }
2677     }
2678 
2679     if (nRecv >= 3)
2680     {
2681         rc = atoi(lpszResponse);
2682 
2683         SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2684                     &nRecv, sizeof(DWORD));
2685     }
2686 
2687 lerror:
2688     TRACE("return %d\n", rc);
2689     return rc;
2690 }
2691 
2692 
2693 /***********************************************************************
2694  *           FTP_SendPassword (internal)
2695  *
2696  * Send password to ftp server
2697  *
2698  * RETURNS
2699  *   TRUE on success
2700  *   NULL on failure
2701  *
2702  */
2703 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2704 {
2705     INT nResCode;
2706     BOOL bSuccess = FALSE;
2707 
2708     TRACE("\n");
2709     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2710         goto lend;
2711 
2712     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2713     if (nResCode)
2714     {
2715         TRACE("Received reply code %d\n", nResCode);
2716         /* Login successful... */
2717         if (nResCode == 230)
2718             bSuccess = TRUE;
2719         /* Command not implemented, superfluous at the server site... */
2720         /* Need account for login... */
2721         else if (nResCode == 332)
2722             bSuccess = FTP_SendAccount(lpwfs);
2723         else
2724             FTP_SetResponseError(nResCode);
2725     }
2726 
2727 lend:
2728     TRACE("Returning %d\n", bSuccess);
2729     return bSuccess;
2730 }
2731 
2732 
2733 /***********************************************************************
2734  *           FTP_SendAccount (internal)
2735  *
2736  *
2737  *
2738  * RETURNS
2739  *   TRUE on success
2740  *   FALSE on failure
2741  *
2742  */
2743 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2744 {
2745     INT nResCode;
2746     BOOL bSuccess = FALSE;
2747 
2748     TRACE("\n");
2749     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2750         goto lend;
2751 
2752     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2753     if (nResCode)
2754         bSuccess = TRUE;
2755     else
2756         FTP_SetResponseError(nResCode);
2757 
2758 lend:
2759     return bSuccess;
2760 }
2761 
2762 
2763 /***********************************************************************
2764  *           FTP_SendStore (internal)
2765  *
2766  * Send request to upload file to ftp server
2767  *
2768  * RETURNS
2769  *   TRUE on success
2770  *   FALSE on failure
2771  *
2772  */
2773 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2774 {
2775     INT nResCode;
2776     BOOL bSuccess = FALSE;
2777 
2778     TRACE("\n");
2779     if (!FTP_InitListenSocket(lpwfs))
2780         goto lend;
2781 
2782     if (!FTP_SendType(lpwfs, dwType))
2783         goto lend;
2784 
2785     if (!FTP_SendPortOrPasv(lpwfs))
2786         goto lend;
2787 
2788     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2789             goto lend;
2790     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2791     if (nResCode)
2792     {
2793         if (nResCode == 150 || nResCode == 125)
2794             bSuccess = TRUE;
2795         else
2796             FTP_SetResponseError(nResCode);
2797     }
2798 
2799 lend:
2800     if (!bSuccess && lpwfs->lstnSocket != -1)
2801     {
2802         closesocket(lpwfs->lstnSocket);
2803         lpwfs->lstnSocket = -1;
2804     }
2805 
2806     return bSuccess;
2807 }
2808 
2809 
2810 /***********************************************************************
2811  *           FTP_InitListenSocket (internal)
2812  *
2813  * Create a socket to listen for server response
2814  *
2815  * RETURNS
2816  *   TRUE on success
2817  *   FALSE on failure
2818  *
2819  */
2820 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2821 {
2822     BOOL bSuccess = FALSE;
2823     socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2824 
2825     TRACE("\n");
2826 
2827     lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2828     if (lpwfs->lstnSocket == -1)
2829     {
2830         TRACE("Unable to create listening socket\n");
2831             goto lend;
2832     }
2833 
2834     /* We obtain our ip addr from the name of the command channel socket */
2835     lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2836 
2837     /* and get the system to assign us a port */
2838     lpwfs->lstnSocketAddress.sin_port = htons(0);
2839 
2840     if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2841     {
2842         TRACE("Unable to bind socket\n");
2843         goto lend;
2844     }
2845 
2846     if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2847     {
2848         TRACE("listen failed\n");
2849         goto lend;
2850     }
2851 
2852     if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2853         bSuccess = TRUE;
2854 
2855 lend:
2856     if (!bSuccess && lpwfs->lstnSocket != -1)
2857     {
2858         closesocket(lpwfs->lstnSocket);
2859         lpwfs->lstnSocket = -1;
2860     }
2861 
2862     return bSuccess;
2863 }
2864 
2865 
2866 /***********************************************************************
2867  *           FTP_SendType (internal)
2868  *
2869  * Tell server type of data being transferred
2870  *
2871  * RETURNS
2872  *   TRUE on success
2873  *   FALSE on failure
2874  *
2875  * W98SE doesn't cache the type that's currently set
2876  * (i.e. it sends it always),
2877  * so we probably don't want to do that either.
2878  */
2879 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2880 {
2881     INT nResCode;
2882     WCHAR type[] = { 'I','\0' };
2883     BOOL bSuccess = FALSE;
2884 
2885     TRACE("\n");
2886     if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2887         type[0] = 'A';
2888 
2889     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2890         goto lend;
2891 
2892     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2893     if (nResCode)
2894     {
2895         if (nResCode == 2)
2896             bSuccess = TRUE;
2897         else
2898             FTP_SetResponseError(nResCode);
2899     }
2900 
2901 lend:
2902     return bSuccess;
2903 }
2904 
2905 
2906 #if 0  /* FIXME: should probably be used for FtpGetFileSize */
2907 /***********************************************************************
2908  *           FTP_GetFileSize (internal)
2909  *
2910  * Retrieves from the server the size of the given file
2911  *
2912  * RETURNS
2913  *   TRUE on success
2914  *   FALSE on failure
2915  *
2916  */
2917 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2918 {
2919     INT nResCode;
2920     BOOL bSuccess = FALSE;
2921 
2922     TRACE("\n");
2923 
2924     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2925         goto lend;
2926 
2927     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2928     if (nResCode)
2929     {
2930         if (nResCode == 213) {
2931             /* Now parses the output to get the actual file size */
2932             int i;
2933             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2934 
2935             for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2936             if (lpszResponseBuffer[i] == '\0') return FALSE;
2937             *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2938             
2939             bSuccess = TRUE;
2940         } else {
2941             FTP_SetResponseError(nResCode);
2942         }
2943     }
2944 
2945 lend:
2946     return bSuccess;
2947 }
2948 #endif
2949 
2950 
2951 /***********************************************************************
2952  *           FTP_SendPort (internal)
2953  *
2954  * Tell server which port to use
2955  *
2956  * RETURNS
2957  *   TRUE on success
2958  *   FALSE on failure
2959  *
2960  */
2961 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
2962 {
2963     static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2964     INT nResCode;
2965     WCHAR szIPAddress[64];
2966     BOOL bSuccess = FALSE;
2967     TRACE("\n");
2968 
2969     sprintfW(szIPAddress, szIPFormat,
2970          lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2971         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2972         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2973         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2974         lpwfs->lstnSocketAddress.sin_port & 0xFF,
2975         (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2976 
2977     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2978         goto lend;
2979 
2980     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2981     if (nResCode)
2982     {
2983         if (nResCode == 200)
2984             bSuccess = TRUE;
2985         else
2986             FTP_SetResponseError(nResCode);
2987     }
2988 
2989 lend:
2990     return bSuccess;
2991 }
2992 
2993 
2994 /***********************************************************************
2995  *           FTP_DoPassive (internal)
2996  *
2997  * Tell server that we want to do passive transfers
2998  * and connect data socket
2999  *
3000  * RETURNS
3001  *   TRUE on success
3002  *   FALSE on failure
3003  *
3004  */
3005 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3006 {
3007     INT nResCode;
3008     BOOL bSuccess = FALSE;
3009 
3010     TRACE("\n");
3011     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3012         goto lend;
3013 
3014     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3015     if (nResCode)
3016     {
3017         if (nResCode == 227)
3018         {
3019             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3020             LPSTR p;
3021             int f[6];
3022             int i;
3023             char *pAddr, *pPort;
3024             INT nsocket = -1;
3025             struct sockaddr_in dataSocketAddress;
3026 
3027             p = lpszResponseBuffer+4; /* skip status code */
3028             while (*p != '\0' && (*p < '' || *p > '9')) p++;
3029 
3030             if (*p == '\0')
3031             {
3032                 ERR("no address found in response, aborting\n");
3033                 goto lend;
3034             }
3035 
3036             if (sscanf(p, "%d,%d,%d,%d,%d,%d",  &f[0], &f[1], &f[2], &f[3],
3037                                                 &f[4], &f[5]) != 6)
3038             {
3039                 ERR("unknown response address format '%s', aborting\n", p);
3040                 goto lend;
3041             }
3042             for (i=0; i < 6; i++)
3043                 f[i] = f[i] & 0xff;
3044 
3045             dataSocketAddress = lpwfs->socketAddress;
3046             pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3047             pPort = (char *)&(dataSocketAddress.sin_port);
3048             pAddr[0] = f[0];
3049             pAddr[1] = f[1];
3050             pAddr[2] = f[2];
3051             pAddr[3] = f[3];
3052             pPort[0] = f[4];
3053             pPort[1] = f[5];
3054 
3055             nsocket = socket(AF_INET,SOCK_STREAM,0);
3056             if (nsocket == -1)
3057                 goto lend;
3058 
3059             if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3060             {
3061                 ERR("can't connect passive FTP data port.\n");
3062                 closesocket(nsocket);
3063                 goto lend;
3064             }
3065             lpwfs->pasvSocket = nsocket;
3066             bSuccess = TRUE;
3067         }
3068         else
3069             FTP_SetResponseError(nResCode);
3070     }
3071 
3072 lend:
3073     return bSuccess;
3074 }
3075 
3076 
3077 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3078 {
3079     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3080     {
3081         if (!FTP_DoPassive(lpwfs))
3082             return FALSE;
3083     }
3084     else
3085     {
3086         if (!FTP_SendPort(lpwfs))
3087             return FALSE;
3088     }
3089     return TRUE;
3090 }
3091 
3092 
3093 /***********************************************************************
3094  *           FTP_GetDataSocket (internal)
3095  *
3096  * Either accepts an incoming data socket connection from the server
3097  * or just returns the already opened socket after a PASV command
3098  * in case of passive FTP.
3099  *
3100  *
3101  * RETURNS
3102  *   TRUE on success
3103  *   FALSE on failure
3104  *
3105  */
3106 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3107 {
3108     struct sockaddr_in saddr;
3109     socklen_t addrlen = sizeof(struct sockaddr);
3110 
3111     TRACE("\n");
3112     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3113     {
3114         *nDataSocket = lpwfs->pasvSocket;
3115         lpwfs->pasvSocket = -1;
3116     }
3117     else
3118     {
3119         *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3120         closesocket(lpwfs->lstnSocket);
3121         lpwfs->lstnSocket = -1;
3122     }
3123     return *nDataSocket != -1;
3124 }
3125 
3126 
3127 /***********************************************************************
3128  *           FTP_SendData (internal)
3129  *
3130  * Send data to the server
3131  *
3132  * RETURNS
3133  *   TRUE on success
3134  *   FALSE on failure
3135  *
3136  */
3137 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3138 {
3139     BY_HANDLE_FILE_INFORMATION fi;
3140     DWORD nBytesRead = 0;
3141     DWORD nBytesSent = 0;
3142     DWORD nTotalSent = 0;
3143     DWORD nBytesToSend, nLen;
3144     int nRC = 1;
3145     time_t s_long_time, e_long_time;
3146     LONG nSeconds;
3147     CHAR *lpszBuffer;
3148 
3149     TRACE("\n");
3150     lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3151 
3152     /* Get the size of the file. */
3153     GetFileInformationByHandle(hFile, &fi);
3154     time(&s_long_time);
3155 
3156     do
3157     {
3158         nBytesToSend = nBytesRead - nBytesSent;
3159 
3160         if (nBytesToSend <= 0)
3161         {
3162             /* Read data from file. */
3163             nBytesSent = 0;
3164             if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3165             ERR("Failed reading from file\n");
3166 
3167             if (nBytesRead > 0)
3168                 nBytesToSend = nBytesRead;
3169             else
3170                 break;
3171         }
3172 
3173         nLen = DATA_PACKET_SIZE < nBytesToSend ?
3174             DATA_PACKET_SIZE : nBytesToSend;
3175         nRC  = send(nDataSocket, lpszBuffer, nLen, 0);
3176 
3177         if (nRC != -1)
3178         {
3179             nBytesSent += nRC;
3180             nTotalSent += nRC;
3181         }
3182 
3183         /* Do some computation to display the status. */
3184         time(&e_long_time);
3185         nSeconds = e_long_time - s_long_time;
3186         if( nSeconds / 60 > 0 )
3187         {
3188             TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3189             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3190             nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3191         }
3192         else
3193         {
3194             TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3195             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3196             (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3197         }
3198     } while (nRC != -1);
3199 
3200     TRACE("file transfer complete!\n");
3201 
3202     HeapFree(GetProcessHeap(), 0, lpszBuffer);
3203 
3204     return nTotalSent;
3205 }
3206 
3207 
3208 /***********************************************************************
3209  *           FTP_SendRetrieve (internal)
3210  *
3211  * Send request to retrieve a file
3212  *
3213  * RETURNS
3214  *   Number of bytes to be received on success
3215  *   0 on failure
3216  *
3217  */
3218 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3219 {
3220     INT nResCode;
3221     BOOL ret;
3222 
3223     TRACE("\n");
3224     if (!(ret = FTP_InitListenSocket(lpwfs)))
3225         goto lend;
3226 
3227     if (!(ret = FTP_SendType(lpwfs, dwType)))
3228         goto lend;
3229 
3230     if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3231         goto lend;
3232 
3233     if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3234         goto lend;
3235 
3236     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3237     if ((nResCode != 125) && (nResCode != 150)) {
3238         /* That means that we got an error getting the file. */
3239         FTP_SetResponseError(nResCode);
3240         ret = FALSE;
3241     }
3242 
3243 lend:
3244     if (!ret && lpwfs->lstnSocket != -1)
3245     {
3246         closesocket(lpwfs->lstnSocket);
3247         lpwfs->lstnSocket = -1;
3248     }
3249 
3250     return ret;
3251 }
3252 
3253 
3254 /***********************************************************************
3255  *           FTP_RetrieveData  (internal)
3256  *
3257  * Retrieve data from server
3258  *
3259  * RETURNS
3260  *   TRUE on success
3261  *   FALSE on failure
3262  *
3263  */
3264 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3265 {
3266     DWORD nBytesWritten;
3267     INT nRC = 0;
3268     CHAR *lpszBuffer;
3269 
3270     TRACE("\n");
3271 
3272     lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3273     if (NULL == lpszBuffer)
3274     {
3275         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3276         return FALSE;
3277     }
3278 
3279     while (nRC != -1)
3280     {
3281         nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3282         if (nRC != -1)
3283         {
3284             /* other side closed socket. */
3285             if (nRC == 0)
3286                 goto recv_end;
3287             WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3288         }
3289     }
3290 
3291     TRACE("Data transfer complete\n");
3292 
3293 recv_end:
3294     HeapFree(GetProcessHeap(), 0, lpszBuffer);
3295 
3296     return  (nRC != -1);
3297 }
3298 
3299 /***********************************************************************
3300  *           FTPFINDNEXT_Destroy (internal)
3301  *
3302  * Deallocate session handle
3303  */
3304 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3305 {
3306     LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3307     DWORD i;
3308 
3309     TRACE("\n");
3310 
3311     WININET_Release(&lpwfn->lpFtpSession->hdr);
3312 
3313     for (i = 0; i < lpwfn->size; i++)
3314     {
3315         HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3316     }
3317 
3318     HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3319     HeapFree(GetProcessHeap(), 0, lpwfn);
3320 }
3321 
3322 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3323 {
3324     WIN32_FIND_DATAW *find_data = data;
3325     DWORD res = ERROR_SUCCESS;
3326 
3327     TRACE("index(%d) size(%d)\n", find->index, find->size);
3328 
3329     ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3330 
3331     if (find->index < find->size) {
3332         FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3333         find->index++;
3334 
3335         TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3336     }else {
3337         res = ERROR_NO_MORE_FILES;
3338     }
3339 
3340     if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3341     {
3342         INTERNET_ASYNC_RESULT iar;
3343 
3344         iar.dwResult = (res == ERROR_SUCCESS);
3345         iar.dwError = res;
3346 
3347         INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3348                               INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3349                               sizeof(INTERNET_ASYNC_RESULT));
3350     }
3351 
3352     return res;
3353 }
3354 
3355 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3356 {
3357     struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3358 
3359     FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3360 }
3361 
3362 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3363 {
3364     switch(option) {
3365     case INTERNET_OPTION_HANDLE_TYPE:
3366         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3367 
3368         if (*size < sizeof(ULONG))
3369             return ERROR_INSUFFICIENT_BUFFER;
3370 
3371         *size = sizeof(DWORD);
3372         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3373         return ERROR_SUCCESS;
3374     }
3375 
3376     return INET_QueryOption(option, buffer, size, unicode);
3377 }
3378 
3379 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3380 {
3381     WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3382 
3383     if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3384     {
3385         WORKREQUEST workRequest;
3386         struct WORKREQ_FTPFINDNEXTW *req;
3387 
3388         workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3389         workRequest.hdr = WININET_AddRef( &find->hdr );
3390         req = &workRequest.u.FtpFindNextW;
3391         req->lpFindFileData = data;
3392 
3393         INTERNET_AsyncCall(&workRequest);
3394 
3395         return ERROR_SUCCESS;
3396     }
3397 
3398     return FTPFINDNEXT_FindNextFileProc(find, data);
3399 }
3400 
3401 static const object_vtbl_t FTPFINDNEXTVtbl = {
3402     FTPFINDNEXT_Destroy,
3403     NULL,
3404     FTPFINDNEXT_QueryOption,
3405     NULL,
3406     NULL,
3407     NULL,
3408     NULL,
3409     NULL,
3410     NULL,
3411     FTPFINDNEXT_FindNextFileW
3412 };
3413 
3414 /***********************************************************************
3415  *           FTP_ReceiveFileList (internal)
3416  *
3417  * Read file list from server
3418  *
3419  * RETURNS
3420  *   Handle to file list on success
3421  *   NULL on failure
3422  *
3423  */
3424 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3425         LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3426 {
3427     DWORD dwSize = 0;
3428     LPFILEPROPERTIESW lpafp = NULL;
3429     LPWININETFTPFINDNEXTW lpwfn = NULL;
3430     HINTERNET handle = 0;
3431 
3432     TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3433 
3434     if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3435     {
3436         if(lpFindFileData)
3437             FTP_ConvertFileProp(lpafp, lpFindFileData);
3438 
3439         lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3440         if (lpwfn)
3441         {
3442             lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3443             lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3444             lpwfn->hdr.dwContext = dwContext;
3445             lpwfn->hdr.refs = 1;
3446             lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3447             lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3448             lpwfn->size = dwSize;
3449             lpwfn->lpafp = lpafp;
3450 
3451             WININET_AddRef( &lpwfs->hdr );
3452             lpwfn->lpFtpSession = lpwfs;
3453             list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3454 
3455             handle = WININET_AllocHandle( &lpwfn->hdr );
3456         }
3457     }
3458 
3459     if( lpwfn )
3460         WININET_Release( &lpwfn->hdr );
3461 
3462     TRACE("Matched %d files\n", dwSize);
3463     return handle;
3464 }
3465 
3466 
3467 /***********************************************************************
3468  *           FTP_ConvertFileProp (internal)
3469  *
3470  * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3471  *
3472  * RETURNS
3473  *   TRUE on success
3474  *   FALSE on failure
3475  *
3476  */
3477 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3478 {
3479     BOOL bSuccess = FALSE;
3480 
3481     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3482 
3483     if (lpafp)
3484     {
3485         SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3486         lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3487         lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3488         
3489         /* Not all fields are filled in */
3490         lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3491         lpFindFileData->nFileSizeLow = lpafp->nSize;
3492 
3493         if (lpafp->bIsDirectory)
3494             lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3495 
3496         if (lpafp->lpszName)
3497             lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3498 
3499         bSuccess = TRUE;
3500     }
3501 
3502     return bSuccess;
3503 }
3504 
3505 /***********************************************************************
3506  *           FTP_ParseNextFile (internal)
3507  *
3508  * Parse the next line in file listing
3509  *
3510  * RETURNS
3511  *   TRUE on success
3512  *   FALSE on failure
3513  */
3514 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3515 {
3516     static const char szSpace[] = " \t";
3517     DWORD nBufLen;
3518     char *pszLine;
3519     char *pszToken;
3520     char *pszTmp;
3521     BOOL found = FALSE;
3522     int i;
3523     
3524     lpfp->lpszName = NULL;
3525     do {
3526         if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3527             return FALSE;
3528     
3529         pszToken = strtok(pszLine, szSpace);
3530         /* ls format
3531          * <Permissions> <NoLinks> <owner>   <group> <size> <date>  <time or year> <filename>
3532          *
3533          * For instance:
3534          * drwx--s---     2         pcarrier  ens     512    Sep 28  1995           pcarrier
3535          */
3536         if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3537             if(!FTP_ParsePermission(pszToken, lpfp))
3538                 lpfp->bIsDirectory = FALSE;
3539             for(i=0; i<=3; i++) {
3540               if(!(pszToken = strtok(NULL, szSpace)))
3541                   break;
3542             }
3543             if(!pszToken) continue;
3544             if(lpfp->bIsDirectory) {
3545                 TRACE("Is directory\n");
3546                 lpfp->nSize = 0;
3547             }
3548             else {
3549                 TRACE("Size: %s\n", pszToken);
3550                 lpfp->nSize = atol(pszToken);
3551             }
3552             
3553             lpfp->tmLastModified.wSecond = 0;
3554             lpfp->tmLastModified.wMinute = 0;
3555             lpfp->tmLastModified.wHour   = 0;
3556             lpfp->tmLastModified.wDay    = 0;
3557             lpfp->tmLastModified.wMonth  = 0;
3558             lpfp->tmLastModified.wYear   = 0;
3559             
3560             /* Determine month */
3561             pszToken = strtok(NULL, szSpace);
3562             if(!pszToken) continue;
3563             if(strlen(pszToken) >= 3) {
3564                 pszToken[3] = 0;
3565                 if((pszTmp = StrStrIA(szMonths, pszToken)))
3566                     lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3567             }
3568             /* Determine day */
3569             pszToken = strtok(NULL, szSpace);
3570             if(!pszToken) continue;
3571             lpfp->tmLastModified.wDay = atoi(pszToken);
3572             /* Determine time or year */
3573             pszToken = strtok(NULL, szSpace);
3574             if(!pszToken) continue;
3575             if((pszTmp = strchr(pszToken, ':'))) {
3576                 SYSTEMTIME curr_time;
3577                 *pszTmp = 0;
3578                 pszTmp++;
3579                 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3580                 lpfp->tmLastModified.wHour = atoi(pszToken);
3581                 GetLocalTime( &curr_time );
3582                 lpfp->tmLastModified.wYear = curr_time.wYear;
3583             }
3584             else {
3585                 lpfp->tmLastModified.wYear = atoi(pszToken);
3586                 lpfp->tmLastModified.wHour = 12;
3587             }
3588             TRACE("Mod time: %02d:%02d:%02d  %04d/%02d/%02d\n",
3589                   lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3590                   lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3591 
3592             pszToken = strtok(NULL, szSpace);
3593             if(!pszToken) continue;
3594             lpfp->lpszName = heap_strdupAtoW(pszToken);
3595             TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3596         }
3597         /* NT way of parsing ... :
3598             
3599                 07-13-03  08:55PM       <DIR>          sakpatch
3600                 05-09-03  06:02PM             12656686 2003-04-21bgm_cmd_e.rgz
3601         */
3602         else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3603             int mon, mday, year, hour, min;
3604             lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3605             
3606             sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3607             lpfp->tmLastModified.wDay   = mday;
3608             lpfp->tmLastModified.wMonth = mon;
3609             lpfp->tmLastModified.wYear  = year;
3610 
3611             /* Hacky and bad Y2K protection :-) */
3612             if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3613 
3614             pszToken = strtok(NULL, szSpace);
3615             if(!pszToken) continue;
3616             sscanf(pszToken, "%d:%d", &hour, &min);
3617             lpfp->tmLastModified.wHour   = hour;
3618             lpfp->tmLastModified.wMinute = min;
3619             if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3620                 lpfp->tmLastModified.wHour += 12;
3621             }
3622             lpfp->tmLastModified.wSecond = 0;
3623 
3624             TRACE("Mod time: %02d:%02d:%02d  %04d/%02d/%02d\n",
3625                   lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3626                   lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3627 
3628             pszToken = strtok(NULL, szSpace);
3629             if(!pszToken) continue;
3630             if(!strcasecmp(pszToken, "<DIR>")) {
3631                 lpfp->bIsDirectory = TRUE;
3632                 lpfp->nSize = 0;
3633                 TRACE("Is directory\n");
3634             }
3635             else {
3636                 lpfp->bIsDirectory = FALSE;
3637                 lpfp->nSize = atol(pszToken);
3638                 TRACE("Size: %d\n", lpfp->nSize);
3639             }
3640             
3641             pszToken = strtok(NULL, szSpace);
3642             if(!pszToken) continue;
3643             lpfp->lpszName = heap_strdupAtoW(pszToken);
3644             TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3645         }
3646         /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3647         else if(pszToken[0] == '+') {
3648             FIXME("EPLF Format not implemented\n");
3649         }
3650         
3651         if(lpfp->lpszName) {
3652             if((lpszSearchFile == NULL) ||
3653                (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3654                 found = TRUE;
3655                 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3656             }
3657             else {
3658                 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3659                 lpfp->lpszName = NULL;
3660             }
3661         }
3662     } while(!found);
3663     return TRUE;
3664 }
3665 
3666 /***********************************************************************
3667  *           FTP_ParseDirectory (internal)
3668  *
3669  * Parse string of directory information
3670  *
3671  * RETURNS
3672  *   TRUE on success
3673  *   FALSE on failure
3674  */
3675 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3676     LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3677 {
3678     BOOL bSuccess = TRUE;
3679     INT sizeFilePropArray = 500;/*20; */
3680     INT indexFilePropArray = -1;
3681 
3682     TRACE("\n");
3683 
3684     /* Allocate initial file properties array */
3685     *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3686     if (!*lpafp)
3687         return FALSE;
3688 
3689     do {
3690         if (indexFilePropArray+1 >= sizeFilePropArray)
3691         {
3692             LPFILEPROPERTIESW tmpafp;
3693             
3694             sizeFilePropArray *= 2;
3695             tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3696                 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3697             if (NULL == tmpafp)
3698             {
3699                 bSuccess = FALSE;
3700                 break;
3701             }
3702 
3703             *lpafp = tmpafp;
3704         }
3705         indexFilePropArray++;
3706     } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3707 
3708     if (bSuccess && indexFilePropArray)
3709     {
3710         if (indexFilePropArray < sizeFilePropArray - 1)
3711         {
3712             LPFILEPROPERTIESW tmpafp;
3713 
3714             tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3715                 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3716             if (NULL != tmpafp)
3717                 *lpafp = tmpafp;
3718         }
3719         *dwfp = indexFilePropArray;
3720     }
3721     else
3722     {
3723         HeapFree(GetProcessHeap(), 0, *lpafp);
3724         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3725         bSuccess = FALSE;
3726     }
3727 
3728     return bSuccess;
3729 }
3730 
3731 
3732 /***********************************************************************
3733  *           FTP_ParsePermission (internal)
3734  *
3735  * Parse permission string of directory information
3736  *
3737  * RETURNS
3738  *   TRUE on success
3739  *   FALSE on failure
3740  *
3741  */
3742 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3743 {
3744     BOOL bSuccess = TRUE;
3745     unsigned short nPermission = 0;
3746     INT nPos = 1;
3747     INT nLast  = 9;
3748 
3749     TRACE("\n");
3750     if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3751     {
3752         bSuccess = FALSE;
3753         return bSuccess;
3754     }
3755 
3756     lpfp->bIsDirectory = (*lpszPermission == 'd');
3757     do
3758     {
3759         switch (nPos)
3760         {
3761             case 1:
3762                 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3763                 break;
3764             case 2:
3765                 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3766                 break;
3767             case 3:
3768                 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3769                 break;
3770             case 4:
3771                 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3772                 break;
3773             case 5:
3774                 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3775                 break;
3776             case 6:
3777                 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3778                 break;
3779             case 7:
3780                 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3781                 break;
3782             case 8:
3783                 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3784                 break;
3785             case 9:
3786                 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3787                 break;
3788         }
3789         nPos++;
3790     }while (nPos <= nLast);
3791 
3792     lpfp->permissions = nPermission;
3793     return bSuccess;
3794 }
3795 
3796 
3797 /***********************************************************************
3798  *           FTP_SetResponseError (internal)
3799  *
3800  * Set the appropriate error code for a given response from the server
3801  *
3802  * RETURNS
3803  *
3804  */
3805 static DWORD FTP_SetResponseError(DWORD dwResponse)
3806 {
3807     DWORD dwCode = 0;
3808 
3809     switch(dwResponse)
3810     {
3811     case 425: /* Cannot open data connection. */
3812         dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3813         break;
3814 
3815     case 426: /* Connection closed, transer aborted. */
3816         dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3817         break;
3818 
3819     case 530: /* Not logged in. Login incorrect. */
3820         dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3821         break;
3822 
3823     case 421: /* Service not available - Server may be shutting down. */
3824     case 450: /* File action not taken. File may be busy. */
3825     case 451: /* Action aborted. Server error. */
3826     case 452: /* Action not taken. Insufficient storage space on server. */
3827     case 500: /* Syntax error. Command unrecognized. */
3828     case 501: /* Syntax error. Error in parameters or arguments. */
3829     case 502: /* Command not implemented. */
3830     case 503: /* Bad sequence of commands. */
3831     case 504: /* Command not implemented for that parameter. */
3832     case 532: /* Need account for storing files */
3833     case 550: /* File action not taken. File not found or no access. */
3834     case 551: /* Requested action aborted. Page type unknown */