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