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