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