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