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