wininet: Fix fd leak on failure case.
[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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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(%ld)\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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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(%ld)\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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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%08lx,0x%08lx,0x%08lx)\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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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     LPWININETFILE 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(WININETFILE));
1037         lpwh->hdr.htype = WH_HFILE;
1038         lpwh->hdr.dwFlags = dwFlags;
1039         lpwh->hdr.dwContext = dwContext;
1040         lpwh->hdr.lpwhparent = WININET_AddRef( &lpwfs->hdr );
1041         lpwh->hdr.dwRefCount = 1;
1042         lpwh->hdr.destroy = FTP_CloseFileTransferHandle;
1043         lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1044         lpwh->nDataSocket = nDataSocket;
1045         lpwh->session_deleted = FALSE;
1046         
1047         handle = WININET_AllocHandle( &lpwh->hdr );
1048         if( !handle )
1049             goto lend;
1050
1051         /* Indicate that a download is currently in progress */
1052         lpwfs->download_in_progress = lpwh;
1053     }
1054
1055     if (lpwfs->lstnSocket != -1)
1056         closesocket(lpwfs->lstnSocket);
1057
1058     hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1059     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1060     {
1061         INTERNET_ASYNC_RESULT iar;
1062
1063         if (lpwh)
1064         {
1065             iar.dwResult = (DWORD)handle;
1066             iar.dwError = ERROR_SUCCESS;
1067             SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1068                 &iar, sizeof(INTERNET_ASYNC_RESULT));
1069         }
1070
1071         iar.dwResult = (DWORD)bSuccess;
1072         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1073         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1074             &iar, sizeof(INTERNET_ASYNC_RESULT));
1075     }
1076
1077 lend:
1078     if( lpwh )
1079         WININET_Release( &lpwh->hdr );
1080
1081     return handle;
1082 }
1083
1084
1085 /***********************************************************************
1086  *           FtpGetFileA (WININET.@)
1087  *
1088  * Retrieve file from the FTP server
1089  *
1090  * RETURNS
1091  *    TRUE on success
1092  *    FALSE on failure
1093  *
1094  */
1095 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1096     BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1097     DWORD dwContext)
1098 {
1099     LPWSTR lpwzRemoteFile;
1100     LPWSTR lpwzNewFile;
1101     BOOL ret;
1102     
1103     lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
1104     lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
1105     ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1106         dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1107     HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1108     HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1109     return ret;
1110 }
1111
1112
1113 /***********************************************************************
1114  *           FtpGetFileW (WININET.@)
1115  *
1116  * Retrieve file from the FTP server
1117  *
1118  * RETURNS
1119  *    TRUE on success
1120  *    FALSE on failure
1121  *
1122  */
1123 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1124     BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1125     DWORD dwContext)
1126 {
1127     LPWININETFTPSESSIONW lpwfs;
1128     LPWININETAPPINFOW hIC = NULL;
1129     BOOL r = FALSE;
1130
1131     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
1132     if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1133     {
1134         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1135         goto lend;
1136     }
1137
1138     if (lpwfs->download_in_progress != NULL) {
1139         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1140         goto lend;
1141     }
1142     
1143     hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1144     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1145     {
1146         WORKREQUEST workRequest;
1147         struct WORKREQ_FTPGETFILEW *req;
1148
1149         workRequest.asyncall = FTPGETFILEW;
1150         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1151         req = &workRequest.u.FtpGetFileW;
1152         req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
1153         req->lpszNewFile = WININET_strdupW(lpszNewFile);
1154         req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1155         req->fFailIfExists = fFailIfExists;
1156         req->dwFlags = dwInternetFlags;
1157         req->dwContext = dwContext;
1158
1159         r = INTERNET_AsyncCall(&workRequest);
1160     }
1161     else
1162     {
1163         r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1164            fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1165     }
1166
1167 lend:
1168     if( lpwfs )
1169         WININET_Release( &lpwfs->hdr );
1170
1171     return r;
1172 }
1173
1174
1175 /***********************************************************************
1176  *           FTP_FtpGetFileW (Internal)
1177  *
1178  * Retrieve file from the FTP server
1179  *
1180  * RETURNS
1181  *    TRUE on success
1182  *    FALSE on failure
1183  *
1184  */
1185 BOOL WINAPI FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1186         BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1187         DWORD dwContext)
1188 {
1189     DWORD nBytes;
1190     BOOL bSuccess = FALSE;
1191     HANDLE hFile;
1192     LPWININETAPPINFOW hIC = NULL;
1193
1194     TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1195
1196     assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1197
1198     /* Clear any error information */
1199     INTERNET_SetLastError(0);
1200
1201     /* Ensure we can write to lpszNewfile by opening it */
1202     hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1203         CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1204     if (INVALID_HANDLE_VALUE == hFile)
1205         goto lend;
1206
1207     /* Set up socket to retrieve data */
1208     nBytes = FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags);
1209
1210     if (nBytes > 0)
1211     {
1212         INT nDataSocket;
1213
1214         /* Get data socket to server */
1215         if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1216         {
1217             INT nResCode;
1218
1219             /* Receive data */
1220             FTP_RetrieveFileData(lpwfs, nDataSocket, nBytes, hFile);
1221             nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1222             if (nResCode)
1223             {
1224                 if (nResCode == 226)
1225                     bSuccess = TRUE;
1226                 else
1227                     FTP_SetResponseError(nResCode);
1228             }
1229             closesocket(nDataSocket);
1230         }
1231     }
1232
1233 lend:
1234     if (lpwfs->lstnSocket != -1)
1235         closesocket(lpwfs->lstnSocket);
1236
1237     if (hFile)
1238         CloseHandle(hFile);
1239
1240     hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1241     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1242     {
1243         INTERNET_ASYNC_RESULT iar;
1244
1245         iar.dwResult = (DWORD)bSuccess;
1246         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1247         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1248             &iar, sizeof(INTERNET_ASYNC_RESULT));
1249     }
1250
1251     return bSuccess;
1252 }
1253
1254 /***********************************************************************
1255  *           FtpGetFileSize  (WININET.@)
1256  */
1257 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1258 {
1259     FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1260
1261     if (lpdwFileSizeHigh)
1262         *lpdwFileSizeHigh = 0;
1263
1264     return 0;
1265 }
1266
1267 /***********************************************************************
1268  *           FtpDeleteFileA  (WININET.@)
1269  *
1270  * Delete a file on the ftp server
1271  *
1272  * RETURNS
1273  *    TRUE on success
1274  *    FALSE on failure
1275  *
1276  */
1277 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1278 {
1279     LPWSTR lpwzFileName;
1280     BOOL ret;
1281     
1282     lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1283     ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1284     HeapFree(GetProcessHeap(), 0, lpwzFileName);
1285     return ret;
1286 }
1287
1288 /***********************************************************************
1289  *           FtpDeleteFileW  (WININET.@)
1290  *
1291  * Delete a file on the ftp server
1292  *
1293  * RETURNS
1294  *    TRUE on success
1295  *    FALSE on failure
1296  *
1297  */
1298 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1299 {
1300     LPWININETFTPSESSIONW lpwfs;
1301     LPWININETAPPINFOW hIC = NULL;
1302     BOOL r = FALSE;
1303
1304     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1305     if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1306     {
1307         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1308         goto lend;
1309     }
1310
1311     hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1312     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1313     {
1314         WORKREQUEST workRequest;
1315         struct WORKREQ_FTPDELETEFILEW *req;
1316
1317         workRequest.asyncall = FTPDELETEFILEW;
1318         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1319         req = &workRequest.u.FtpDeleteFileW;
1320         req->lpszFilename = WININET_strdupW(lpszFileName);
1321
1322         r = INTERNET_AsyncCall(&workRequest);
1323     }
1324     else
1325     {
1326         r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1327     }
1328
1329 lend:
1330     if( lpwfs )
1331         WININET_Release( &lpwfs->hdr );
1332
1333     return r;
1334 }
1335
1336 /***********************************************************************
1337  *           FTP_FtpDeleteFileW  (Internal)
1338  *
1339  * Delete a file on the ftp server
1340  *
1341  * RETURNS
1342  *    TRUE on success
1343  *    FALSE on failure
1344  *
1345  */
1346 BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName)
1347 {
1348     INT nResCode;
1349     BOOL bSuccess = FALSE;
1350     LPWININETAPPINFOW hIC = NULL;
1351
1352     TRACE("%p\n", lpwfs);
1353
1354     assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1355
1356     /* Clear any error information */
1357     INTERNET_SetLastError(0);
1358
1359     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1360         goto lend;
1361
1362     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1363     if (nResCode)
1364     {
1365         if (nResCode == 250)
1366             bSuccess = TRUE;
1367         else
1368             FTP_SetResponseError(nResCode);
1369     }
1370 lend:
1371     hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1372     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1373     {
1374         INTERNET_ASYNC_RESULT iar;
1375
1376         iar.dwResult = (DWORD)bSuccess;
1377         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1378         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1379             &iar, sizeof(INTERNET_ASYNC_RESULT));
1380     }
1381
1382     return bSuccess;
1383 }
1384
1385
1386 /***********************************************************************
1387  *           FtpRemoveDirectoryA  (WININET.@)
1388  *
1389  * Remove a directory on the ftp server
1390  *
1391  * RETURNS
1392  *    TRUE on success
1393  *    FALSE on failure
1394  *
1395  */
1396 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1397 {
1398     LPWSTR lpwzDirectory;
1399     BOOL ret;
1400     
1401     lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
1402     ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1403     HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1404     return ret;
1405 }
1406
1407 /***********************************************************************
1408  *           FtpRemoveDirectoryW  (WININET.@)
1409  *
1410  * Remove a directory on the ftp server
1411  *
1412  * RETURNS
1413  *    TRUE on success
1414  *    FALSE on failure
1415  *
1416  */
1417 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1418 {
1419     LPWININETFTPSESSIONW lpwfs;
1420     LPWININETAPPINFOW hIC = NULL;
1421     BOOL r = FALSE;
1422
1423     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1424     if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1425     {
1426         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1427         goto lend;
1428     }
1429
1430     hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1431     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1432     {
1433         WORKREQUEST workRequest;
1434         struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1435
1436         workRequest.asyncall = FTPREMOVEDIRECTORYW;
1437         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1438         req = &workRequest.u.FtpRemoveDirectoryW;
1439         req->lpszDirectory = WININET_strdupW(lpszDirectory);
1440
1441         r = INTERNET_AsyncCall(&workRequest);
1442     }
1443     else
1444     {
1445         r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1446     }
1447
1448 lend:
1449     if( lpwfs )
1450         WININET_Release( &lpwfs->hdr );
1451
1452     return r;
1453 }
1454
1455 /***********************************************************************
1456  *           FTP_FtpRemoveDirectoryW  (Internal)
1457  *
1458  * Remove a directory on the ftp server
1459  *
1460  * RETURNS
1461  *    TRUE on success
1462  *    FALSE on failure
1463  *
1464  */
1465 BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
1466 {
1467     INT nResCode;
1468     BOOL bSuccess = FALSE;
1469     LPWININETAPPINFOW hIC = NULL;
1470
1471     TRACE("\n");
1472
1473     assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1474
1475     /* Clear any error information */
1476     INTERNET_SetLastError(0);
1477
1478     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1479         goto lend;
1480
1481     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1482     if (nResCode)
1483     {
1484         if (nResCode == 250)
1485             bSuccess = TRUE;
1486         else
1487             FTP_SetResponseError(nResCode);
1488     }
1489
1490 lend:
1491     hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1492     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1493     {
1494         INTERNET_ASYNC_RESULT iar;
1495
1496         iar.dwResult = (DWORD)bSuccess;
1497         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1498         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1499             &iar, sizeof(INTERNET_ASYNC_RESULT));
1500     }
1501
1502     return bSuccess;
1503 }
1504
1505
1506 /***********************************************************************
1507  *           FtpRenameFileA  (WININET.@)
1508  *
1509  * Rename a file on the ftp server
1510  *
1511  * RETURNS
1512  *    TRUE on success
1513  *    FALSE on failure
1514  *
1515  */
1516 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1517 {
1518     LPWSTR lpwzSrc;
1519     LPWSTR lpwzDest;
1520     BOOL ret;
1521     
1522     lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL;
1523     lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL;
1524     ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1525     HeapFree(GetProcessHeap(), 0, lpwzSrc);
1526     HeapFree(GetProcessHeap(), 0, lpwzDest);
1527     return ret;
1528 }
1529
1530 /***********************************************************************
1531  *           FtpRenameFileW  (WININET.@)
1532  *
1533  * Rename a file on the ftp server
1534  *
1535  * RETURNS
1536  *    TRUE on success
1537  *    FALSE on failure
1538  *
1539  */
1540 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
1541 {
1542     LPWININETFTPSESSIONW lpwfs;
1543     LPWININETAPPINFOW hIC = NULL;
1544     BOOL r = FALSE;
1545
1546     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1547     if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1548     {
1549         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1550         goto lend;
1551     }
1552
1553     hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1554     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1555     {
1556         WORKREQUEST workRequest;
1557         struct WORKREQ_FTPRENAMEFILEW *req;
1558
1559         workRequest.asyncall = FTPRENAMEFILEW;
1560         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1561         req = &workRequest.u.FtpRenameFileW;
1562         req->lpszSrcFile = WININET_strdupW(lpszSrc);
1563         req->lpszDestFile = WININET_strdupW(lpszDest);
1564
1565         r = INTERNET_AsyncCall(&workRequest);
1566     }
1567     else
1568     {
1569         r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
1570     }
1571
1572 lend:
1573     if( lpwfs )
1574         WININET_Release( &lpwfs->hdr );
1575
1576     return r;
1577 }
1578
1579 /***********************************************************************
1580  *           FTP_FtpRenameFileW  (Internal)
1581  *
1582  * Rename a file on the ftp server
1583  *
1584  * RETURNS
1585  *    TRUE on success
1586  *    FALSE on failure
1587  *
1588  */
1589 BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs,
1590                          LPCWSTR lpszSrc, LPCWSTR lpszDest)
1591 {
1592     INT nResCode;
1593     BOOL bSuccess = FALSE;
1594     LPWININETAPPINFOW hIC = NULL;
1595
1596     TRACE("\n");
1597
1598     assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1599
1600     /* Clear any error information */
1601     INTERNET_SetLastError(0);
1602
1603     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
1604         goto lend;
1605
1606     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1607     if (nResCode == 350)
1608     {
1609         if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
1610             goto lend;
1611
1612         nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1613     }
1614
1615     if (nResCode == 250)
1616         bSuccess = TRUE;
1617     else
1618         FTP_SetResponseError(nResCode);
1619
1620 lend:
1621     hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1622     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1623     {
1624         INTERNET_ASYNC_RESULT iar;
1625
1626         iar.dwResult = (DWORD)bSuccess;
1627         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1628         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1629             &iar, sizeof(INTERNET_ASYNC_RESULT));
1630     }
1631
1632     return bSuccess;
1633 }
1634
1635 /***********************************************************************
1636  *           FtpCommandA  (WININET.@)
1637  */
1638 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1639                          LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1640 {
1641     FIXME("%p %d 0x%08lx %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1642           debugstr_a(lpszCommand), dwContext, phFtpCommand);
1643
1644     return TRUE;
1645 }
1646
1647 /***********************************************************************
1648  *           FtpCommandW  (WININET.@)
1649  */
1650 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1651                          LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1652 {
1653     FIXME("%p %d 0x%08lx %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1654           debugstr_w(lpszCommand), dwContext, phFtpCommand);
1655
1656     return TRUE;
1657 }
1658
1659 /***********************************************************************
1660  *           FTP_Connect (internal)
1661  *
1662  * Connect to a ftp server
1663  *
1664  * RETURNS
1665  *   HINTERNET a session handle on success
1666  *   NULL on failure
1667  *
1668  * NOTES:
1669  *
1670  * Windows uses 'anonymous' as the username, when given a NULL username
1671  * and a NULL password. The password is first looked up in:
1672  *
1673  * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
1674  *
1675  * If this entry is not present it uses the current username as the password.
1676  *
1677  */
1678
1679 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
1680         INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
1681         LPCWSTR lpszPassword, DWORD dwFlags, DWORD dwContext,
1682         DWORD dwInternalFlags)
1683 {
1684     static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
1685                                    'M','i','c','r','o','s','o','f','t','\\',
1686                                    'W','i','n','d','o','w','s','\\',
1687                                    'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1688                                    'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
1689     static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
1690     static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
1691     static const WCHAR szEmpty[] = {'\0'};
1692     struct sockaddr_in socketAddr;
1693     INT nsocket = -1;
1694     UINT sock_namelen;
1695     BOOL bSuccess = FALSE;
1696     LPWININETFTPSESSIONW lpwfs = NULL;
1697     HINTERNET handle = NULL;
1698
1699     TRACE("%p  Server(%s) Port(%d) User(%s) Paswd(%s)\n",
1700             hIC, debugstr_w(lpszServerName),
1701             nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
1702
1703     assert( hIC->hdr.htype == WH_HINIT );
1704
1705     if (NULL == lpszUserName && NULL != lpszPassword)
1706     {
1707         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1708         goto lerror;
1709     }
1710     
1711     lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
1712     if (NULL == lpwfs)
1713     {
1714         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1715         goto lerror;
1716     }
1717
1718     if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
1719         nServerPort = INTERNET_DEFAULT_FTP_PORT;
1720
1721     lpwfs->hdr.htype = WH_HFTPSESSION;
1722     lpwfs->hdr.lpwhparent = WININET_AddRef( &hIC->hdr );
1723     lpwfs->hdr.dwFlags = dwFlags;
1724     lpwfs->hdr.dwContext = dwContext;
1725     lpwfs->hdr.dwInternalFlags = dwInternalFlags;
1726     lpwfs->hdr.dwRefCount = 1;
1727     lpwfs->hdr.destroy = FTP_CloseSessionHandle;
1728     lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
1729     lpwfs->download_in_progress = NULL;
1730
1731     handle = WININET_AllocHandle( &lpwfs->hdr );
1732     if( !handle )
1733     {
1734         ERR("Failed to alloc handle\n");
1735         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1736         goto lerror;
1737     }
1738
1739     if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
1740         if(strchrW(hIC->lpszProxy, ' '))
1741             FIXME("Several proxies not implemented.\n");
1742         if(hIC->lpszProxyBypass)
1743             FIXME("Proxy bypass is ignored.\n");
1744     }
1745     if ( !lpszUserName) {
1746         HKEY key;
1747         WCHAR szPassword[MAX_PATH];
1748         DWORD len = sizeof(szPassword);
1749
1750         lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
1751
1752         RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
1753         if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
1754             /* Nothing in the registry, get the username and use that as the password */
1755             if (!GetUserNameW(szPassword, &len)) {
1756                 /* Should never get here, but use an empty password as failsafe */
1757                 strcpyW(szPassword, szEmpty);
1758             }
1759         }
1760         RegCloseKey(key);
1761
1762         TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
1763         lpwfs->lpszPassword = WININET_strdupW(szPassword);
1764     }
1765     else {
1766         lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
1767
1768         if (lpszPassword)
1769             lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
1770         else
1771             lpwfs->lpszPassword = WININET_strdupW(szEmpty);
1772     }
1773     
1774     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
1775     if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
1776     {
1777         INTERNET_ASYNC_RESULT iar;
1778
1779         iar.dwResult = (DWORD)handle;
1780         iar.dwError = ERROR_SUCCESS;
1781
1782         SendAsyncCallback(&hIC->hdr, dwContext,
1783                       INTERNET_STATUS_HANDLE_CREATED, &iar,
1784                       sizeof(INTERNET_ASYNC_RESULT));
1785     }
1786         
1787     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
1788         (LPWSTR) lpszServerName, strlenW(lpszServerName));
1789
1790     if (!GetAddress(lpszServerName, nServerPort, &socketAddr))
1791     {
1792         INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1793         goto lerror;
1794     }
1795
1796     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
1797         (LPWSTR) lpszServerName, strlenW(lpszServerName));
1798
1799     nsocket = socket(AF_INET,SOCK_STREAM,0);
1800     if (nsocket == -1)
1801     {
1802         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1803         goto lerror;
1804     }
1805
1806     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
1807         &socketAddr, sizeof(struct sockaddr_in));
1808
1809     if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
1810     {
1811         ERR("Unable to connect (%s)\n", strerror(errno));
1812         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1813     }
1814     else
1815     {
1816         TRACE("Connected to server\n");
1817         lpwfs->sndSocket = nsocket;
1818         SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
1819             &socketAddr, sizeof(struct sockaddr_in));
1820
1821         sock_namelen = sizeof(lpwfs->socketAddress);
1822         getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
1823
1824         if (FTP_ConnectToHost(lpwfs))
1825         {
1826             TRACE("Successfully logged into server\n");
1827             bSuccess = TRUE;
1828         }
1829     }
1830
1831 lerror:
1832     if (!bSuccess && nsocket == -1)
1833         closesocket(nsocket);
1834
1835     if (!bSuccess && lpwfs)
1836     {
1837         HeapFree(GetProcessHeap(), 0, lpwfs);
1838         WININET_FreeHandle( handle );
1839         handle = NULL;
1840         lpwfs = NULL;
1841     }
1842
1843     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1844     {
1845         INTERNET_ASYNC_RESULT iar;
1846
1847         iar.dwResult = (DWORD)lpwfs;
1848         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1849         SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1850             &iar, sizeof(INTERNET_ASYNC_RESULT));
1851     }
1852
1853     return handle;
1854 }
1855
1856
1857 /***********************************************************************
1858  *           FTP_ConnectToHost (internal)
1859  *
1860  * Connect to a ftp server
1861  *
1862  * RETURNS
1863  *   TRUE on success
1864  *   NULL on failure
1865  *
1866  */
1867 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
1868 {
1869     INT nResCode;
1870     BOOL bSuccess = FALSE;
1871
1872     TRACE("\n");
1873     FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1874
1875     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
1876         goto lend;
1877
1878     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1879     if (nResCode)
1880     {
1881         /* Login successful... */
1882         if (nResCode == 230)
1883             bSuccess = TRUE;
1884         /* User name okay, need password... */
1885         else if (nResCode == 331)
1886             bSuccess = FTP_SendPassword(lpwfs);
1887         /* Need account for login... */
1888         else if (nResCode == 332)
1889             bSuccess = FTP_SendAccount(lpwfs);
1890         else
1891             FTP_SetResponseError(nResCode);
1892     }
1893
1894     TRACE("Returning %d\n", bSuccess);
1895 lend:
1896     return bSuccess;
1897 }
1898
1899
1900 /***********************************************************************
1901  *           FTP_SendCommandA (internal)
1902  *
1903  * Send command to server
1904  *
1905  * RETURNS
1906  *   TRUE on success
1907  *   NULL on failure
1908  *
1909  */
1910 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
1911         INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext)
1912 {
1913         DWORD len;
1914         CHAR *buf;
1915         DWORD nBytesSent = 0;
1916         int nRC = 0;
1917         DWORD dwParamLen;
1918
1919         TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
1920
1921         if (lpfnStatusCB)
1922         {
1923              HINTERNET hHandle = WININET_FindHandle( hdr );
1924              if( hHandle )
1925              {
1926                  lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
1927                  WININET_Release( hdr );
1928              }
1929         }
1930
1931         dwParamLen = lpszParam?strlen(lpszParam)+1:0;
1932         len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
1933         if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
1934         {
1935             INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1936             return FALSE;
1937         }
1938         sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
1939                 dwParamLen ? lpszParam : "", szCRLF);
1940
1941         TRACE("Sending (%s) len(%ld)\n", buf, len);
1942         while((nBytesSent < len) && (nRC != -1))
1943         {
1944                 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
1945                 nBytesSent += nRC;
1946         }
1947
1948         HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
1949
1950         if (lpfnStatusCB)
1951         {
1952              HINTERNET hHandle = WININET_FindHandle( hdr );
1953              if( hHandle )
1954              {
1955                  lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_REQUEST_SENT,
1956                               &nBytesSent, sizeof(DWORD));
1957                  WININET_Release( hdr );
1958              }
1959         }
1960
1961         TRACE("Sent %ld bytes\n", nBytesSent);
1962         return (nRC != -1);
1963 }
1964
1965 /***********************************************************************
1966  *           FTP_SendCommand (internal)
1967  *
1968  * Send command to server
1969  *
1970  * RETURNS
1971  *   TRUE on success
1972  *   NULL on failure
1973  *
1974  */
1975 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
1976         INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext)
1977 {
1978     BOOL ret;
1979     LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
1980     ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
1981     HeapFree(GetProcessHeap(), 0, lpszParamA);
1982     return ret;
1983 }
1984
1985 /***********************************************************************
1986  *           FTP_ReceiveResponse (internal)
1987  *
1988  * Receive response from server
1989  *
1990  * RETURNS
1991  *   Reply code on success
1992  *   0 on failure
1993  *
1994  */
1995 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD dwContext)
1996 {
1997     LPSTR lpszResponse = INTERNET_GetResponseBuffer();
1998     DWORD nRecv;
1999     INT rc = 0;
2000     char firstprefix[5];
2001     BOOL multiline = FALSE;
2002     LPWININETAPPINFOW hIC = NULL;
2003
2004     TRACE("socket(%d)\n", lpwfs->sndSocket);
2005
2006     hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
2007     SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2008
2009     while(1)
2010     {
2011         if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2012             goto lerror;
2013
2014         if (nRecv >= 3)
2015         {
2016             if(!multiline)
2017             {
2018                 if(lpszResponse[3] != '-')
2019                     break;
2020                 else
2021                 {  /* Start of multiline repsonse.  Loop until we get "nnn " */
2022                     multiline = TRUE;
2023                     memcpy(firstprefix, lpszResponse, 3);
2024                     firstprefix[3] = ' ';
2025                     firstprefix[4] = '\0';
2026                 }
2027             }
2028             else
2029             {
2030                 if(!memcmp(firstprefix, lpszResponse, 4))
2031                     break;
2032             }
2033         }
2034     }
2035
2036     if (nRecv >= 3)
2037     {
2038         rc = atoi(lpszResponse);
2039
2040         SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2041                     &nRecv, sizeof(DWORD));
2042     }
2043
2044 lerror:
2045     TRACE("return %d\n", rc);
2046     return rc;
2047 }
2048
2049
2050 /***********************************************************************
2051  *           FTP_SendPassword (internal)
2052  *
2053  * Send password to ftp server
2054  *
2055  * RETURNS
2056  *   TRUE on success
2057  *   NULL on failure
2058  *
2059  */
2060 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2061 {
2062     INT nResCode;
2063     BOOL bSuccess = FALSE;
2064
2065     TRACE("\n");
2066     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2067         goto lend;
2068
2069     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2070     if (nResCode)
2071     {
2072         TRACE("Received reply code %d\n", nResCode);
2073         /* Login successful... */
2074         if (nResCode == 230)
2075             bSuccess = TRUE;
2076         /* Command not implemented, superfluous at the server site... */
2077         /* Need account for login... */
2078         else if (nResCode == 332)
2079             bSuccess = FTP_SendAccount(lpwfs);
2080         else
2081             FTP_SetResponseError(nResCode);
2082     }
2083
2084 lend:
2085     TRACE("Returning %d\n", bSuccess);
2086     return bSuccess;
2087 }
2088
2089
2090 /***********************************************************************
2091  *           FTP_SendAccount (internal)
2092  *
2093  *
2094  *
2095  * RETURNS
2096  *   TRUE on success
2097  *   FALSE on failure
2098  *
2099  */
2100 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2101 {
2102     INT nResCode;
2103     BOOL bSuccess = FALSE;
2104
2105     TRACE("\n");
2106     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2107         goto lend;
2108
2109     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2110     if (nResCode)
2111         bSuccess = TRUE;
2112     else
2113         FTP_SetResponseError(nResCode);
2114
2115 lend:
2116     return bSuccess;
2117 }
2118
2119
2120 /***********************************************************************
2121  *           FTP_SendStore (internal)
2122  *
2123  * Send request to upload file to ftp server
2124  *
2125  * RETURNS
2126  *   TRUE on success
2127  *   FALSE on failure
2128  *
2129  */
2130 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2131 {
2132     INT nResCode;
2133     BOOL bSuccess = FALSE;
2134
2135     TRACE("\n");
2136     if (!FTP_InitListenSocket(lpwfs))
2137         goto lend;
2138
2139     if (!FTP_SendType(lpwfs, dwType))
2140         goto lend;
2141
2142     if (!FTP_SendPortOrPasv(lpwfs))
2143         goto lend;
2144
2145     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2146             goto lend;
2147     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2148     if (nResCode)
2149     {
2150         if (nResCode == 150 || nResCode == 125)
2151             bSuccess = TRUE;
2152         else
2153             FTP_SetResponseError(nResCode);
2154     }
2155
2156 lend:
2157     if (!bSuccess && lpwfs->lstnSocket != -1)
2158     {
2159         closesocket(lpwfs->lstnSocket);
2160         lpwfs->lstnSocket = -1;
2161     }
2162
2163     return bSuccess;
2164 }
2165
2166
2167 /***********************************************************************
2168  *           FTP_InitListenSocket (internal)
2169  *
2170  * Create a socket to listen for server response
2171  *
2172  * RETURNS
2173  *   TRUE on success
2174  *   FALSE on failure
2175  *
2176  */
2177 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2178 {
2179     BOOL bSuccess = FALSE;
2180     size_t namelen = sizeof(struct sockaddr_in);
2181
2182     TRACE("\n");
2183
2184     lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2185     if (lpwfs->lstnSocket == -1)
2186     {
2187         TRACE("Unable to create listening socket\n");
2188             goto lend;
2189     }
2190
2191     /* We obtain our ip addr from the name of the command channel socket */
2192     lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2193
2194     /* and get the system to assign us a port */
2195     lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
2196
2197     if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2198     {
2199         TRACE("Unable to bind socket\n");
2200         goto lend;
2201     }
2202
2203     if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2204     {
2205         TRACE("listen failed\n");
2206         goto lend;
2207     }
2208
2209     if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2210         bSuccess = TRUE;
2211
2212 lend:
2213     if (!bSuccess && lpwfs->lstnSocket == -1)
2214     {
2215         closesocket(lpwfs->lstnSocket);
2216         lpwfs->lstnSocket = -1;
2217     }
2218
2219     return bSuccess;
2220 }
2221
2222
2223 /***********************************************************************
2224  *           FTP_SendType (internal)
2225  *
2226  * Tell server type of data being transferred
2227  *
2228  * RETURNS
2229  *   TRUE on success
2230  *   FALSE on failure
2231  *
2232  * W98SE doesn't cache the type that's currently set
2233  * (i.e. it sends it always),
2234  * so we probably don't want to do that either.
2235  */
2236 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2237 {
2238     INT nResCode;
2239     WCHAR type[] = { 'I','\0' };
2240     BOOL bSuccess = FALSE;
2241
2242     TRACE("\n");
2243     if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2244         type[0] = 'A';
2245
2246     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2247         goto lend;
2248
2249     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2250     if (nResCode)
2251     {
2252         if (nResCode == 2)
2253             bSuccess = TRUE;
2254         else
2255             FTP_SetResponseError(nResCode);
2256     }
2257
2258 lend:
2259     return bSuccess;
2260 }
2261
2262 /***********************************************************************
2263  *           FTP_GetFileSize (internal)
2264  *
2265  * Retrieves from the server the size of the given file
2266  *
2267  * RETURNS
2268  *   TRUE on success
2269  *   FALSE on failure
2270  *
2271  */
2272 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2273 {
2274     INT nResCode;
2275     BOOL bSuccess = FALSE;
2276
2277     TRACE("\n");
2278
2279     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2280         goto lend;
2281
2282     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2283     if (nResCode)
2284     {
2285         if (nResCode == 213) {
2286             /* Now parses the output to get the actual file size */
2287             int i;
2288             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2289
2290             for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2291             if (lpszResponseBuffer[i] == '\0') return FALSE;
2292             *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2293             
2294             bSuccess = TRUE;
2295         } else {
2296             FTP_SetResponseError(nResCode);
2297         }
2298     }
2299
2300 lend:
2301     return bSuccess;
2302 }
2303
2304
2305 /***********************************************************************
2306  *           FTP_SendPort (internal)
2307  *
2308  * Tell server which port to use
2309  *
2310  * RETURNS
2311  *   TRUE on success
2312  *   FALSE on failure
2313  *
2314  */
2315 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2316 {
2317     static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2318     INT nResCode;
2319     WCHAR szIPAddress[64];
2320     BOOL bSuccess = FALSE;
2321     TRACE("\n");
2322
2323     sprintfW(szIPAddress, szIPFormat,
2324          lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2325         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2326         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2327         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2328         lpwfs->lstnSocketAddress.sin_port & 0xFF,
2329         (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2330
2331     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2332         goto lend;
2333
2334     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2335     if (nResCode)
2336     {
2337         if (nResCode == 200)
2338             bSuccess = TRUE;
2339         else
2340             FTP_SetResponseError(nResCode);
2341     }
2342
2343 lend:
2344     return bSuccess;
2345 }
2346
2347
2348 /***********************************************************************
2349  *           FTP_DoPassive (internal)
2350  *
2351  * Tell server that we want to do passive transfers
2352  * and connect data socket
2353  *
2354  * RETURNS
2355  *   TRUE on success
2356  *   FALSE on failure
2357  *
2358  */
2359 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2360 {
2361     INT nResCode;
2362     BOOL bSuccess = FALSE;
2363
2364     TRACE("\n");
2365     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2366         goto lend;
2367
2368     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2369     if (nResCode)
2370     {
2371         if (nResCode == 227)
2372         {
2373             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2374             LPSTR p;
2375             int f[6];
2376             int i;
2377             char *pAddr, *pPort;
2378             INT nsocket = -1;
2379             struct sockaddr_in dataSocketAddress;
2380
2381             p = lpszResponseBuffer+4; /* skip status code */
2382
2383             /* do a very strict check; we can improve that later. */
2384
2385             if (strncmp(p, "Entering Passive Mode", 21))
2386             {
2387                 ERR("unknown response '%.*s', aborting\n", 21, p);
2388                 goto lend;
2389             }
2390             p += 21; /* skip string */
2391             if ((*p++ != ' ') || (*p++ != '('))
2392             {
2393                 ERR("unknown response format, aborting\n");
2394                 goto lend;
2395             }
2396
2397             if (sscanf(p, "%d,%d,%d,%d,%d,%d",  &f[0], &f[1], &f[2], &f[3],
2398                                                 &f[4], &f[5]) != 6)
2399             {
2400                 ERR("unknown response address format '%s', aborting\n", p);
2401                 goto lend;
2402             }
2403             for (i=0; i < 6; i++)
2404                 f[i] = f[i] & 0xff;
2405
2406             dataSocketAddress = lpwfs->socketAddress;
2407             pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2408             pPort = (char *)&(dataSocketAddress.sin_port);
2409             pAddr[0] = f[0];
2410             pAddr[1] = f[1];
2411             pAddr[2] = f[2];
2412             pAddr[3] = f[3];
2413             pPort[0] = f[4];
2414             pPort[1] = f[5];
2415
2416             nsocket = socket(AF_INET,SOCK_STREAM,0);
2417             if (nsocket == -1)
2418                 goto lend;
2419
2420             if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2421             {
2422                 ERR("can't connect passive FTP data port.\n");
2423                 closesocket(nsocket);
2424                 goto lend;
2425             }
2426             lpwfs->pasvSocket = nsocket;
2427             bSuccess = TRUE;
2428         }
2429         else
2430             FTP_SetResponseError(nResCode);
2431     }
2432
2433 lend:
2434     return bSuccess;
2435 }
2436
2437
2438 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2439 {
2440     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2441     {
2442         if (!FTP_DoPassive(lpwfs))
2443             return FALSE;
2444     }
2445     else
2446     {
2447         if (!FTP_SendPort(lpwfs))
2448             return FALSE;
2449     }
2450     return TRUE;
2451 }
2452
2453
2454 /***********************************************************************
2455  *           FTP_GetDataSocket (internal)
2456  *
2457  * Either accepts an incoming data socket connection from the server
2458  * or just returns the already opened socket after a PASV command
2459  * in case of passive FTP.
2460  *
2461  *
2462  * RETURNS
2463  *   TRUE on success
2464  *   FALSE on failure
2465  *
2466  */
2467 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
2468 {
2469     struct sockaddr_in saddr;
2470     size_t addrlen = sizeof(struct sockaddr);
2471
2472     TRACE("\n");
2473     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2474     {
2475         *nDataSocket = lpwfs->pasvSocket;
2476     }
2477     else
2478     {
2479         *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
2480         closesocket(lpwfs->lstnSocket);
2481         lpwfs->lstnSocket = -1;
2482     }
2483     return *nDataSocket != -1;
2484 }
2485
2486
2487 /***********************************************************************
2488  *           FTP_SendData (internal)
2489  *
2490  * Send data to the server
2491  *
2492  * RETURNS
2493  *   TRUE on success
2494  *   FALSE on failure
2495  *
2496  */
2497 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
2498 {
2499     BY_HANDLE_FILE_INFORMATION fi;
2500     DWORD nBytesRead = 0;
2501     DWORD nBytesSent = 0;
2502     DWORD nTotalSent = 0;
2503     DWORD nBytesToSend, nLen;
2504     int nRC = 1;
2505     time_t s_long_time, e_long_time;
2506     LONG nSeconds;
2507     CHAR *lpszBuffer;
2508
2509     TRACE("\n");
2510     lpszBuffer = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR)*DATA_PACKET_SIZE);
2511     memset(lpszBuffer, 0, sizeof(CHAR)*DATA_PACKET_SIZE);
2512
2513     /* Get the size of the file. */
2514     GetFileInformationByHandle(hFile, &fi);
2515     time(&s_long_time);
2516
2517     do
2518     {
2519         nBytesToSend = nBytesRead - nBytesSent;
2520
2521         if (nBytesToSend <= 0)
2522         {
2523             /* Read data from file. */
2524             nBytesSent = 0;
2525             if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
2526             ERR("Failed reading from file\n");
2527
2528             if (nBytesRead > 0)
2529                 nBytesToSend = nBytesRead;
2530             else
2531                 break;
2532         }
2533
2534         nLen = DATA_PACKET_SIZE < nBytesToSend ?
2535             DATA_PACKET_SIZE : nBytesToSend;
2536         nRC  = send(nDataSocket, lpszBuffer, nLen, 0);
2537
2538         if (nRC != -1)
2539         {
2540             nBytesSent += nRC;
2541             nTotalSent += nRC;
2542         }
2543
2544         /* Do some computation to display the status. */
2545         time(&e_long_time);
2546         nSeconds = e_long_time - s_long_time;
2547         if( nSeconds / 60 > 0 )
2548         {
2549             TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld min %ld sec estimated remaining time %ld sec\n",
2550             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
2551             nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
2552         }
2553         else
2554         {
2555             TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld sec estimated remaining time %ld sec\n",
2556             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
2557             (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
2558         }
2559     } while (nRC != -1);
2560
2561     TRACE("file transfer complete!\n");
2562
2563     HeapFree(GetProcessHeap(), 0, lpszBuffer);
2564
2565     return nTotalSent;
2566 }
2567
2568
2569 /***********************************************************************
2570  *           FTP_SendRetrieve (internal)
2571  *
2572  * Send request to retrieve a file
2573  *
2574  * RETURNS
2575  *   Number of bytes to be received on success
2576  *   0 on failure
2577  *
2578  */
2579 static DWORD FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2580 {
2581     INT nResCode;
2582     DWORD nResult = 0;
2583
2584     TRACE("\n");
2585     if (!FTP_InitListenSocket(lpwfs))
2586         goto lend;
2587
2588     if (!FTP_SendType(lpwfs, dwType))
2589         goto lend;
2590
2591     if (!FTP_SendPortOrPasv(lpwfs))
2592         goto lend;
2593
2594     if (!FTP_GetFileSize(lpwfs, lpszRemoteFile, &nResult))
2595         goto lend;
2596
2597     TRACE("Waiting to receive %ld bytes\n", nResult);
2598     
2599     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0))
2600         goto lend;
2601
2602     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2603     if ((nResCode != 125) && (nResCode != 150)) {
2604         /* That means that we got an error getting the file. */
2605         nResult = 0;
2606     }
2607
2608 lend:
2609     if (0 == nResult && lpwfs->lstnSocket != -1)
2610     {
2611         closesocket(lpwfs->lstnSocket);
2612         lpwfs->lstnSocket = -1;
2613     }
2614
2615     return nResult;
2616 }
2617
2618
2619 /***********************************************************************
2620  *           FTP_RetrieveData  (internal)
2621  *
2622  * Retrieve data from server
2623  *
2624  * RETURNS
2625  *   TRUE on success
2626  *   FALSE on failure
2627  *
2628  */
2629 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile)
2630 {
2631     DWORD nBytesWritten;
2632     DWORD nBytesReceived = 0;
2633     INT nRC = 0;
2634     CHAR *lpszBuffer;
2635
2636     TRACE("\n");
2637
2638     if (INVALID_HANDLE_VALUE == hFile)
2639         return FALSE;
2640
2641     lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2642     if (NULL == lpszBuffer)
2643     {
2644         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2645         return FALSE;
2646     }
2647
2648     while (nBytesReceived < nBytes && nRC != -1)
2649     {
2650         nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
2651         if (nRC != -1)
2652         {
2653             /* other side closed socket. */
2654             if (nRC == 0)
2655                 goto recv_end;
2656             WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
2657             nBytesReceived += nRC;
2658         }
2659
2660         TRACE("%ld bytes of %ld (%ld%%)\r", nBytesReceived, nBytes,
2661            nBytesReceived * 100 / nBytes);
2662     }
2663
2664     TRACE("Data transfer complete\n");
2665     HeapFree(GetProcessHeap(), 0, lpszBuffer);
2666
2667 recv_end:
2668     return  (nRC != -1);
2669 }
2670
2671
2672 /***********************************************************************
2673  *           FTP_CloseSessionHandle (internal)
2674  *
2675  * Deallocate session handle
2676  *
2677  * RETURNS
2678  *   TRUE on success
2679  *   FALSE on failure
2680  *
2681  */
2682 static void FTP_CloseSessionHandle(LPWININETHANDLEHEADER hdr)
2683 {
2684     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2685
2686     TRACE("\n");
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_CloseFindNextHandle (internal)
2705  *
2706  * Deallocate session handle
2707  *
2708  * RETURNS
2709  *   TRUE on success
2710  *   FALSE on failure
2711  *
2712  */
2713 static void FTP_CloseFindNextHandle(LPWININETHANDLEHEADER hdr)
2714 {
2715     LPWININETFINDNEXTW lpwfn = (LPWININETFINDNEXTW) hdr;
2716     DWORD i;
2717
2718     TRACE("\n");
2719
2720     for (i = 0; i < lpwfn->size; i++)
2721     {
2722         HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
2723     }
2724
2725     HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
2726     HeapFree(GetProcessHeap(), 0, lpwfn);
2727 }
2728
2729 /***********************************************************************
2730  *           FTP_CloseFileTransferHandle (internal)
2731  *
2732  * Closes the file transfer handle. This also 'cleans' the data queue of
2733  * the 'transfer conplete' message (this is a bit of a hack though :-/ )
2734  *
2735  */
2736 static void FTP_CloseFileTransferHandle(LPWININETHANDLEHEADER hdr)
2737 {
2738     LPWININETFILE lpwh = (LPWININETFILE) hdr;
2739     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) lpwh->hdr.lpwhparent;
2740     INT nResCode;
2741
2742     TRACE("\n");
2743
2744     if (!lpwh->session_deleted)
2745         lpwfs->download_in_progress = NULL;
2746
2747     /* This just serves to flush the control socket of any spurrious lines written
2748        to it (like '226 Transfer complete.').
2749
2750        Wonder what to do if the server sends us an error code though...
2751     */
2752     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2753     
2754     if (lpwh->nDataSocket != -1)
2755         closesocket(lpwh->nDataSocket);
2756
2757     HeapFree(GetProcessHeap(), 0, lpwh);
2758 }
2759
2760 /***********************************************************************
2761  *           FTP_ReceiveFileList (internal)
2762  *
2763  * Read file list from server
2764  *
2765  * RETURNS
2766  *   Handle to file list on success
2767  *   NULL on failure
2768  *
2769  */
2770 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
2771         LPWIN32_FIND_DATAW lpFindFileData, DWORD dwContext)
2772 {
2773     DWORD dwSize = 0;
2774     LPFILEPROPERTIESW lpafp = NULL;
2775     LPWININETFINDNEXTW lpwfn = NULL;
2776     HINTERNET handle = 0;
2777
2778     TRACE("(%p,%d,%s,%p,%ld)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
2779
2780     if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
2781     {
2782         if(lpFindFileData)
2783             FTP_ConvertFileProp(lpafp, lpFindFileData);
2784
2785         lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFINDNEXTW));
2786         if (lpwfn)
2787         {
2788             lpwfn->hdr.htype = WH_HFINDNEXT;
2789             lpwfn->hdr.lpwhparent = WININET_AddRef( &lpwfs->hdr );
2790             lpwfn->hdr.dwContext = dwContext;
2791             lpwfn->hdr.dwRefCount = 1;
2792             lpwfn->hdr.destroy = FTP_CloseFindNextHandle;
2793             lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
2794             lpwfn->index = 1; /* Next index is 1 since we return index 0 */
2795             lpwfn->size = dwSize;
2796             lpwfn->lpafp = lpafp;
2797
2798             handle = WININET_AllocHandle( &lpwfn->hdr );
2799         }
2800     }
2801
2802     if( lpwfn )
2803         WININET_Release( &lpwfn->hdr );
2804
2805     TRACE("Matched %ld files\n", dwSize);
2806     return handle;
2807 }
2808
2809
2810 /***********************************************************************
2811  *           FTP_ConvertFileProp (internal)
2812  *
2813  * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
2814  *
2815  * RETURNS
2816  *   TRUE on success
2817  *   FALSE on failure
2818  *
2819  */
2820 BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
2821 {
2822     BOOL bSuccess = FALSE;
2823
2824     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
2825
2826     if (lpafp)
2827     {
2828         /* Convert 'Unix' time to Windows time */
2829         RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
2830                                   (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
2831         lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
2832         lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
2833         
2834         /* Not all fields are filled in */
2835         lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
2836         lpFindFileData->nFileSizeLow = lpafp->nSize;
2837
2838         if (lpafp->bIsDirectory)
2839             lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
2840
2841         if (lpafp->lpszName)
2842             lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
2843
2844         bSuccess = TRUE;
2845     }
2846
2847     return bSuccess;
2848 }
2849
2850 /***********************************************************************
2851  *           FTP_ParseNextFile (internal)
2852  *
2853  * Parse the next line in file listing
2854  *
2855  * RETURNS
2856  *   TRUE on success
2857  *   FALSE on failure
2858  */
2859 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
2860 {
2861     static const char szSpace[] = " \t";
2862     DWORD nBufLen;
2863     char *pszLine;
2864     char *pszToken;
2865     char *pszTmp;
2866     BOOL found = FALSE;
2867     int i;
2868     
2869     lpfp->lpszName = NULL;
2870     do {
2871         if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
2872             return FALSE;
2873     
2874         pszToken = strtok(pszLine, szSpace);
2875         /* ls format
2876          * <Permissions> <NoLinks> <owner>   <group> <size> <date>  <time or year> <filename>
2877          *
2878          * For instance:
2879          * drwx--s---     2         pcarrier  ens     512    Sep 28  1995           pcarrier
2880          */
2881         if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
2882             if(!FTP_ParsePermission(pszToken, lpfp))
2883                 lpfp->bIsDirectory = FALSE;
2884             for(i=0; i<=3; i++) {
2885               if(!(pszToken = strtok(NULL, szSpace)))
2886                   break;
2887             }
2888             if(!pszToken) continue;
2889             if(lpfp->bIsDirectory) {
2890                 TRACE("Is directory\n");
2891                 lpfp->nSize = 0;
2892             }
2893             else {
2894                 TRACE("Size: %s\n", pszToken);
2895                 lpfp->nSize = atol(pszToken);
2896             }
2897             
2898             lpfp->tmLastModified.tm_sec  = 0;
2899             lpfp->tmLastModified.tm_min  = 0;
2900             lpfp->tmLastModified.tm_hour = 0;
2901             lpfp->tmLastModified.tm_mday = 0;
2902             lpfp->tmLastModified.tm_mon  = 0;
2903             lpfp->tmLastModified.tm_year = 0;
2904             
2905             /* Determine month */
2906             pszToken = strtok(NULL, szSpace);
2907             if(!pszToken) continue;
2908             if(strlen(pszToken) >= 3) {
2909                 pszToken[3] = 0;
2910                 if((pszTmp = StrStrIA(szMonths, pszToken)))
2911                     lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
2912             }
2913             /* Determine day */
2914             pszToken = strtok(NULL, szSpace);
2915             if(!pszToken) continue;
2916             lpfp->tmLastModified.tm_mday = atoi(pszToken);
2917             /* Determine time or year */
2918             pszToken = strtok(NULL, szSpace);
2919             if(!pszToken) continue;
2920             if((pszTmp = strchr(pszToken, ':'))) {
2921                 struct tm* apTM;
2922                 time_t aTime;
2923                 *pszTmp = 0;
2924                 pszTmp++;
2925                 lpfp->tmLastModified.tm_min = atoi(pszTmp);
2926                 lpfp->tmLastModified.tm_hour = atoi(pszToken);
2927                 time(&aTime);
2928                 apTM = localtime(&aTime);
2929                 lpfp->tmLastModified.tm_year = apTM->tm_year;
2930             }
2931             else {
2932                 lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
2933                 lpfp->tmLastModified.tm_hour = 12;
2934             }
2935             TRACE("Mod time: %02d:%02d:%02d  %02d/%02d/%02d\n",
2936                 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
2937                 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
2938                 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
2939
2940             pszToken = strtok(NULL, szSpace);
2941             if(!pszToken) continue;
2942             lpfp->lpszName = WININET_strdup_AtoW(pszToken);
2943             TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
2944         }
2945         /* NT way of parsing ... :
2946             
2947                 07-13-03  08:55PM       <DIR>          sakpatch
2948                 05-09-03  06:02PM             12656686 2003-04-21bgm_cmd_e.rgz
2949         */
2950         else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
2951             lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
2952             
2953             sscanf(pszToken, "%d-%d-%d",
2954                 &lpfp->tmLastModified.tm_mon,
2955                 &lpfp->tmLastModified.tm_mday,
2956                 &lpfp->tmLastModified.tm_year);
2957
2958             /* Hacky and bad Y2K protection :-) */
2959             if (lpfp->tmLastModified.tm_year < 70)
2960                 lpfp->tmLastModified.tm_year += 100;
2961             
2962             pszToken = strtok(NULL, szSpace);
2963             if(!pszToken) continue;
2964             sscanf(pszToken, "%d:%d",
2965                 &lpfp->tmLastModified.tm_hour,
2966                 &lpfp->tmLastModified.tm_min);
2967             if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
2968                 lpfp->tmLastModified.tm_hour += 12;
2969             }
2970             lpfp->tmLastModified.tm_sec = 0;
2971
2972             TRACE("Mod time: %02d:%02d:%02d  %02d/%02d/%02d\n",
2973                 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
2974                 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
2975                 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
2976             
2977             pszToken = strtok(NULL, szSpace);
2978             if(!pszToken) continue;
2979             if(!strcasecmp(pszToken, "<DIR>")) {
2980                 lpfp->bIsDirectory = TRUE;
2981                 lpfp->nSize = 0;
2982                 TRACE("Is directory\n");
2983             }
2984             else {
2985                 lpfp->bIsDirectory = FALSE;
2986                 lpfp->nSize = atol(pszToken);
2987                 TRACE("Size: %ld\n", lpfp->nSize);
2988             }
2989             
2990             pszToken = strtok(NULL, szSpace);
2991             if(!pszToken) continue;
2992             lpfp->lpszName = WININET_strdup_AtoW(pszToken);
2993             TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
2994         }
2995         /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
2996         else if(pszToken[0] == '+') {
2997             FIXME("EPLF Format not implemented\n");
2998         }
2999         
3000         if(lpfp->lpszName) {
3001             if((lpszSearchFile == NULL) ||
3002                (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3003                 found = TRUE;
3004                 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3005             }
3006             else {
3007                 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3008                 lpfp->lpszName = NULL;
3009             }
3010         }
3011     } while(!found);
3012     return TRUE;
3013 }
3014
3015 /***********************************************************************
3016  *           FTP_ParseDirectory (internal)
3017  *
3018  * Parse string of directory information
3019  *
3020  * RETURNS
3021  *   TRUE on success
3022  *   FALSE on failure
3023  */
3024 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3025     LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3026 {
3027     BOOL bSuccess = TRUE;
3028     INT sizeFilePropArray = 500;/*20; */
3029     INT indexFilePropArray = -1;
3030
3031     TRACE("\n");
3032
3033     /* Allocate intial file properties array */
3034     *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3035     if (!*lpafp)
3036         return FALSE;
3037
3038     do {
3039         if (indexFilePropArray+1 >= sizeFilePropArray)
3040         {
3041             LPFILEPROPERTIESW tmpafp;
3042             
3043             sizeFilePropArray *= 2;
3044             tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3045                 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3046             if (NULL == tmpafp)
3047             {
3048                 bSuccess = FALSE;
3049                 break;
3050             }
3051
3052             *lpafp = tmpafp;
3053         }
3054         indexFilePropArray++;
3055     } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3056
3057     if (bSuccess && indexFilePropArray)
3058     {
3059         if (indexFilePropArray < sizeFilePropArray - 1)
3060         {
3061             LPFILEPROPERTIESW tmpafp;
3062
3063             tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3064                 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3065             if (NULL == tmpafp)
3066                 *lpafp = tmpafp;
3067         }
3068         *dwfp = indexFilePropArray;
3069     }
3070     else
3071     {
3072         HeapFree(GetProcessHeap(), 0, *lpafp);
3073         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3074         bSuccess = FALSE;
3075     }
3076
3077     return bSuccess;
3078 }
3079
3080
3081 /***********************************************************************
3082  *           FTP_ParsePermission (internal)
3083  *
3084  * Parse permission string of directory information
3085  *
3086  * RETURNS
3087  *   TRUE on success
3088  *   FALSE on failure
3089  *
3090  */
3091 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3092 {
3093     BOOL bSuccess = TRUE;
3094     unsigned short nPermission = 0;
3095     INT nPos = 1;
3096     INT nLast  = 9;
3097
3098     TRACE("\n");
3099     if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3100     {
3101         bSuccess = FALSE;
3102         return bSuccess;
3103     }
3104
3105     lpfp->bIsDirectory = (*lpszPermission == 'd');
3106     do
3107     {
3108         switch (nPos)
3109         {
3110             case 1:
3111                 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3112                 break;
3113             case 2:
3114                 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3115                 break;
3116             case 3:
3117                 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3118                 break;
3119             case 4:
3120                 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3121                 break;
3122             case 5:
3123                 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3124                 break;
3125             case 6:
3126                 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3127                 break;
3128             case 7:
3129                 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3130                 break;
3131             case 8:
3132                 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3133                 break;
3134             case 9:
3135                 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3136                 break;
3137         }
3138         nPos++;
3139     }while (nPos <= nLast);
3140
3141     lpfp->permissions = nPermission;
3142     return bSuccess;
3143 }
3144
3145
3146 /***********************************************************************
3147  *           FTP_SetResponseError (internal)
3148  *
3149  * Set the appropriate error code for a given response from the server
3150  *
3151  * RETURNS
3152  *
3153  */
3154 static DWORD FTP_SetResponseError(DWORD dwResponse)
3155 {
3156     DWORD dwCode = 0;
3157
3158     switch(dwResponse)
3159     {
3160         case 421: /* Service not available - Server may be shutting down. */
3161             dwCode = ERROR_INTERNET_TIMEOUT;
3162             break;
3163
3164         case 425: /* Cannot open data connection. */
3165             dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3166             break;
3167
3168         case 426: /* Connection closed, transer aborted. */
3169             dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3170             break;
3171
3172         case 500: /* Syntax error. Command unrecognized. */
3173         case 501: /* Syntax error. Error in parameters or arguments. */
3174             dwCode = ERROR_INTERNET_INCORRECT_FORMAT;
3175             break;
3176
3177         case 530: /* Not logged in. Login incorrect. */
3178             dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3179             break;
3180
3181         case 550: /* File action not taken. File not found or no access. */
3182             dwCode = ERROR_INTERNET_ITEM_NOT_FOUND;
3183             break;
3184
3185         case 450: /* File action not taken. File may be busy. */
3186         case 451: /* Action aborted. Server error. */
3187         case 452: /* Action not taken. Insufficient storage space on server. */
3188         case 502: /* Command not implemented. */
3189         case 503: /* Bad sequence of command. */
3190         case 504: /* Command not implemented for that parameter. */
3191         case 532: /* Need account for storing files */
3192         case 551: /* Requested action aborted. Page type unknown */
3193         case 552: /* Action aborted. Exceeded storage allocation */
3194         case 553: /* Action not taken. File name not allowed. */
3195
3196         default:
3197             dwCode = ERROR_INTERNET_INTERNAL_ERROR;
3198             break;
3199     }
3200
3201     INTERNET_SetLastError(dwCode);
3202     return dwCode;
3203 }