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