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