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