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