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