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