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