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