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