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