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