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