quartz: Use proper alloc/free functions for COM objects.
[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 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 dwContext);
133 static DWORD FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
134 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, DWORD nBytes, 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_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize);
141 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs);
142 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs);
143 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs);
144 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
145 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
146 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
147         LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
148 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
149         LPWIN32_FIND_DATAW lpFindFileData, DWORD dwContext);
150 static DWORD FTP_SetResponseError(DWORD dwResponse);
151
152 /***********************************************************************
153  *           FtpPutFileA (WININET.@)
154  *
155  * Uploads a file to the FTP server
156  *
157  * RETURNS
158  *    TRUE on success
159  *    FALSE on failure
160  *
161  */
162 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
163     LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
164 {
165     LPWSTR lpwzLocalFile;
166     LPWSTR lpwzNewRemoteFile;
167     BOOL ret;
168     
169     lpwzLocalFile = lpszLocalFile?WININET_strdup_AtoW(lpszLocalFile):NULL;
170     lpwzNewRemoteFile = lpszNewRemoteFile?WININET_strdup_AtoW(lpszNewRemoteFile):NULL;
171     ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
172                       dwFlags, dwContext);
173     HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
174     HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
175     return ret;
176 }
177
178 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
179 {
180     struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
181     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
182
183     TRACE("%p\n", lpwfs);
184
185     FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
186                req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
187
188     HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
189     HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
190 }
191
192 /***********************************************************************
193  *           FtpPutFileW (WININET.@)
194  *
195  * Uploads a file to the FTP server
196  *
197  * RETURNS
198  *    TRUE on success
199  *    FALSE on failure
200  *
201  */
202 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
203     LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
204 {
205     LPWININETFTPSESSIONW lpwfs;
206     LPWININETAPPINFOW hIC = NULL;
207     BOOL r = FALSE;
208
209     if (!lpszLocalFile || !lpszNewRemoteFile)
210     {
211         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
212         return FALSE;
213     }
214
215     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
216     if (!lpwfs)
217     {
218         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
219         return FALSE;
220     }
221
222     if (WH_HFTPSESSION != lpwfs->hdr.htype)
223     {
224         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
225         goto lend;
226     }
227
228     if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
229     {
230         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
231         goto lend;
232     }
233
234     hIC = lpwfs->lpAppInfo;
235     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
236     {
237         WORKREQUEST workRequest;
238         struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
239
240         workRequest.asyncproc = AsyncFtpPutFileProc;
241         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
242         req->lpszLocalFile = WININET_strdupW(lpszLocalFile);
243         req->lpszNewRemoteFile = WININET_strdupW(lpszNewRemoteFile);
244         req->dwFlags = dwFlags;
245         req->dwContext = dwContext;
246
247         r = INTERNET_AsyncCall(&workRequest);
248     }
249     else
250     {
251         r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
252             lpszNewRemoteFile, dwFlags, dwContext);
253     }
254
255 lend:
256     WININET_Release( &lpwfs->hdr );
257
258     return r;
259 }
260
261 /***********************************************************************
262  *           FTP_FtpPutFileW (Internal)
263  *
264  * Uploads a file to the FTP server
265  *
266  * RETURNS
267  *    TRUE on success
268  *    FALSE on failure
269  *
270  */
271 BOOL WINAPI FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
272     LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
273 {
274     HANDLE hFile = NULL;
275     BOOL bSuccess = FALSE;
276     LPWININETAPPINFOW hIC = NULL;
277     INT nResCode;
278
279     TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
280
281     /* Clear any error information */
282     INTERNET_SetLastError(0);
283
284     /* Open file to be uploaded */
285     if (INVALID_HANDLE_VALUE ==
286         (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
287         /* Let CreateFile set the appropriate error */
288         return FALSE;
289
290     hIC = lpwfs->lpAppInfo;
291
292     SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
293
294     if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
295     {
296         INT nDataSocket;
297
298         /* Get data socket to server */
299         if (FTP_GetDataSocket(lpwfs, &nDataSocket))
300         {
301             FTP_SendData(lpwfs, nDataSocket, hFile);
302             closesocket(nDataSocket);
303             nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
304             if (nResCode)
305             {
306                 if (nResCode == 226)
307                     bSuccess = TRUE;
308                 else
309                     FTP_SetResponseError(nResCode);
310             }
311         }
312     }
313
314     if (lpwfs->lstnSocket != -1)
315         closesocket(lpwfs->lstnSocket);
316
317     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
318     {
319         INTERNET_ASYNC_RESULT iar;
320
321         iar.dwResult = (DWORD)bSuccess;
322         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
323         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
324             &iar, sizeof(INTERNET_ASYNC_RESULT));
325     }
326
327     if (hFile)
328         CloseHandle(hFile);
329
330     return bSuccess;
331 }
332
333
334 /***********************************************************************
335  *           FtpSetCurrentDirectoryA (WININET.@)
336  *
337  * Change the working directory on the FTP server
338  *
339  * RETURNS
340  *    TRUE on success
341  *    FALSE on failure
342  *
343  */
344 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
345 {
346     LPWSTR lpwzDirectory;
347     BOOL ret;
348     
349     lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
350     ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
351     HeapFree(GetProcessHeap(), 0, lpwzDirectory);
352     return ret;
353 }
354
355
356 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
357 {
358     struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
359     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
360
361     TRACE("%p\n", lpwfs);
362
363     FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
364     HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
365 }
366
367 /***********************************************************************
368  *           FtpSetCurrentDirectoryW (WININET.@)
369  *
370  * Change the working directory on the FTP server
371  *
372  * RETURNS
373  *    TRUE on success
374  *    FALSE on failure
375  *
376  */
377 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
378 {
379     LPWININETFTPSESSIONW lpwfs = NULL;
380     LPWININETAPPINFOW hIC = NULL;
381     BOOL r = FALSE;
382
383     if (!lpszDirectory)
384     {
385         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
386         goto lend;
387     }
388
389     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
390     if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
391     {
392         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
393         goto lend;
394     }
395
396     TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
397
398     hIC = lpwfs->lpAppInfo;
399     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
400     {
401         WORKREQUEST workRequest;
402         struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
403
404         workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
405         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
406         req = &workRequest.u.FtpSetCurrentDirectoryW;
407         req->lpszDirectory = WININET_strdupW(lpszDirectory);
408
409         r = INTERNET_AsyncCall(&workRequest);
410     }
411     else
412     {
413         r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
414     }
415
416 lend:
417     if( lpwfs )
418         WININET_Release( &lpwfs->hdr );
419
420     return r;
421 }
422
423
424 /***********************************************************************
425  *           FTP_FtpSetCurrentDirectoryW (Internal)
426  *
427  * Change the working directory on the FTP server
428  *
429  * RETURNS
430  *    TRUE on success
431  *    FALSE on failure
432  *
433  */
434 BOOL WINAPI FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
435 {
436     INT nResCode;
437     LPWININETAPPINFOW hIC = NULL;
438     DWORD bSuccess = FALSE;
439
440     TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
441
442     /* Clear any error information */
443     INTERNET_SetLastError(0);
444
445     hIC = lpwfs->lpAppInfo;
446     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
447         lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
448         goto lend;
449
450     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
451
452     if (nResCode)
453     {
454         if (nResCode == 250)
455             bSuccess = TRUE;
456         else
457             FTP_SetResponseError(nResCode);
458     }
459
460 lend:
461     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
462     {
463         INTERNET_ASYNC_RESULT iar;
464
465         iar.dwResult = (DWORD)bSuccess;
466         iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
467         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
468             &iar, sizeof(INTERNET_ASYNC_RESULT));
469     }
470     return bSuccess;
471 }
472
473
474 /***********************************************************************
475  *           FtpCreateDirectoryA (WININET.@)
476  *
477  * Create new directory on the FTP server
478  *
479  * RETURNS
480  *    TRUE on success
481  *    FALSE on failure
482  *
483  */
484 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
485 {
486     LPWSTR lpwzDirectory;
487     BOOL ret;
488     
489     lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
490     ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
491     HeapFree(GetProcessHeap(), 0, lpwzDirectory);
492     return ret;
493 }
494
495
496 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
497 {
498     struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
499     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
500
501     TRACE(" %p\n", lpwfs);
502
503     FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
504     HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
505 }
506
507 /***********************************************************************
508  *           FtpCreateDirectoryW (WININET.@)
509  *
510  * Create new directory on the FTP server
511  *
512  * RETURNS
513  *    TRUE on success
514  *    FALSE on failure
515  *
516  */
517 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
518 {
519     LPWININETFTPSESSIONW lpwfs;
520     LPWININETAPPINFOW hIC = NULL;
521     BOOL r = FALSE;
522
523     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
524     if (!lpwfs)
525     {
526         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
527         return FALSE;
528     }
529
530     if (WH_HFTPSESSION != lpwfs->hdr.htype)
531     {
532         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
533         goto lend;
534     }
535
536     if (!lpszDirectory)
537     {
538         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
539         goto lend;
540     }
541
542     hIC = lpwfs->lpAppInfo;
543     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
544     {
545         WORKREQUEST workRequest;
546         struct WORKREQ_FTPCREATEDIRECTORYW *req;
547
548         workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
549         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
550         req = &workRequest.u.FtpCreateDirectoryW;
551         req->lpszDirectory = WININET_strdupW(lpszDirectory);
552
553         r = INTERNET_AsyncCall(&workRequest);
554     }
555     else
556     {
557         r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
558     }
559 lend:
560     WININET_Release( &lpwfs->hdr );
561
562     return r;
563 }
564
565
566 /***********************************************************************
567  *           FTP_FtpCreateDirectoryW (Internal)
568  *
569  * Create new directory on the FTP server
570  *
571  * RETURNS
572  *    TRUE on success
573  *    FALSE on failure
574  *
575  */
576 BOOL WINAPI FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
577 {
578     INT nResCode;
579     BOOL bSuccess = FALSE;
580     LPWININETAPPINFOW hIC = NULL;
581
582     TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
583
584     /* Clear any error information */
585     INTERNET_SetLastError(0);
586
587     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
588         goto lend;
589
590     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
591     if (nResCode)
592     {
593         if (nResCode == 257)
594             bSuccess = TRUE;
595         else
596             FTP_SetResponseError(nResCode);
597     }
598
599 lend:
600     hIC = lpwfs->lpAppInfo;
601     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
602     {
603         INTERNET_ASYNC_RESULT iar;
604
605         iar.dwResult = (DWORD)bSuccess;
606         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
607         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
608             &iar, sizeof(INTERNET_ASYNC_RESULT));
609     }
610
611     return bSuccess;
612 }
613
614 /***********************************************************************
615  *           FtpFindFirstFileA (WININET.@)
616  *
617  * Search the specified directory
618  *
619  * RETURNS
620  *    HINTERNET on success
621  *    NULL on failure
622  *
623  */
624 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
625     LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD dwContext)
626 {
627     LPWSTR lpwzSearchFile;
628     WIN32_FIND_DATAW wfd;
629     LPWIN32_FIND_DATAW lpFindFileDataW;
630     HINTERNET ret;
631     
632     lpwzSearchFile = lpszSearchFile?WININET_strdup_AtoW(lpszSearchFile):NULL;
633     lpFindFileDataW = lpFindFileData?&wfd:NULL;
634     ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
635     HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
636     
637     if(lpFindFileData) {
638         WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
639     }
640     return ret;
641 }
642
643
644 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
645 {
646     struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
647     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
648
649     TRACE("%p\n", lpwfs);
650
651     FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
652        req->lpFindFileData, req->dwFlags, req->dwContext);
653     HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
654 }
655
656 /***********************************************************************
657  *           FtpFindFirstFileW (WININET.@)
658  *
659  * Search the specified directory
660  *
661  * RETURNS
662  *    HINTERNET on success
663  *    NULL on failure
664  *
665  */
666 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
667     LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD dwContext)
668 {
669     LPWININETFTPSESSIONW lpwfs;
670     LPWININETAPPINFOW hIC = NULL;
671     HINTERNET r = NULL;
672
673     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
674     if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
675     {
676         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
677         goto lend;
678     }
679
680     hIC = lpwfs->lpAppInfo;
681     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
682     {
683         WORKREQUEST workRequest;
684         struct WORKREQ_FTPFINDFIRSTFILEW *req;
685
686         workRequest.asyncproc = AsyncFtpFindFirstFileProc;
687         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
688         req = &workRequest.u.FtpFindFirstFileW;
689         req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : WININET_strdupW(lpszSearchFile);
690         req->lpFindFileData = lpFindFileData;
691         req->dwFlags = dwFlags;
692         req->dwContext= dwContext;
693
694         INTERNET_AsyncCall(&workRequest);
695         r = NULL;
696     }
697     else
698     {
699         r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
700             dwFlags, dwContext);
701     }
702 lend:
703     if( lpwfs )
704         WININET_Release( &lpwfs->hdr );
705
706     return r;
707 }
708
709
710 /***********************************************************************
711  *           FTP_FtpFindFirstFileW (Internal)
712  *
713  * Search the specified directory
714  *
715  * RETURNS
716  *    HINTERNET on success
717  *    NULL on failure
718  *
719  */
720 HINTERNET WINAPI FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
721     LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD dwContext)
722 {
723     INT nResCode;
724     LPWININETAPPINFOW hIC = NULL;
725     HINTERNET hFindNext = NULL;
726
727     TRACE("\n");
728
729     /* Clear any error information */
730     INTERNET_SetLastError(0);
731
732     if (!FTP_InitListenSocket(lpwfs))
733         goto lend;
734
735     if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
736         goto lend;
737
738     if (!FTP_SendPortOrPasv(lpwfs))
739         goto lend;
740
741     hIC = lpwfs->lpAppInfo;
742     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
743         lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
744         goto lend;
745
746     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
747     if (nResCode)
748     {
749         if (nResCode == 125 || nResCode == 150)
750         {
751             INT nDataSocket;
752
753             /* Get data socket to server */
754             if (FTP_GetDataSocket(lpwfs, &nDataSocket))
755             {
756                 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
757                 closesocket(nDataSocket);
758                 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
759                 if (nResCode != 226 && nResCode != 250)
760                     INTERNET_SetLastError(ERROR_NO_MORE_FILES);
761             }
762         }
763         else
764             FTP_SetResponseError(nResCode);
765     }
766
767 lend:
768     if (lpwfs->lstnSocket != -1)
769         closesocket(lpwfs->lstnSocket);
770
771     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
772     {
773         INTERNET_ASYNC_RESULT iar;
774
775         if (hFindNext)
776         {
777             iar.dwResult = (DWORD)hFindNext;
778             iar.dwError = ERROR_SUCCESS;
779             SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
780                 &iar, sizeof(INTERNET_ASYNC_RESULT));
781         }
782
783         iar.dwResult = (DWORD)hFindNext;
784         iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
785         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
786             &iar, sizeof(INTERNET_ASYNC_RESULT));
787     }
788
789     return hFindNext;
790 }
791
792
793 /***********************************************************************
794  *           FtpGetCurrentDirectoryA (WININET.@)
795  *
796  * Retrieves the current directory
797  *
798  * RETURNS
799  *    TRUE on success
800  *    FALSE on failure
801  *
802  */
803 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
804     LPDWORD lpdwCurrentDirectory)
805 {
806     WCHAR *dir = NULL;
807     DWORD len;
808     BOOL ret;
809
810     if(lpdwCurrentDirectory) {
811         len = *lpdwCurrentDirectory;
812         if(lpszCurrentDirectory)
813         {
814             dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
815             if (NULL == dir)
816             {
817                 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
818                 return FALSE;
819             }
820         }
821     }
822     ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
823     if(lpdwCurrentDirectory) {
824         *lpdwCurrentDirectory = len;
825         if(lpszCurrentDirectory) { 
826             WideCharToMultiByte(CP_ACP, 0, dir, len, lpszCurrentDirectory, *lpdwCurrentDirectory, NULL, NULL);
827             HeapFree(GetProcessHeap(), 0, dir);
828         }
829     }
830     return ret;
831 }
832
833
834 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
835 {
836     struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
837     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
838
839     TRACE("%p\n", lpwfs);
840
841     FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
842 }
843
844 /***********************************************************************
845  *           FtpGetCurrentDirectoryW (WININET.@)
846  *
847  * Retrieves the current directory
848  *
849  * RETURNS
850  *    TRUE on success
851  *    FALSE on failure
852  *
853  */
854 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
855     LPDWORD lpdwCurrentDirectory)
856 {
857     LPWININETFTPSESSIONW lpwfs;
858     LPWININETAPPINFOW hIC = NULL;
859     BOOL r = FALSE;
860
861     TRACE("len(%d)\n", *lpdwCurrentDirectory);
862
863     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
864     if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
865     {
866         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
867         goto lend;
868     }
869
870     hIC = lpwfs->lpAppInfo;
871     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
872     {
873         WORKREQUEST workRequest;
874         struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
875
876         workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
877         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
878         req = &workRequest.u.FtpGetCurrentDirectoryW;
879         req->lpszDirectory = lpszCurrentDirectory;
880         req->lpdwDirectory = lpdwCurrentDirectory;
881
882         r = INTERNET_AsyncCall(&workRequest);
883     }
884     else
885     {
886         r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
887             lpdwCurrentDirectory);
888     }
889
890 lend:
891     if( lpwfs )
892         WININET_Release( &lpwfs->hdr );
893
894     return r;
895 }
896
897
898 /***********************************************************************
899  *           FTP_FtpGetCurrentDirectoryW (Internal)
900  *
901  * Retrieves the current directory
902  *
903  * RETURNS
904  *    TRUE on success
905  *    FALSE on failure
906  *
907  */
908 BOOL WINAPI FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
909         LPDWORD lpdwCurrentDirectory)
910 {
911     INT nResCode;
912     LPWININETAPPINFOW hIC = NULL;
913     DWORD bSuccess = FALSE;
914
915     TRACE("len(%d)\n", *lpdwCurrentDirectory);
916
917     /* Clear any error information */
918     INTERNET_SetLastError(0);
919
920     ZeroMemory(lpszCurrentDirectory, *lpdwCurrentDirectory);
921
922     hIC = lpwfs->lpAppInfo;
923     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
924         lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
925         goto lend;
926
927     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
928     if (nResCode)
929     {
930         if (nResCode == 257) /* Extract directory name */
931         {
932             DWORD firstpos, lastpos, len;
933             LPWSTR lpszResponseBuffer = WININET_strdup_AtoW(INTERNET_GetResponseBuffer());
934
935             for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
936             {
937                 if ('"' == lpszResponseBuffer[lastpos])
938                 {
939                     if (!firstpos)
940                         firstpos = lastpos;
941                     else
942                         break;
943                 }
944             }
945
946             len = lastpos - firstpos - 1;
947             lstrcpynW(lpszCurrentDirectory, &lpszResponseBuffer[firstpos+1], *lpdwCurrentDirectory);
948             HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
949             *lpdwCurrentDirectory = len;
950             bSuccess = TRUE;
951         }
952         else
953             FTP_SetResponseError(nResCode);
954     }
955
956 lend:
957     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
958     {
959         INTERNET_ASYNC_RESULT iar;
960
961         iar.dwResult = (DWORD)bSuccess;
962         iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
963         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
964             &iar, sizeof(INTERNET_ASYNC_RESULT));
965     }
966
967     return (DWORD) bSuccess;
968 }
969
970 /***********************************************************************
971  *           FtpOpenFileA (WININET.@)
972  *
973  * Open a remote file for writing or reading
974  *
975  * RETURNS
976  *    HINTERNET handle on success
977  *    NULL on failure
978  *
979  */
980 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
981     LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
982     DWORD dwContext)
983 {
984     LPWSTR lpwzFileName;
985     HINTERNET ret;
986     
987     lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
988     ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
989     HeapFree(GetProcessHeap(), 0, lpwzFileName);
990     return ret;
991 }
992
993
994 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
995 {
996     struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
997     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
998
999     TRACE("%p\n", lpwfs);
1000
1001     FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1002         req->dwAccess, req->dwFlags, req->dwContext);
1003     HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1004 }
1005
1006 /***********************************************************************
1007  *           FtpOpenFileW (WININET.@)
1008  *
1009  * Open a remote file for writing or reading
1010  *
1011  * RETURNS
1012  *    HINTERNET handle on success
1013  *    NULL on failure
1014  *
1015  */
1016 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1017     LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1018     DWORD dwContext)
1019 {
1020     LPWININETFTPSESSIONW lpwfs;
1021     LPWININETAPPINFOW hIC = NULL;
1022     HINTERNET r = NULL;
1023     
1024     TRACE("(%p,%s,0x%08x,0x%08x,0x%08x)\n", hFtpSession,
1025         debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1026
1027     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1028     if (!lpwfs)
1029     {
1030         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1031         return FALSE;
1032     }
1033
1034     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1035     {
1036         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1037         goto lend;
1038     }
1039
1040     if ((!lpszFileName) ||
1041         ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1042         ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1043     {
1044         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1045         goto lend;
1046     }
1047
1048     if (lpwfs->download_in_progress != NULL) {
1049         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1050         goto lend;
1051     }
1052     hIC = lpwfs->lpAppInfo;
1053     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1054     {
1055         WORKREQUEST workRequest;
1056         struct WORKREQ_FTPOPENFILEW *req;
1057
1058         workRequest.asyncproc = AsyncFtpOpenFileProc;
1059         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1060         req = &workRequest.u.FtpOpenFileW;
1061         req->lpszFilename = WININET_strdupW(lpszFileName);
1062         req->dwAccess = fdwAccess;
1063         req->dwFlags = dwFlags;
1064         req->dwContext = dwContext;
1065
1066         INTERNET_AsyncCall(&workRequest);
1067         r = NULL;
1068     }
1069     else
1070     {
1071         r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1072     }
1073
1074 lend:
1075     WININET_Release( &lpwfs->hdr );
1076
1077     return r;
1078 }
1079
1080
1081 /***********************************************************************
1082  *           FTP_FtpOpenFileW (Internal)
1083  *
1084  * Open a remote file for writing or reading
1085  *
1086  * RETURNS
1087  *    HINTERNET handle on success
1088  *    NULL on failure
1089  *
1090  */
1091 HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
1092         LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1093         DWORD dwContext)
1094 {
1095     INT nDataSocket;
1096     BOOL bSuccess = FALSE;
1097     LPWININETFTPFILE lpwh = NULL;
1098     LPWININETAPPINFOW hIC = NULL;
1099     HINTERNET handle = NULL;
1100
1101     TRACE("\n");
1102
1103     /* Clear any error information */
1104     INTERNET_SetLastError(0);
1105
1106     if (GENERIC_READ == fdwAccess)
1107     {
1108         /* Set up socket to retrieve data */
1109         bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1110     }
1111     else if (GENERIC_WRITE == fdwAccess)
1112     {
1113         /* Set up socket to send data */
1114         bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1115     }
1116
1117     /* Get data socket to server */
1118     if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1119     {
1120         lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPFILE));
1121         lpwh->hdr.htype = WH_HFILE;
1122         lpwh->hdr.dwFlags = dwFlags;
1123         lpwh->hdr.dwContext = dwContext;
1124         lpwh->hdr.dwRefCount = 1;
1125         lpwh->hdr.destroy = FTP_CloseFileTransferHandle;
1126         lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1127         lpwh->nDataSocket = nDataSocket;
1128         lpwh->session_deleted = FALSE;
1129
1130         WININET_AddRef( &lpwfs->hdr );
1131         lpwh->lpFtpSession = lpwfs;
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 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 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 dwContext)
1306 {
1307     DWORD nBytes;
1308     BOOL bSuccess = FALSE;
1309     HANDLE hFile;
1310     LPWININETAPPINFOW hIC = NULL;
1311
1312     TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1313
1314     /* Clear any error information */
1315     INTERNET_SetLastError(0);
1316
1317     /* Ensure we can write to lpszNewfile by opening it */
1318     hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1319         CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1320     if (INVALID_HANDLE_VALUE == hFile)
1321         goto lend;
1322
1323     /* Set up socket to retrieve data */
1324     nBytes = FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags);
1325
1326     if (nBytes > 0)
1327     {
1328         INT nDataSocket;
1329
1330         /* Get data socket to server */
1331         if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1332         {
1333             INT nResCode;
1334
1335             /* Receive data */
1336             FTP_RetrieveFileData(lpwfs, nDataSocket, nBytes, hFile);
1337             nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1338             if (nResCode)
1339             {
1340                 if (nResCode == 226)
1341                     bSuccess = TRUE;
1342                 else
1343                     FTP_SetResponseError(nResCode);
1344             }
1345             closesocket(nDataSocket);
1346         }
1347     }
1348
1349 lend:
1350     if (lpwfs->lstnSocket != -1)
1351         closesocket(lpwfs->lstnSocket);
1352
1353     if (hFile)
1354         CloseHandle(hFile);
1355
1356     hIC = lpwfs->lpAppInfo;
1357     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1358     {
1359         INTERNET_ASYNC_RESULT iar;
1360
1361         iar.dwResult = (DWORD)bSuccess;
1362         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1363         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1364             &iar, sizeof(INTERNET_ASYNC_RESULT));
1365     }
1366
1367     return bSuccess;
1368 }
1369
1370 /***********************************************************************
1371  *           FtpGetFileSize  (WININET.@)
1372  */
1373 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1374 {
1375     FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1376
1377     if (lpdwFileSizeHigh)
1378         *lpdwFileSizeHigh = 0;
1379
1380     return 0;
1381 }
1382
1383 /***********************************************************************
1384  *           FtpDeleteFileA  (WININET.@)
1385  *
1386  * Delete a file on the ftp server
1387  *
1388  * RETURNS
1389  *    TRUE on success
1390  *    FALSE on failure
1391  *
1392  */
1393 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1394 {
1395     LPWSTR lpwzFileName;
1396     BOOL ret;
1397     
1398     lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1399     ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1400     HeapFree(GetProcessHeap(), 0, lpwzFileName);
1401     return ret;
1402 }
1403
1404 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1405 {
1406     struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1407     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1408
1409     TRACE("%p\n", lpwfs);
1410
1411     FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1412     HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1413 }
1414
1415 /***********************************************************************
1416  *           FtpDeleteFileW  (WININET.@)
1417  *
1418  * Delete a file on the ftp server
1419  *
1420  * RETURNS
1421  *    TRUE on success
1422  *    FALSE on failure
1423  *
1424  */
1425 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1426 {
1427     LPWININETFTPSESSIONW lpwfs;
1428     LPWININETAPPINFOW hIC = NULL;
1429     BOOL r = FALSE;
1430
1431     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1432     if (!lpwfs)
1433     {
1434         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1435         return FALSE;
1436     }
1437
1438     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1439     {
1440         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1441         goto lend;
1442     }
1443
1444     if (!lpszFileName)
1445     {
1446         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1447         goto lend;
1448     }
1449
1450     hIC = lpwfs->lpAppInfo;
1451     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1452     {
1453         WORKREQUEST workRequest;
1454         struct WORKREQ_FTPDELETEFILEW *req;
1455
1456         workRequest.asyncproc = AsyncFtpDeleteFileProc;
1457         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1458         req = &workRequest.u.FtpDeleteFileW;
1459         req->lpszFilename = WININET_strdupW(lpszFileName);
1460
1461         r = INTERNET_AsyncCall(&workRequest);
1462     }
1463     else
1464     {
1465         r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1466     }
1467
1468 lend:
1469     WININET_Release( &lpwfs->hdr );
1470
1471     return r;
1472 }
1473
1474 /***********************************************************************
1475  *           FTP_FtpDeleteFileW  (Internal)
1476  *
1477  * Delete a file on the ftp server
1478  *
1479  * RETURNS
1480  *    TRUE on success
1481  *    FALSE on failure
1482  *
1483  */
1484 BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName)
1485 {
1486     INT nResCode;
1487     BOOL bSuccess = FALSE;
1488     LPWININETAPPINFOW hIC = NULL;
1489
1490     TRACE("%p\n", lpwfs);
1491
1492     /* Clear any error information */
1493     INTERNET_SetLastError(0);
1494
1495     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1496         goto lend;
1497
1498     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1499     if (nResCode)
1500     {
1501         if (nResCode == 250)
1502             bSuccess = TRUE;
1503         else
1504             FTP_SetResponseError(nResCode);
1505     }
1506 lend:
1507     hIC = lpwfs->lpAppInfo;
1508     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1509     {
1510         INTERNET_ASYNC_RESULT iar;
1511
1512         iar.dwResult = (DWORD)bSuccess;
1513         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1514         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1515             &iar, sizeof(INTERNET_ASYNC_RESULT));
1516     }
1517
1518     return bSuccess;
1519 }
1520
1521
1522 /***********************************************************************
1523  *           FtpRemoveDirectoryA  (WININET.@)
1524  *
1525  * Remove a directory on the ftp server
1526  *
1527  * RETURNS
1528  *    TRUE on success
1529  *    FALSE on failure
1530  *
1531  */
1532 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1533 {
1534     LPWSTR lpwzDirectory;
1535     BOOL ret;
1536     
1537     lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
1538     ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1539     HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1540     return ret;
1541 }
1542
1543 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1544 {
1545     struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1546     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1547
1548     TRACE("%p\n", lpwfs);
1549
1550     FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1551     HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1552 }
1553
1554 /***********************************************************************
1555  *           FtpRemoveDirectoryW  (WININET.@)
1556  *
1557  * Remove a directory on the ftp server
1558  *
1559  * RETURNS
1560  *    TRUE on success
1561  *    FALSE on failure
1562  *
1563  */
1564 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1565 {
1566     LPWININETFTPSESSIONW lpwfs;
1567     LPWININETAPPINFOW hIC = NULL;
1568     BOOL r = FALSE;
1569
1570     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1571     if (!lpwfs)
1572     {
1573         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1574         return FALSE;
1575     }
1576
1577     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1578     {
1579         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1580         goto lend;
1581     }
1582
1583     if (!lpszDirectory)
1584     {
1585         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1586         goto lend;
1587     }
1588
1589     hIC = lpwfs->lpAppInfo;
1590     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1591     {
1592         WORKREQUEST workRequest;
1593         struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1594
1595         workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1596         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1597         req = &workRequest.u.FtpRemoveDirectoryW;
1598         req->lpszDirectory = WININET_strdupW(lpszDirectory);
1599
1600         r = INTERNET_AsyncCall(&workRequest);
1601     }
1602     else
1603     {
1604         r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1605     }
1606
1607 lend:
1608     WININET_Release( &lpwfs->hdr );
1609
1610     return r;
1611 }
1612
1613 /***********************************************************************
1614  *           FTP_FtpRemoveDirectoryW  (Internal)
1615  *
1616  * Remove a directory on the ftp server
1617  *
1618  * RETURNS
1619  *    TRUE on success
1620  *    FALSE on failure
1621  *
1622  */
1623 BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
1624 {
1625     INT nResCode;
1626     BOOL bSuccess = FALSE;
1627     LPWININETAPPINFOW hIC = NULL;
1628
1629     TRACE("\n");
1630
1631     /* Clear any error information */
1632     INTERNET_SetLastError(0);
1633
1634     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1635         goto lend;
1636
1637     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1638     if (nResCode)
1639     {
1640         if (nResCode == 250)
1641             bSuccess = TRUE;
1642         else
1643             FTP_SetResponseError(nResCode);
1644     }
1645
1646 lend:
1647     hIC = lpwfs->lpAppInfo;
1648     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1649     {
1650         INTERNET_ASYNC_RESULT iar;
1651
1652         iar.dwResult = (DWORD)bSuccess;
1653         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1654         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1655             &iar, sizeof(INTERNET_ASYNC_RESULT));
1656     }
1657
1658     return bSuccess;
1659 }
1660
1661
1662 /***********************************************************************
1663  *           FtpRenameFileA  (WININET.@)
1664  *
1665  * Rename a file on the ftp server
1666  *
1667  * RETURNS
1668  *    TRUE on success
1669  *    FALSE on failure
1670  *
1671  */
1672 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1673 {
1674     LPWSTR lpwzSrc;
1675     LPWSTR lpwzDest;
1676     BOOL ret;
1677     
1678     lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL;
1679     lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL;
1680     ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1681     HeapFree(GetProcessHeap(), 0, lpwzSrc);
1682     HeapFree(GetProcessHeap(), 0, lpwzDest);
1683     return ret;
1684 }
1685
1686 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
1687 {
1688     struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
1689     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1690
1691     TRACE("%p\n", lpwfs);
1692
1693     FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
1694     HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
1695     HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
1696 }
1697
1698 /***********************************************************************
1699  *           FtpRenameFileW  (WININET.@)
1700  *
1701  * Rename a file on the ftp server
1702  *
1703  * RETURNS
1704  *    TRUE on success
1705  *    FALSE on failure
1706  *
1707  */
1708 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
1709 {
1710     LPWININETFTPSESSIONW lpwfs;
1711     LPWININETAPPINFOW hIC = NULL;
1712     BOOL r = FALSE;
1713
1714     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1715     if (!lpwfs)
1716     {
1717         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1718         return FALSE;
1719     }
1720
1721     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1722     {
1723         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1724         goto lend;
1725     }
1726
1727     if (!lpszSrc || !lpszDest)
1728     {
1729         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1730         goto lend;
1731     }
1732
1733     hIC = lpwfs->lpAppInfo;
1734     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1735     {
1736         WORKREQUEST workRequest;
1737         struct WORKREQ_FTPRENAMEFILEW *req;
1738
1739         workRequest.asyncproc = AsyncFtpRenameFileProc;
1740         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1741         req = &workRequest.u.FtpRenameFileW;
1742         req->lpszSrcFile = WININET_strdupW(lpszSrc);
1743         req->lpszDestFile = WININET_strdupW(lpszDest);
1744
1745         r = INTERNET_AsyncCall(&workRequest);
1746     }
1747     else
1748     {
1749         r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
1750     }
1751
1752 lend:
1753     WININET_Release( &lpwfs->hdr );
1754
1755     return r;
1756 }
1757
1758 /***********************************************************************
1759  *           FTP_FtpRenameFileW  (Internal)
1760  *
1761  * Rename a file on the ftp server
1762  *
1763  * RETURNS
1764  *    TRUE on success
1765  *    FALSE on failure
1766  *
1767  */
1768 BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs,
1769                          LPCWSTR lpszSrc, LPCWSTR lpszDest)
1770 {
1771     INT nResCode;
1772     BOOL bSuccess = FALSE;
1773     LPWININETAPPINFOW hIC = NULL;
1774
1775     TRACE("\n");
1776
1777     /* Clear any error information */
1778     INTERNET_SetLastError(0);
1779
1780     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
1781         goto lend;
1782
1783     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1784     if (nResCode == 350)
1785     {
1786         if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
1787             goto lend;
1788
1789         nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1790     }
1791
1792     if (nResCode == 250)
1793         bSuccess = TRUE;
1794     else
1795         FTP_SetResponseError(nResCode);
1796
1797 lend:
1798     hIC = lpwfs->lpAppInfo;
1799     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1800     {
1801         INTERNET_ASYNC_RESULT iar;
1802
1803         iar.dwResult = (DWORD)bSuccess;
1804         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1805         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1806             &iar, sizeof(INTERNET_ASYNC_RESULT));
1807     }
1808
1809     return bSuccess;
1810 }
1811
1812 /***********************************************************************
1813  *           FtpCommandA  (WININET.@)
1814  */
1815 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1816                          LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1817 {
1818     FIXME("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1819           debugstr_a(lpszCommand), dwContext, phFtpCommand);
1820
1821     return TRUE;
1822 }
1823
1824 /***********************************************************************
1825  *           FtpCommandW  (WININET.@)
1826  */
1827 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1828                          LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1829 {
1830     FIXME("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1831           debugstr_w(lpszCommand), dwContext, phFtpCommand);
1832
1833     return TRUE;
1834 }
1835
1836 /***********************************************************************
1837  *           FTP_Connect (internal)
1838  *
1839  * Connect to a ftp server
1840  *
1841  * RETURNS
1842  *   HINTERNET a session handle on success
1843  *   NULL on failure
1844  *
1845  * NOTES:
1846  *
1847  * Windows uses 'anonymous' as the username, when given a NULL username
1848  * and a NULL password. The password is first looked up in:
1849  *
1850  * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
1851  *
1852  * If this entry is not present it uses the current username as the password.
1853  *
1854  */
1855
1856 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
1857         INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
1858         LPCWSTR lpszPassword, DWORD dwFlags, DWORD dwContext,
1859         DWORD dwInternalFlags)
1860 {
1861     static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
1862                                    'M','i','c','r','o','s','o','f','t','\\',
1863                                    'W','i','n','d','o','w','s','\\',
1864                                    'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1865                                    'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
1866     static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
1867     static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
1868     static const WCHAR szEmpty[] = {'\0'};
1869     struct sockaddr_in socketAddr;
1870     INT nsocket = -1;
1871     UINT sock_namelen;
1872     BOOL bSuccess = FALSE;
1873     LPWININETFTPSESSIONW lpwfs = NULL;
1874     HINTERNET handle = NULL;
1875
1876     TRACE("%p  Server(%s) Port(%d) User(%s) Paswd(%s)\n",
1877             hIC, debugstr_w(lpszServerName),
1878             nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
1879
1880     assert( hIC->hdr.htype == WH_HINIT );
1881
1882     if (NULL == lpszUserName && NULL != lpszPassword)
1883     {
1884         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1885         goto lerror;
1886     }
1887     
1888     lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
1889     if (NULL == lpwfs)
1890     {
1891         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1892         goto lerror;
1893     }
1894
1895     if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
1896         nServerPort = INTERNET_DEFAULT_FTP_PORT;
1897
1898     lpwfs->hdr.htype = WH_HFTPSESSION;
1899     lpwfs->hdr.dwFlags = dwFlags;
1900     lpwfs->hdr.dwContext = dwContext;
1901     lpwfs->hdr.dwInternalFlags = dwInternalFlags;
1902     lpwfs->hdr.dwRefCount = 1;
1903     lpwfs->hdr.destroy = FTP_CloseSessionHandle;
1904     lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
1905     lpwfs->download_in_progress = NULL;
1906
1907     WININET_AddRef( &hIC->hdr );
1908     lpwfs->lpAppInfo = hIC;
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 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 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 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  *           FTP_GetFileSize (internal)
2433  *
2434  * Retrieves from the server the size of the given file
2435  *
2436  * RETURNS
2437  *   TRUE on success
2438  *   FALSE on failure
2439  *
2440  */
2441 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2442 {
2443     INT nResCode;
2444     BOOL bSuccess = FALSE;
2445
2446     TRACE("\n");
2447
2448     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2449         goto lend;
2450
2451     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2452     if (nResCode)
2453     {
2454         if (nResCode == 213) {
2455             /* Now parses the output to get the actual file size */
2456             int i;
2457             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2458
2459             for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2460             if (lpszResponseBuffer[i] == '\0') return FALSE;
2461             *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2462             
2463             bSuccess = TRUE;
2464         } else {
2465             FTP_SetResponseError(nResCode);
2466         }
2467     }
2468
2469 lend:
2470     return bSuccess;
2471 }
2472
2473
2474 /***********************************************************************
2475  *           FTP_SendPort (internal)
2476  *
2477  * Tell server which port to use
2478  *
2479  * RETURNS
2480  *   TRUE on success
2481  *   FALSE on failure
2482  *
2483  */
2484 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2485 {
2486     static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2487     INT nResCode;
2488     WCHAR szIPAddress[64];
2489     BOOL bSuccess = FALSE;
2490     TRACE("\n");
2491
2492     sprintfW(szIPAddress, szIPFormat,
2493          lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2494         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2495         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2496         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2497         lpwfs->lstnSocketAddress.sin_port & 0xFF,
2498         (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2499
2500     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2501         goto lend;
2502
2503     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2504     if (nResCode)
2505     {
2506         if (nResCode == 200)
2507             bSuccess = TRUE;
2508         else
2509             FTP_SetResponseError(nResCode);
2510     }
2511
2512 lend:
2513     return bSuccess;
2514 }
2515
2516
2517 /***********************************************************************
2518  *           FTP_DoPassive (internal)
2519  *
2520  * Tell server that we want to do passive transfers
2521  * and connect data socket
2522  *
2523  * RETURNS
2524  *   TRUE on success
2525  *   FALSE on failure
2526  *
2527  */
2528 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2529 {
2530     INT nResCode;
2531     BOOL bSuccess = FALSE;
2532
2533     TRACE("\n");
2534     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2535         goto lend;
2536
2537     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2538     if (nResCode)
2539     {
2540         if (nResCode == 227)
2541         {
2542             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2543             LPSTR p;
2544             int f[6];
2545             int i;
2546             char *pAddr, *pPort;
2547             INT nsocket = -1;
2548             struct sockaddr_in dataSocketAddress;
2549
2550             p = lpszResponseBuffer+4; /* skip status code */
2551
2552             /* do a very strict check; we can improve that later. */
2553
2554             if (strncmp(p, "Entering Passive Mode", 21))
2555             {
2556                 ERR("unknown response '%.*s', aborting\n", 21, p);
2557                 goto lend;
2558             }
2559             p += 21; /* skip string */
2560             if ((*p++ != ' ') || (*p++ != '('))
2561             {
2562                 ERR("unknown response format, aborting\n");
2563                 goto lend;
2564             }
2565
2566             if (sscanf(p, "%d,%d,%d,%d,%d,%d",  &f[0], &f[1], &f[2], &f[3],
2567                                                 &f[4], &f[5]) != 6)
2568             {
2569                 ERR("unknown response address format '%s', aborting\n", p);
2570                 goto lend;
2571             }
2572             for (i=0; i < 6; i++)
2573                 f[i] = f[i] & 0xff;
2574
2575             dataSocketAddress = lpwfs->socketAddress;
2576             pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2577             pPort = (char *)&(dataSocketAddress.sin_port);
2578             pAddr[0] = f[0];
2579             pAddr[1] = f[1];
2580             pAddr[2] = f[2];
2581             pAddr[3] = f[3];
2582             pPort[0] = f[4];
2583             pPort[1] = f[5];
2584
2585             nsocket = socket(AF_INET,SOCK_STREAM,0);
2586             if (nsocket == -1)
2587                 goto lend;
2588
2589             if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2590             {
2591                 ERR("can't connect passive FTP data port.\n");
2592                 closesocket(nsocket);
2593                 goto lend;
2594             }
2595             lpwfs->pasvSocket = nsocket;
2596             bSuccess = TRUE;
2597         }
2598         else
2599             FTP_SetResponseError(nResCode);
2600     }
2601
2602 lend:
2603     return bSuccess;
2604 }
2605
2606
2607 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2608 {
2609     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2610     {
2611         if (!FTP_DoPassive(lpwfs))
2612             return FALSE;
2613     }
2614     else
2615     {
2616         if (!FTP_SendPort(lpwfs))
2617             return FALSE;
2618     }
2619     return TRUE;
2620 }
2621
2622
2623 /***********************************************************************
2624  *           FTP_GetDataSocket (internal)
2625  *
2626  * Either accepts an incoming data socket connection from the server
2627  * or just returns the already opened socket after a PASV command
2628  * in case of passive FTP.
2629  *
2630  *
2631  * RETURNS
2632  *   TRUE on success
2633  *   FALSE on failure
2634  *
2635  */
2636 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
2637 {
2638     struct sockaddr_in saddr;
2639     socklen_t addrlen = sizeof(struct sockaddr);
2640
2641     TRACE("\n");
2642     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2643     {
2644         *nDataSocket = lpwfs->pasvSocket;
2645     }
2646     else
2647     {
2648         *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
2649         closesocket(lpwfs->lstnSocket);
2650         lpwfs->lstnSocket = -1;
2651     }
2652     return *nDataSocket != -1;
2653 }
2654
2655
2656 /***********************************************************************
2657  *           FTP_SendData (internal)
2658  *
2659  * Send data to the server
2660  *
2661  * RETURNS
2662  *   TRUE on success
2663  *   FALSE on failure
2664  *
2665  */
2666 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
2667 {
2668     BY_HANDLE_FILE_INFORMATION fi;
2669     DWORD nBytesRead = 0;
2670     DWORD nBytesSent = 0;
2671     DWORD nTotalSent = 0;
2672     DWORD nBytesToSend, nLen;
2673     int nRC = 1;
2674     time_t s_long_time, e_long_time;
2675     LONG nSeconds;
2676     CHAR *lpszBuffer;
2677
2678     TRACE("\n");
2679     lpszBuffer = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR)*DATA_PACKET_SIZE);
2680     memset(lpszBuffer, 0, sizeof(CHAR)*DATA_PACKET_SIZE);
2681
2682     /* Get the size of the file. */
2683     GetFileInformationByHandle(hFile, &fi);
2684     time(&s_long_time);
2685
2686     do
2687     {
2688         nBytesToSend = nBytesRead - nBytesSent;
2689
2690         if (nBytesToSend <= 0)
2691         {
2692             /* Read data from file. */
2693             nBytesSent = 0;
2694             if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
2695             ERR("Failed reading from file\n");
2696
2697             if (nBytesRead > 0)
2698                 nBytesToSend = nBytesRead;
2699             else
2700                 break;
2701         }
2702
2703         nLen = DATA_PACKET_SIZE < nBytesToSend ?
2704             DATA_PACKET_SIZE : nBytesToSend;
2705         nRC  = send(nDataSocket, lpszBuffer, nLen, 0);
2706
2707         if (nRC != -1)
2708         {
2709             nBytesSent += nRC;
2710             nTotalSent += nRC;
2711         }
2712
2713         /* Do some computation to display the status. */
2714         time(&e_long_time);
2715         nSeconds = e_long_time - s_long_time;
2716         if( nSeconds / 60 > 0 )
2717         {
2718             TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
2719             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
2720             nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
2721         }
2722         else
2723         {
2724             TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
2725             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
2726             (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
2727         }
2728     } while (nRC != -1);
2729
2730     TRACE("file transfer complete!\n");
2731
2732     HeapFree(GetProcessHeap(), 0, lpszBuffer);
2733
2734     return nTotalSent;
2735 }
2736
2737
2738 /***********************************************************************
2739  *           FTP_SendRetrieve (internal)
2740  *
2741  * Send request to retrieve a file
2742  *
2743  * RETURNS
2744  *   Number of bytes to be received on success
2745  *   0 on failure
2746  *
2747  */
2748 static DWORD FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2749 {
2750     INT nResCode;
2751     DWORD nResult = 0;
2752
2753     TRACE("\n");
2754     if (!FTP_InitListenSocket(lpwfs))
2755         goto lend;
2756
2757     if (!FTP_SendType(lpwfs, dwType))
2758         goto lend;
2759
2760     if (!FTP_SendPortOrPasv(lpwfs))
2761         goto lend;
2762
2763     if (!FTP_GetFileSize(lpwfs, lpszRemoteFile, &nResult))
2764         goto lend;
2765
2766     TRACE("Waiting to receive %d bytes\n", nResult);
2767     
2768     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0))
2769         goto lend;
2770
2771     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2772     if ((nResCode != 125) && (nResCode != 150)) {
2773         /* That means that we got an error getting the file. */
2774         nResult = 0;
2775     }
2776
2777 lend:
2778     if (0 == nResult && lpwfs->lstnSocket != -1)
2779     {
2780         closesocket(lpwfs->lstnSocket);
2781         lpwfs->lstnSocket = -1;
2782     }
2783
2784     return nResult;
2785 }
2786
2787
2788 /***********************************************************************
2789  *           FTP_RetrieveData  (internal)
2790  *
2791  * Retrieve data from server
2792  *
2793  * RETURNS
2794  *   TRUE on success
2795  *   FALSE on failure
2796  *
2797  */
2798 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile)
2799 {
2800     DWORD nBytesWritten;
2801     DWORD nBytesReceived = 0;
2802     INT nRC = 0;
2803     CHAR *lpszBuffer;
2804
2805     TRACE("\n");
2806
2807     if (INVALID_HANDLE_VALUE == hFile)
2808         return FALSE;
2809
2810     lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2811     if (NULL == lpszBuffer)
2812     {
2813         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2814         return FALSE;
2815     }
2816
2817     while (nBytesReceived < nBytes && nRC != -1)
2818     {
2819         nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
2820         if (nRC != -1)
2821         {
2822             /* other side closed socket. */
2823             if (nRC == 0)
2824                 goto recv_end;
2825             WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
2826             nBytesReceived += nRC;
2827         }
2828
2829         TRACE("%d bytes of %d (%d%%)\r", nBytesReceived, nBytes,
2830            nBytesReceived * 100 / nBytes);
2831     }
2832
2833     TRACE("Data transfer complete\n");
2834     HeapFree(GetProcessHeap(), 0, lpszBuffer);
2835
2836 recv_end:
2837     return  (nRC != -1);
2838 }
2839
2840
2841 /***********************************************************************
2842  *           FTP_CloseSessionHandle (internal)
2843  *
2844  * Deallocate session handle
2845  *
2846  * RETURNS
2847  *   TRUE on success
2848  *   FALSE on failure
2849  *
2850  */
2851 static void FTP_CloseSessionHandle(LPWININETHANDLEHEADER hdr)
2852 {
2853     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2854
2855     TRACE("\n");
2856
2857     WININET_Release(&lpwfs->lpAppInfo->hdr);
2858
2859     if (lpwfs->download_in_progress != NULL)
2860         lpwfs->download_in_progress->session_deleted = TRUE;
2861     
2862     if (lpwfs->sndSocket != -1)
2863         closesocket(lpwfs->sndSocket);
2864
2865     if (lpwfs->lstnSocket != -1)
2866         closesocket(lpwfs->lstnSocket);
2867
2868     HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2869     HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2870     HeapFree(GetProcessHeap(), 0, lpwfs);
2871 }
2872
2873
2874 /***********************************************************************
2875  *           FTP_FindNextFileW (Internal)
2876  *
2877  * Continues a file search from a previous call to FindFirstFile
2878  *
2879  * RETURNS
2880  *    TRUE on success
2881  *    FALSE on failure
2882  *
2883  */
2884 BOOL WINAPI FTP_FindNextFileW(LPWININETFTPFINDNEXTW lpwh, LPVOID lpvFindData)
2885 {
2886     BOOL bSuccess = TRUE;
2887     LPWIN32_FIND_DATAW lpFindFileData;
2888
2889     TRACE("index(%d) size(%d)\n", lpwh->index, lpwh->size);
2890
2891     assert (lpwh->hdr.htype == WH_HFTPFINDNEXT);
2892
2893     /* Clear any error information */
2894     INTERNET_SetLastError(0);
2895
2896     lpFindFileData = (LPWIN32_FIND_DATAW) lpvFindData;
2897     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
2898
2899     if (lpwh->index >= lpwh->size)
2900     {
2901         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
2902         bSuccess = FALSE;
2903         goto lend;
2904     }
2905
2906     FTP_ConvertFileProp(&lpwh->lpafp[lpwh->index], lpFindFileData);
2907     lpwh->index++;
2908
2909     TRACE("\nName: %s\nSize: %d\n", debugstr_w(lpFindFileData->cFileName), lpFindFileData->nFileSizeLow);
2910
2911 lend:
2912
2913     if (lpwh->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2914     {
2915         INTERNET_ASYNC_RESULT iar;
2916
2917         iar.dwResult = (DWORD)bSuccess;
2918         iar.dwError = iar.dwError = bSuccess ? ERROR_SUCCESS :
2919                                                INTERNET_GetLastError();
2920
2921         INTERNET_SendCallback(&lpwh->hdr, lpwh->hdr.dwContext,
2922                               INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2923                               sizeof(INTERNET_ASYNC_RESULT));
2924     }
2925
2926     return bSuccess;
2927 }
2928
2929
2930 /***********************************************************************
2931  *           FTP_CloseFindNextHandle (internal)
2932  *
2933  * Deallocate session handle
2934  *
2935  * RETURNS
2936  *   TRUE on success
2937  *   FALSE on failure
2938  *
2939  */
2940 static void FTP_CloseFindNextHandle(LPWININETHANDLEHEADER hdr)
2941 {
2942     LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
2943     DWORD i;
2944
2945     TRACE("\n");
2946
2947     WININET_Release(&lpwfn->lpFtpSession->hdr);
2948
2949     for (i = 0; i < lpwfn->size; i++)
2950     {
2951         HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
2952     }
2953
2954     HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
2955     HeapFree(GetProcessHeap(), 0, lpwfn);
2956 }
2957
2958 /***********************************************************************
2959  *           FTP_CloseFileTransferHandle (internal)
2960  *
2961  * Closes the file transfer handle. This also 'cleans' the data queue of
2962  * the 'transfer conplete' message (this is a bit of a hack though :-/ )
2963  *
2964  */
2965 static void FTP_CloseFileTransferHandle(LPWININETHANDLEHEADER hdr)
2966 {
2967     LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
2968     LPWININETFTPSESSIONW lpwfs = lpwh->lpFtpSession;
2969     INT nResCode;
2970
2971     TRACE("\n");
2972
2973     WININET_Release(&lpwh->lpFtpSession->hdr);
2974
2975     if (!lpwh->session_deleted)
2976         lpwfs->download_in_progress = NULL;
2977
2978     /* This just serves to flush the control socket of any spurrious lines written
2979        to it (like '226 Transfer complete.').
2980
2981        Wonder what to do if the server sends us an error code though...
2982     */
2983     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2984     
2985     if (lpwh->nDataSocket != -1)
2986         closesocket(lpwh->nDataSocket);
2987
2988     HeapFree(GetProcessHeap(), 0, lpwh);
2989 }
2990
2991 /***********************************************************************
2992  *           FTP_ReceiveFileList (internal)
2993  *
2994  * Read file list from server
2995  *
2996  * RETURNS
2997  *   Handle to file list on success
2998  *   NULL on failure
2999  *
3000  */
3001 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3002         LPWIN32_FIND_DATAW lpFindFileData, DWORD dwContext)
3003 {
3004     DWORD dwSize = 0;
3005     LPFILEPROPERTIESW lpafp = NULL;
3006     LPWININETFTPFINDNEXTW lpwfn = NULL;
3007     HINTERNET handle = 0;
3008
3009     TRACE("(%p,%d,%s,%p,%d)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3010
3011     if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3012     {
3013         if(lpFindFileData)
3014             FTP_ConvertFileProp(lpafp, lpFindFileData);
3015
3016         lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3017         if (lpwfn)
3018         {
3019             lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3020             lpwfn->hdr.dwContext = dwContext;
3021             lpwfn->hdr.dwRefCount = 1;
3022             lpwfn->hdr.destroy = FTP_CloseFindNextHandle;
3023             lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3024             lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3025             lpwfn->size = dwSize;
3026             lpwfn->lpafp = lpafp;
3027
3028             WININET_AddRef( &lpwfs->hdr );
3029             lpwfn->lpFtpSession = lpwfs;
3030
3031             handle = WININET_AllocHandle( &lpwfn->hdr );
3032         }
3033     }
3034
3035     if( lpwfn )
3036         WININET_Release( &lpwfn->hdr );
3037
3038     TRACE("Matched %d files\n", dwSize);
3039     return handle;
3040 }
3041
3042
3043 /***********************************************************************
3044  *           FTP_ConvertFileProp (internal)
3045  *
3046  * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3047  *
3048  * RETURNS
3049  *   TRUE on success
3050  *   FALSE on failure
3051  *
3052  */
3053 BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3054 {
3055     BOOL bSuccess = FALSE;
3056
3057     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3058
3059     if (lpafp)
3060     {
3061         /* Convert 'Unix' time to Windows time */
3062         RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
3063                                   (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
3064         lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3065         lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3066         
3067         /* Not all fields are filled in */
3068         lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3069         lpFindFileData->nFileSizeLow = lpafp->nSize;
3070
3071         if (lpafp->bIsDirectory)
3072             lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3073
3074         if (lpafp->lpszName)
3075             lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3076
3077         bSuccess = TRUE;
3078     }
3079
3080     return bSuccess;
3081 }
3082
3083 /***********************************************************************
3084  *           FTP_ParseNextFile (internal)
3085  *
3086  * Parse the next line in file listing
3087  *
3088  * RETURNS
3089  *   TRUE on success
3090  *   FALSE on failure
3091  */
3092 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3093 {
3094     static const char szSpace[] = " \t";
3095     DWORD nBufLen;
3096     char *pszLine;
3097     char *pszToken;
3098     char *pszTmp;
3099     BOOL found = FALSE;
3100     int i;
3101     
3102     lpfp->lpszName = NULL;
3103     do {
3104         if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3105             return FALSE;
3106     
3107         pszToken = strtok(pszLine, szSpace);
3108         /* ls format
3109          * <Permissions> <NoLinks> <owner>   <group> <size> <date>  <time or year> <filename>
3110          *
3111          * For instance:
3112          * drwx--s---     2         pcarrier  ens     512    Sep 28  1995           pcarrier
3113          */
3114         if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3115             if(!FTP_ParsePermission(pszToken, lpfp))
3116                 lpfp->bIsDirectory = FALSE;
3117             for(i=0; i<=3; i++) {
3118               if(!(pszToken = strtok(NULL, szSpace)))
3119                   break;
3120             }
3121             if(!pszToken) continue;
3122             if(lpfp->bIsDirectory) {
3123                 TRACE("Is directory\n");
3124                 lpfp->nSize = 0;
3125             }
3126             else {
3127                 TRACE("Size: %s\n", pszToken);
3128                 lpfp->nSize = atol(pszToken);
3129             }
3130             
3131             lpfp->tmLastModified.tm_sec  = 0;
3132             lpfp->tmLastModified.tm_min  = 0;
3133             lpfp->tmLastModified.tm_hour = 0;
3134             lpfp->tmLastModified.tm_mday = 0;
3135             lpfp->tmLastModified.tm_mon  = 0;
3136             lpfp->tmLastModified.tm_year = 0;
3137             
3138             /* Determine month */
3139             pszToken = strtok(NULL, szSpace);
3140             if(!pszToken) continue;
3141             if(strlen(pszToken) >= 3) {
3142                 pszToken[3] = 0;
3143                 if((pszTmp = StrStrIA(szMonths, pszToken)))
3144                     lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
3145             }
3146             /* Determine day */
3147             pszToken = strtok(NULL, szSpace);
3148             if(!pszToken) continue;
3149             lpfp->tmLastModified.tm_mday = atoi(pszToken);
3150             /* Determine time or year */
3151             pszToken = strtok(NULL, szSpace);
3152             if(!pszToken) continue;
3153             if((pszTmp = strchr(pszToken, ':'))) {
3154                 struct tm* apTM;
3155                 time_t aTime;
3156                 *pszTmp = 0;
3157                 pszTmp++;
3158                 lpfp->tmLastModified.tm_min = atoi(pszTmp);
3159                 lpfp->tmLastModified.tm_hour = atoi(pszToken);
3160                 time(&aTime);
3161                 apTM = localtime(&aTime);
3162                 lpfp->tmLastModified.tm_year = apTM->tm_year;
3163             }
3164             else {
3165                 lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
3166                 lpfp->tmLastModified.tm_hour = 12;
3167             }
3168             TRACE("Mod time: %02d:%02d:%02d  %02d/%02d/%02d\n",
3169                 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3170                 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3171                 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3172
3173             pszToken = strtok(NULL, szSpace);
3174             if(!pszToken) continue;
3175             lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3176             TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3177         }
3178         /* NT way of parsing ... :
3179             
3180                 07-13-03  08:55PM       <DIR>          sakpatch
3181                 05-09-03  06:02PM             12656686 2003-04-21bgm_cmd_e.rgz
3182         */
3183         else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3184             lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3185             
3186             sscanf(pszToken, "%d-%d-%d",
3187                 &lpfp->tmLastModified.tm_mon,
3188                 &lpfp->tmLastModified.tm_mday,
3189                 &lpfp->tmLastModified.tm_year);
3190
3191             /* Hacky and bad Y2K protection :-) */
3192             if (lpfp->tmLastModified.tm_year < 70)
3193                 lpfp->tmLastModified.tm_year += 100;
3194             
3195             pszToken = strtok(NULL, szSpace);
3196             if(!pszToken) continue;
3197             sscanf(pszToken, "%d:%d",
3198                 &lpfp->tmLastModified.tm_hour,
3199                 &lpfp->tmLastModified.tm_min);
3200             if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3201                 lpfp->tmLastModified.tm_hour += 12;
3202             }
3203             lpfp->tmLastModified.tm_sec = 0;
3204
3205             TRACE("Mod time: %02d:%02d:%02d  %02d/%02d/%02d\n",
3206                 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3207                 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3208                 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3209             
3210             pszToken = strtok(NULL, szSpace);
3211             if(!pszToken) continue;
3212             if(!strcasecmp(pszToken, "<DIR>")) {
3213                 lpfp->bIsDirectory = TRUE;
3214                 lpfp->nSize = 0;
3215                 TRACE("Is directory\n");
3216             }
3217             else {
3218                 lpfp->bIsDirectory = FALSE;
3219                 lpfp->nSize = atol(pszToken);
3220                 TRACE("Size: %d\n", lpfp->nSize);
3221             }
3222             
3223             pszToken = strtok(NULL, szSpace);
3224             if(!pszToken) continue;
3225             lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3226             TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3227         }
3228         /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3229         else if(pszToken[0] == '+') {
3230             FIXME("EPLF Format not implemented\n");
3231         }
3232         
3233         if(lpfp->lpszName) {
3234             if((lpszSearchFile == NULL) ||
3235                (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3236                 found = TRUE;
3237                 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3238             }
3239             else {
3240                 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3241                 lpfp->lpszName = NULL;
3242             }
3243         }
3244     } while(!found);
3245     return TRUE;
3246 }
3247
3248 /***********************************************************************
3249  *           FTP_ParseDirectory (internal)
3250  *
3251  * Parse string of directory information
3252  *
3253  * RETURNS
3254  *   TRUE on success
3255  *   FALSE on failure
3256  */
3257 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3258     LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3259 {
3260     BOOL bSuccess = TRUE;
3261     INT sizeFilePropArray = 500;/*20; */
3262     INT indexFilePropArray = -1;
3263
3264     TRACE("\n");
3265
3266     /* Allocate intial file properties array */
3267     *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3268     if (!*lpafp)
3269         return FALSE;
3270
3271     do {
3272         if (indexFilePropArray+1 >= sizeFilePropArray)
3273         {
3274             LPFILEPROPERTIESW tmpafp;
3275             
3276             sizeFilePropArray *= 2;
3277             tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3278                 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3279             if (NULL == tmpafp)
3280             {
3281                 bSuccess = FALSE;
3282                 break;
3283             }
3284
3285             *lpafp = tmpafp;
3286         }
3287         indexFilePropArray++;
3288     } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3289
3290     if (bSuccess && indexFilePropArray)
3291     {
3292         if (indexFilePropArray < sizeFilePropArray - 1)
3293         {
3294             LPFILEPROPERTIESW tmpafp;
3295
3296             tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3297                 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3298             if (NULL == tmpafp)
3299                 *lpafp = tmpafp;
3300         }
3301         *dwfp = indexFilePropArray;
3302     }
3303     else
3304     {
3305         HeapFree(GetProcessHeap(), 0, *lpafp);
3306         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3307         bSuccess = FALSE;
3308     }
3309
3310     return bSuccess;
3311 }
3312
3313
3314 /***********************************************************************
3315  *           FTP_ParsePermission (internal)
3316  *
3317  * Parse permission string of directory information
3318  *
3319  * RETURNS
3320  *   TRUE on success
3321  *   FALSE on failure
3322  *
3323  */
3324 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3325 {
3326     BOOL bSuccess = TRUE;
3327     unsigned short nPermission = 0;
3328     INT nPos = 1;
3329     INT nLast  = 9;
3330
3331     TRACE("\n");
3332     if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3333     {
3334         bSuccess = FALSE;
3335         return bSuccess;
3336     }
3337
3338     lpfp->bIsDirectory = (*lpszPermission == 'd');
3339     do
3340     {
3341         switch (nPos)
3342         {
3343             case 1:
3344                 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3345                 break;
3346             case 2:
3347                 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3348                 break;
3349             case 3:
3350                 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3351                 break;
3352             case 4:
3353                 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3354                 break;
3355             case 5:
3356                 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3357                 break;
3358             case 6:
3359                 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3360                 break;
3361             case 7:
3362                 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3363                 break;
3364             case 8:
3365                 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3366                 break;
3367             case 9:
3368                 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3369                 break;
3370         }
3371         nPos++;
3372     }while (nPos <= nLast);
3373
3374     lpfp->permissions = nPermission;
3375     return bSuccess;
3376 }
3377
3378
3379 /***********************************************************************
3380  *           FTP_SetResponseError (internal)
3381  *
3382  * Set the appropriate error code for a given response from the server
3383  *
3384  * RETURNS
3385  *
3386  */
3387 static DWORD FTP_SetResponseError(DWORD dwResponse)
3388 {
3389     DWORD dwCode = 0;
3390
3391     switch(dwResponse)
3392     {
3393         case 421: /* Service not available - Server may be shutting down. */
3394             dwCode = ERROR_INTERNET_TIMEOUT;
3395             break;
3396
3397         case 425: /* Cannot open data connection. */
3398             dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3399             break;
3400
3401         case 426: /* Connection closed, transer aborted. */
3402             dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3403             break;
3404
3405         case 500: /* Syntax error. Command unrecognized. */
3406         case 501: /* Syntax error. Error in parameters or arguments. */
3407             dwCode = ERROR_INTERNET_INCORRECT_FORMAT;
3408             break;
3409
3410         case 530: /* Not logged in. Login incorrect. */
3411             dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3412             break;
3413
3414         case 550: /* File action not taken. File not found or no access. */
3415             dwCode = ERROR_INTERNET_ITEM_NOT_FOUND;
3416             break;
3417
3418         case 450: /* File action not taken. File may be busy. */
3419         case 451: /* Action aborted. Server error. */
3420         case 452: /* Action not taken. Insufficient storage space on server. */
3421         case 502: /* Command not implemented. */
3422         case 503: /* Bad sequence of commands. */
3423         case 504: /* Command not implemented for that parameter. */
3424         case 532: /* Need account for storing files */
3425         case 551: /* Requested action aborted. Page type unknown */
3426         case 552: /* Action aborted. Exceeded storage allocation */
3427         case 553: /* Action not taken. File name not allowed. */
3428
3429         default:
3430             dwCode = ERROR_INTERNET_INTERNAL_ERROR;
3431             break;
3432     }
3433
3434     INTERNET_SetLastError(dwCode);
3435     return dwCode;
3436 }