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