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