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