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