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