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