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