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