msvcrt: Added basic _popen tests.
[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_ReadFileEx(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_size,
1205     DWORD flags, DWORD_PTR context)
1206 {
1207     return FTPFILE_ReadFile(hdr, buf, size, ret_size);
1208 }
1209
1210 static DWORD FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1211 {
1212     ftp_file_t *lpwh = (ftp_file_t*) hdr;
1213     int res;
1214
1215     res = send(lpwh->nDataSocket, buffer, size, 0);
1216
1217     *written = res>0 ? res : 0;
1218     return res >= 0 ? ERROR_SUCCESS : sock_get_error(errno);
1219 }
1220
1221 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
1222 {
1223     INTERNET_ASYNC_RESULT iar;
1224     BYTE buffer[4096];
1225     int available;
1226
1227     TRACE("%p\n", file);
1228
1229     available = recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1230
1231     if(available != -1) {
1232         iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1233         iar.dwError = first_notif ? 0 : available;
1234     }else {
1235         iar.dwResult = 0;
1236         iar.dwError = INTERNET_GetLastError();
1237     }
1238
1239     INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1240                           sizeof(INTERNET_ASYNC_RESULT));
1241 }
1242
1243 static void FTPFILE_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
1244 {
1245     ftp_file_t *file = (ftp_file_t*)workRequest->hdr;
1246
1247     FTP_ReceiveRequestData(file, FALSE);
1248 }
1249
1250 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1251 {
1252     ftp_file_t *file = (ftp_file_t*) hdr;
1253     int retval, unread = 0;
1254
1255     TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
1256
1257 #ifdef FIONREAD
1258     retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
1259     if (!retval)
1260         TRACE("%d bytes of queued, but unread data\n", unread);
1261 #else
1262     FIXME("FIONREAD not available\n");
1263 #endif
1264
1265     *available = unread;
1266
1267     if(!unread) {
1268         BYTE byte;
1269
1270         *available = 0;
1271
1272         retval = recv(file->nDataSocket, &byte, 1, MSG_PEEK);
1273         if(retval > 0) {
1274             WORKREQUEST workRequest;
1275
1276             *available = 0;
1277             workRequest.asyncproc = FTPFILE_AsyncQueryDataAvailableProc;
1278             workRequest.hdr = WININET_AddRef( &file->hdr );
1279
1280             INTERNET_AsyncCall(&workRequest);
1281
1282             return ERROR_IO_PENDING;
1283         }
1284     }
1285
1286     return ERROR_SUCCESS;
1287 }
1288
1289
1290 static const object_vtbl_t FTPFILEVtbl = {
1291     FTPFILE_Destroy,
1292     NULL,
1293     FTPFILE_QueryOption,
1294     INET_SetOption,
1295     FTPFILE_ReadFile,
1296     FTPFILE_ReadFileEx,
1297     FTPFILE_WriteFile,
1298     FTPFILE_QueryDataAvailable,
1299     NULL
1300 };
1301
1302 /***********************************************************************
1303  *           FTP_FtpOpenFileW (Internal)
1304  *
1305  * Open a remote file for writing or reading
1306  *
1307  * RETURNS
1308  *    HINTERNET handle on success
1309  *    NULL on failure
1310  *
1311  */
1312 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1313         LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1314         DWORD_PTR dwContext)
1315 {
1316     INT nDataSocket;
1317     BOOL bSuccess = FALSE;
1318     ftp_file_t *lpwh = NULL;
1319     appinfo_t *hIC = NULL;
1320
1321     TRACE("\n");
1322
1323     /* Clear any error information */
1324     INTERNET_SetLastError(0);
1325
1326     if (GENERIC_READ == fdwAccess)
1327     {
1328         /* Set up socket to retrieve data */
1329         bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1330     }
1331     else if (GENERIC_WRITE == fdwAccess)
1332     {
1333         /* Set up socket to send data */
1334         bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1335     }
1336
1337     /* Get data socket to server */
1338     if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1339     {
1340         lpwh = alloc_object(&lpwfs->hdr, &FTPFILEVtbl, sizeof(ftp_file_t));
1341         lpwh->hdr.htype = WH_HFILE;
1342         lpwh->hdr.dwFlags = dwFlags;
1343         lpwh->hdr.dwContext = dwContext;
1344         lpwh->nDataSocket = nDataSocket;
1345         lpwh->cache_file = NULL;
1346         lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1347         lpwh->session_deleted = FALSE;
1348
1349         WININET_AddRef( &lpwfs->hdr );
1350         lpwh->lpFtpSession = lpwfs;
1351         list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1352         
1353         /* Indicate that a download is currently in progress */
1354         lpwfs->download_in_progress = lpwh;
1355     }
1356
1357     if (lpwfs->lstnSocket != -1)
1358     {
1359         closesocket(lpwfs->lstnSocket);
1360         lpwfs->lstnSocket = -1;
1361     }
1362
1363     if (bSuccess && fdwAccess == GENERIC_READ)
1364     {
1365         WCHAR filename[MAX_PATH + 1];
1366         URL_COMPONENTSW uc;
1367         DWORD len;
1368
1369         memset(&uc, 0, sizeof(uc));
1370         uc.dwStructSize = sizeof(uc);
1371         uc.nScheme      = INTERNET_SCHEME_FTP;
1372         uc.lpszHostName = lpwfs->servername;
1373         uc.nPort        = lpwfs->serverport;
1374         uc.lpszUserName = lpwfs->lpszUserName;
1375         uc.lpszUrlPath  = heap_strdupW(lpszFileName);
1376
1377         if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1378         {
1379             WCHAR *url = heap_alloc(len * sizeof(WCHAR));
1380
1381             if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1382             {
1383                 lpwh->cache_file = heap_strdupW(filename);
1384                 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1385                                                       NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1386                 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1387                 {
1388                     WARN("Could not create cache file: %u\n", GetLastError());
1389                     heap_free(lpwh->cache_file);
1390                     lpwh->cache_file = NULL;
1391                 }
1392             }
1393             heap_free(url);
1394         }
1395         heap_free(uc.lpszUrlPath);
1396     }
1397
1398     hIC = lpwfs->lpAppInfo;
1399     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1400     {
1401         INTERNET_ASYNC_RESULT iar;
1402
1403         if (lpwh)
1404         {
1405             iar.dwResult = (DWORD_PTR)lpwh->hdr.hInternet;
1406             iar.dwError = ERROR_SUCCESS;
1407             SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1408                 &iar, sizeof(INTERNET_ASYNC_RESULT));
1409         }
1410
1411         if(bSuccess) {
1412             FTP_ReceiveRequestData(lpwh, TRUE);
1413         }else {
1414             iar.dwResult = 0;
1415             iar.dwError = INTERNET_GetLastError();
1416             SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1417                     &iar, sizeof(INTERNET_ASYNC_RESULT));
1418         }
1419     }
1420
1421     if(!bSuccess)
1422         return FALSE;
1423
1424     return lpwh->hdr.hInternet;
1425 }
1426
1427
1428 /***********************************************************************
1429  *           FtpOpenFileA (WININET.@)
1430  *
1431  * Open a remote file for writing or reading
1432  *
1433  * RETURNS
1434  *    HINTERNET handle on success
1435  *    NULL on failure
1436  *
1437  */
1438 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1439     LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1440     DWORD_PTR dwContext)
1441 {
1442     LPWSTR lpwzFileName;
1443     HINTERNET ret;
1444
1445     lpwzFileName = heap_strdupAtoW(lpszFileName);
1446     ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1447     heap_free(lpwzFileName);
1448     return ret;
1449 }
1450
1451
1452 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1453 {
1454     struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1455     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1456
1457     TRACE("%p\n", lpwfs);
1458
1459     FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1460         req->dwAccess, req->dwFlags, req->dwContext);
1461     heap_free(req->lpszFilename);
1462 }
1463
1464 /***********************************************************************
1465  *           FtpOpenFileW (WININET.@)
1466  *
1467  * Open a remote file for writing or reading
1468  *
1469  * RETURNS
1470  *    HINTERNET handle on success
1471  *    NULL on failure
1472  *
1473  */
1474 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1475     LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1476     DWORD_PTR dwContext)
1477 {
1478     ftp_session_t *lpwfs;
1479     appinfo_t *hIC = NULL;
1480     HINTERNET r = NULL;
1481
1482     TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1483         debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1484
1485     lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1486     if (!lpwfs)
1487     {
1488         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1489         return FALSE;
1490     }
1491
1492     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1493     {
1494         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1495         goto lend;
1496     }
1497
1498     if ((!lpszFileName) ||
1499         ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1500         ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1501     {
1502         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1503         goto lend;
1504     }
1505
1506     if (lpwfs->download_in_progress != NULL)
1507     {
1508         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1509         goto lend;
1510     }
1511
1512     hIC = lpwfs->lpAppInfo;
1513     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1514     {
1515         WORKREQUEST workRequest;
1516         struct WORKREQ_FTPOPENFILEW *req;
1517
1518         workRequest.asyncproc = AsyncFtpOpenFileProc;
1519         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1520         req = &workRequest.u.FtpOpenFileW;
1521         req->lpszFilename = heap_strdupW(lpszFileName);
1522         req->dwAccess = fdwAccess;
1523         req->dwFlags = dwFlags;
1524         req->dwContext = dwContext;
1525
1526         INTERNET_AsyncCall(&workRequest);
1527         r = NULL;
1528     }
1529     else
1530     {
1531         r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1532     }
1533
1534 lend:
1535     WININET_Release( &lpwfs->hdr );
1536
1537     return r;
1538 }
1539
1540
1541 /***********************************************************************
1542  *           FtpGetFileA (WININET.@)
1543  *
1544  * Retrieve file from the FTP server
1545  *
1546  * RETURNS
1547  *    TRUE on success
1548  *    FALSE on failure
1549  *
1550  */
1551 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1552     BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1553     DWORD_PTR dwContext)
1554 {
1555     LPWSTR lpwzRemoteFile;
1556     LPWSTR lpwzNewFile;
1557     BOOL ret;
1558     
1559     lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1560     lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1561     ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1562         dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1563     heap_free(lpwzRemoteFile);
1564     heap_free(lpwzNewFile);
1565     return ret;
1566 }
1567
1568
1569 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1570 {
1571     struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1572     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1573
1574     TRACE("%p\n", lpwfs);
1575
1576     FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1577              req->lpszNewFile, req->fFailIfExists,
1578              req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1579     heap_free(req->lpszRemoteFile);
1580     heap_free(req->lpszNewFile);
1581 }
1582
1583
1584 /***********************************************************************
1585  *           FtpGetFileW (WININET.@)
1586  *
1587  * Retrieve file from the FTP server
1588  *
1589  * RETURNS
1590  *    TRUE on success
1591  *    FALSE on failure
1592  *
1593  */
1594 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1595     BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1596     DWORD_PTR dwContext)
1597 {
1598     ftp_session_t *lpwfs;
1599     appinfo_t *hIC = NULL;
1600     BOOL r = FALSE;
1601
1602     if (!lpszRemoteFile || !lpszNewFile)
1603     {
1604         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1605         return FALSE;
1606     }
1607
1608     lpwfs = (ftp_session_t*) get_handle_object( hInternet );
1609     if (!lpwfs)
1610     {
1611         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1612         return FALSE;
1613     }
1614
1615     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1616     {
1617         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1618         goto lend;
1619     }
1620
1621     if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1622     {
1623         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1624         goto lend;
1625     }
1626
1627     if (lpwfs->download_in_progress != NULL)
1628     {
1629         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1630         goto lend;
1631     }
1632     
1633     hIC = lpwfs->lpAppInfo;
1634     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1635     {
1636         WORKREQUEST workRequest;
1637         struct WORKREQ_FTPGETFILEW *req;
1638
1639         workRequest.asyncproc = AsyncFtpGetFileProc;
1640         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1641         req = &workRequest.u.FtpGetFileW;
1642         req->lpszRemoteFile = heap_strdupW(lpszRemoteFile);
1643         req->lpszNewFile = heap_strdupW(lpszNewFile);
1644         req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1645         req->fFailIfExists = fFailIfExists;
1646         req->dwFlags = dwInternetFlags;
1647         req->dwContext = dwContext;
1648
1649         r = res_to_le(INTERNET_AsyncCall(&workRequest));
1650     }
1651     else
1652     {
1653         r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1654            fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1655     }
1656
1657 lend:
1658     WININET_Release( &lpwfs->hdr );
1659
1660     return r;
1661 }
1662
1663
1664 /***********************************************************************
1665  *           FTP_FtpGetFileW (Internal)
1666  *
1667  * Retrieve file from the FTP server
1668  *
1669  * RETURNS
1670  *    TRUE on success
1671  *    FALSE on failure
1672  *
1673  */
1674 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1675         BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1676         DWORD_PTR dwContext)
1677 {
1678     BOOL bSuccess = FALSE;
1679     HANDLE hFile;
1680     appinfo_t *hIC = NULL;
1681
1682     TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1683
1684     /* Clear any error information */
1685     INTERNET_SetLastError(0);
1686
1687     /* Ensure we can write to lpszNewfile by opening it */
1688     hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1689         CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1690     if (INVALID_HANDLE_VALUE == hFile)
1691         return FALSE;
1692
1693     /* Set up socket to retrieve data */
1694     if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1695     {
1696         INT nDataSocket;
1697
1698         /* Get data socket to server */
1699         if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1700         {
1701             INT nResCode;
1702
1703             /* Receive data */
1704             FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1705             closesocket(nDataSocket);
1706
1707             nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1708             if (nResCode)
1709             {
1710                 if (nResCode == 226)
1711                     bSuccess = TRUE;
1712                 else
1713                     FTP_SetResponseError(nResCode);
1714             }
1715         }
1716     }
1717
1718     if (lpwfs->lstnSocket != -1)
1719     {
1720         closesocket(lpwfs->lstnSocket);
1721         lpwfs->lstnSocket = -1;
1722     }
1723
1724     CloseHandle(hFile);
1725
1726     hIC = lpwfs->lpAppInfo;
1727     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1728     {
1729         INTERNET_ASYNC_RESULT iar;
1730
1731         iar.dwResult = (DWORD)bSuccess;
1732         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1733         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1734             &iar, sizeof(INTERNET_ASYNC_RESULT));
1735     }
1736
1737     if (!bSuccess) DeleteFileW(lpszNewFile);
1738     return bSuccess;
1739 }
1740
1741 /***********************************************************************
1742  *           FtpGetFileSize  (WININET.@)
1743  */
1744 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1745 {
1746     FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1747
1748     if (lpdwFileSizeHigh)
1749         *lpdwFileSizeHigh = 0;
1750
1751     return 0;
1752 }
1753
1754 /***********************************************************************
1755  *           FtpDeleteFileA  (WININET.@)
1756  *
1757  * Delete a file on the ftp server
1758  *
1759  * RETURNS
1760  *    TRUE on success
1761  *    FALSE on failure
1762  *
1763  */
1764 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1765 {
1766     LPWSTR lpwzFileName;
1767     BOOL ret;
1768     
1769     lpwzFileName = heap_strdupAtoW(lpszFileName);
1770     ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1771     heap_free(lpwzFileName);
1772     return ret;
1773 }
1774
1775 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1776 {
1777     struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1778     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1779
1780     TRACE("%p\n", lpwfs);
1781
1782     FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1783     heap_free(req->lpszFilename);
1784 }
1785
1786 /***********************************************************************
1787  *           FtpDeleteFileW  (WININET.@)
1788  *
1789  * Delete a file on the ftp server
1790  *
1791  * RETURNS
1792  *    TRUE on success
1793  *    FALSE on failure
1794  *
1795  */
1796 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1797 {
1798     ftp_session_t *lpwfs;
1799     appinfo_t *hIC = NULL;
1800     BOOL r = FALSE;
1801
1802     lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1803     if (!lpwfs)
1804     {
1805         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1806         return FALSE;
1807     }
1808
1809     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1810     {
1811         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1812         goto lend;
1813     }
1814
1815     if (lpwfs->download_in_progress != NULL)
1816     {
1817         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1818         goto lend;
1819     }
1820
1821     if (!lpszFileName)
1822     {
1823         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1824         goto lend;
1825     }
1826
1827     hIC = lpwfs->lpAppInfo;
1828     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1829     {
1830         WORKREQUEST workRequest;
1831         struct WORKREQ_FTPDELETEFILEW *req;
1832
1833         workRequest.asyncproc = AsyncFtpDeleteFileProc;
1834         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1835         req = &workRequest.u.FtpDeleteFileW;
1836         req->lpszFilename = heap_strdupW(lpszFileName);
1837
1838         r = res_to_le(INTERNET_AsyncCall(&workRequest));
1839     }
1840     else
1841     {
1842         r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1843     }
1844
1845 lend:
1846     WININET_Release( &lpwfs->hdr );
1847
1848     return r;
1849 }
1850
1851 /***********************************************************************
1852  *           FTP_FtpDeleteFileW  (Internal)
1853  *
1854  * Delete a file on the ftp server
1855  *
1856  * RETURNS
1857  *    TRUE on success
1858  *    FALSE on failure
1859  *
1860  */
1861 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1862 {
1863     INT nResCode;
1864     BOOL bSuccess = FALSE;
1865     appinfo_t *hIC = NULL;
1866
1867     TRACE("%p\n", lpwfs);
1868
1869     /* Clear any error information */
1870     INTERNET_SetLastError(0);
1871
1872     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1873         goto lend;
1874
1875     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1876     if (nResCode)
1877     {
1878         if (nResCode == 250)
1879             bSuccess = TRUE;
1880         else
1881             FTP_SetResponseError(nResCode);
1882     }
1883 lend:
1884     hIC = lpwfs->lpAppInfo;
1885     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1886     {
1887         INTERNET_ASYNC_RESULT iar;
1888
1889         iar.dwResult = (DWORD)bSuccess;
1890         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1891         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1892             &iar, sizeof(INTERNET_ASYNC_RESULT));
1893     }
1894
1895     return bSuccess;
1896 }
1897
1898
1899 /***********************************************************************
1900  *           FtpRemoveDirectoryA  (WININET.@)
1901  *
1902  * Remove a directory on the ftp server
1903  *
1904  * RETURNS
1905  *    TRUE on success
1906  *    FALSE on failure
1907  *
1908  */
1909 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1910 {
1911     LPWSTR lpwzDirectory;
1912     BOOL ret;
1913     
1914     lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1915     ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1916     heap_free(lpwzDirectory);
1917     return ret;
1918 }
1919
1920 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1921 {
1922     struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1923     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1924
1925     TRACE("%p\n", lpwfs);
1926
1927     FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1928     heap_free(req->lpszDirectory);
1929 }
1930
1931 /***********************************************************************
1932  *           FtpRemoveDirectoryW  (WININET.@)
1933  *
1934  * Remove a directory on the ftp server
1935  *
1936  * RETURNS
1937  *    TRUE on success
1938  *    FALSE on failure
1939  *
1940  */
1941 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1942 {
1943     ftp_session_t *lpwfs;
1944     appinfo_t *hIC = NULL;
1945     BOOL r = FALSE;
1946
1947     lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1948     if (!lpwfs)
1949     {
1950         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1951         return FALSE;
1952     }
1953
1954     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1955     {
1956         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1957         goto lend;
1958     }
1959
1960     if (lpwfs->download_in_progress != NULL)
1961     {
1962         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1963         goto lend;
1964     }
1965
1966     if (!lpszDirectory)
1967     {
1968         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1969         goto lend;
1970     }
1971
1972     hIC = lpwfs->lpAppInfo;
1973     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1974     {
1975         WORKREQUEST workRequest;
1976         struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1977
1978         workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1979         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1980         req = &workRequest.u.FtpRemoveDirectoryW;
1981         req->lpszDirectory = heap_strdupW(lpszDirectory);
1982
1983         r = res_to_le(INTERNET_AsyncCall(&workRequest));
1984     }
1985     else
1986     {
1987         r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1988     }
1989
1990 lend:
1991     WININET_Release( &lpwfs->hdr );
1992
1993     return r;
1994 }
1995
1996 /***********************************************************************
1997  *           FTP_FtpRemoveDirectoryW  (Internal)
1998  *
1999  * Remove a directory on the ftp server
2000  *
2001  * RETURNS
2002  *    TRUE on success
2003  *    FALSE on failure
2004  *
2005  */
2006 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
2007 {
2008     INT nResCode;
2009     BOOL bSuccess = FALSE;
2010     appinfo_t *hIC = NULL;
2011
2012     TRACE("\n");
2013
2014     /* Clear any error information */
2015     INTERNET_SetLastError(0);
2016
2017     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2018         goto lend;
2019
2020     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2021     if (nResCode)
2022     {
2023         if (nResCode == 250)
2024             bSuccess = TRUE;
2025         else
2026             FTP_SetResponseError(nResCode);
2027     }
2028
2029 lend:
2030     hIC = lpwfs->lpAppInfo;
2031     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2032     {
2033         INTERNET_ASYNC_RESULT iar;
2034
2035         iar.dwResult = (DWORD)bSuccess;
2036         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2037         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2038             &iar, sizeof(INTERNET_ASYNC_RESULT));
2039     }
2040
2041     return bSuccess;
2042 }
2043
2044
2045 /***********************************************************************
2046  *           FtpRenameFileA  (WININET.@)
2047  *
2048  * Rename a file on the ftp server
2049  *
2050  * RETURNS
2051  *    TRUE on success
2052  *    FALSE on failure
2053  *
2054  */
2055 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2056 {
2057     LPWSTR lpwzSrc;
2058     LPWSTR lpwzDest;
2059     BOOL ret;
2060     
2061     lpwzSrc = heap_strdupAtoW(lpszSrc);
2062     lpwzDest = heap_strdupAtoW(lpszDest);
2063     ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2064     heap_free(lpwzSrc);
2065     heap_free(lpwzDest);
2066     return ret;
2067 }
2068
2069 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
2070 {
2071     struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
2072     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
2073
2074     TRACE("%p\n", lpwfs);
2075
2076     FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
2077     heap_free(req->lpszSrcFile);
2078     heap_free(req->lpszDestFile);
2079 }
2080
2081 /***********************************************************************
2082  *           FtpRenameFileW  (WININET.@)
2083  *
2084  * Rename a file on the ftp server
2085  *
2086  * RETURNS
2087  *    TRUE on success
2088  *    FALSE on failure
2089  *
2090  */
2091 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2092 {
2093     ftp_session_t *lpwfs;
2094     appinfo_t *hIC = NULL;
2095     BOOL r = FALSE;
2096
2097     lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
2098     if (!lpwfs)
2099     {
2100         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2101         return FALSE;
2102     }
2103
2104     if (WH_HFTPSESSION != lpwfs->hdr.htype)
2105     {
2106         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2107         goto lend;
2108     }
2109
2110     if (lpwfs->download_in_progress != NULL)
2111     {
2112         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2113         goto lend;
2114     }
2115
2116     if (!lpszSrc || !lpszDest)
2117     {
2118         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2119         goto lend;
2120     }
2121
2122     hIC = lpwfs->lpAppInfo;
2123     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2124     {
2125         WORKREQUEST workRequest;
2126         struct WORKREQ_FTPRENAMEFILEW *req;
2127
2128         workRequest.asyncproc = AsyncFtpRenameFileProc;
2129         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
2130         req = &workRequest.u.FtpRenameFileW;
2131         req->lpszSrcFile = heap_strdupW(lpszSrc);
2132         req->lpszDestFile = heap_strdupW(lpszDest);
2133
2134         r = res_to_le(INTERNET_AsyncCall(&workRequest));
2135     }
2136     else
2137     {
2138         r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2139     }
2140
2141 lend:
2142     WININET_Release( &lpwfs->hdr );
2143
2144     return r;
2145 }
2146
2147 /***********************************************************************
2148  *           FTP_FtpRenameFileW  (Internal)
2149  *
2150  * Rename a file on the ftp server
2151  *
2152  * RETURNS
2153  *    TRUE on success
2154  *    FALSE on failure
2155  *
2156  */
2157 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2158 {
2159     INT nResCode;
2160     BOOL bSuccess = FALSE;
2161     appinfo_t *hIC = NULL;
2162
2163     TRACE("\n");
2164
2165     /* Clear any error information */
2166     INTERNET_SetLastError(0);
2167
2168     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2169         goto lend;
2170
2171     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2172     if (nResCode == 350)
2173     {
2174         if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2175             goto lend;
2176
2177         nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2178     }
2179
2180     if (nResCode == 250)
2181         bSuccess = TRUE;
2182     else
2183         FTP_SetResponseError(nResCode);
2184
2185 lend:
2186     hIC = lpwfs->lpAppInfo;
2187     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2188     {
2189         INTERNET_ASYNC_RESULT iar;
2190
2191         iar.dwResult = (DWORD)bSuccess;
2192         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2193         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2194             &iar, sizeof(INTERNET_ASYNC_RESULT));
2195     }
2196
2197     return bSuccess;
2198 }
2199
2200 /***********************************************************************
2201  *           FtpCommandA  (WININET.@)
2202  */
2203 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2204                          LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2205 {
2206     BOOL r;
2207     WCHAR *cmdW;
2208
2209     TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2210           debugstr_a(lpszCommand), dwContext, phFtpCommand);
2211
2212     if (fExpectResponse)
2213     {
2214         FIXME("data connection not supported\n");
2215         return FALSE;
2216     }
2217
2218     if (!lpszCommand || !lpszCommand[0])
2219     {
2220         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2221         return FALSE;
2222     }
2223
2224     if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2225     {
2226         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2227         return FALSE;
2228     }
2229
2230     r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2231
2232     heap_free(cmdW);
2233     return r;
2234 }
2235
2236 /***********************************************************************
2237  *           FtpCommandW  (WININET.@)
2238  */
2239 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2240                          LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2241 {
2242     BOOL r = FALSE;
2243     ftp_session_t *lpwfs;
2244     LPSTR cmd = NULL;
2245     DWORD len, nBytesSent= 0;
2246     INT nResCode, nRC = 0;
2247
2248     TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2249            debugstr_w(lpszCommand), dwContext, phFtpCommand);
2250
2251     if (!lpszCommand || !lpszCommand[0])
2252     {
2253         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2254         return FALSE;
2255     }
2256
2257     if (fExpectResponse)
2258     {
2259         FIXME("data connection not supported\n");
2260         return FALSE;
2261     }
2262
2263     lpwfs = (ftp_session_t*) get_handle_object( hConnect );
2264     if (!lpwfs)
2265     {
2266         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2267         return FALSE;
2268     }
2269
2270     if (WH_HFTPSESSION != lpwfs->hdr.htype)
2271     {
2272         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2273         goto lend;
2274     }
2275
2276     if (lpwfs->download_in_progress != NULL)
2277     {
2278         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2279         goto lend;
2280     }
2281
2282     len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2283     if ((cmd = heap_alloc(len)))
2284         WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2285     else
2286     {
2287         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2288         goto lend;
2289     }
2290
2291     strcat(cmd, szCRLF);
2292     len--;
2293
2294     TRACE("Sending (%s) len(%d)\n", cmd, len);
2295     while ((nBytesSent < len) && (nRC != -1))
2296     {
2297         nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2298         if (nRC != -1)
2299         {
2300             nBytesSent += nRC;
2301             TRACE("Sent %d bytes\n", nRC);
2302         }
2303     }
2304
2305     if (nBytesSent)
2306     {
2307         nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2308         if (nResCode > 0 && nResCode < 400)
2309             r = TRUE;
2310         else
2311             FTP_SetResponseError(nResCode);
2312     }
2313
2314 lend:
2315     WININET_Release( &lpwfs->hdr );
2316     heap_free( cmd );
2317     return r;
2318 }
2319
2320
2321 /***********************************************************************
2322  *           FTPSESSION_Destroy (internal)
2323  *
2324  * Deallocate session handle
2325  */
2326 static void FTPSESSION_Destroy(object_header_t *hdr)
2327 {
2328     ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2329
2330     TRACE("\n");
2331
2332     WININET_Release(&lpwfs->lpAppInfo->hdr);
2333
2334     heap_free(lpwfs->lpszPassword);
2335     heap_free(lpwfs->lpszUserName);
2336     heap_free(lpwfs->servername);
2337 }
2338
2339 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2340 {
2341     ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2342
2343     TRACE("\n");
2344
2345     SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2346                       INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2347
2348     if (lpwfs->download_in_progress != NULL)
2349         lpwfs->download_in_progress->session_deleted = TRUE;
2350
2351      if (lpwfs->sndSocket != -1)
2352          closesocket(lpwfs->sndSocket);
2353
2354      if (lpwfs->lstnSocket != -1)
2355          closesocket(lpwfs->lstnSocket);
2356
2357     if (lpwfs->pasvSocket != -1)
2358         closesocket(lpwfs->pasvSocket);
2359
2360     SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2361                       INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2362 }
2363
2364 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2365 {
2366     switch(option) {
2367     case INTERNET_OPTION_HANDLE_TYPE:
2368         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2369
2370         if (*size < sizeof(ULONG))
2371             return ERROR_INSUFFICIENT_BUFFER;
2372
2373         *size = sizeof(DWORD);
2374         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2375         return ERROR_SUCCESS;
2376     }
2377
2378     return INET_QueryOption(hdr, option, buffer, size, unicode);
2379 }
2380
2381 static const object_vtbl_t FTPSESSIONVtbl = {
2382     FTPSESSION_Destroy,
2383     FTPSESSION_CloseConnection,
2384     FTPSESSION_QueryOption,
2385     INET_SetOption,
2386     NULL,
2387     NULL,
2388     NULL,
2389     NULL,
2390     NULL
2391 };
2392
2393
2394 /***********************************************************************
2395  *           FTP_Connect (internal)
2396  *
2397  * Connect to a ftp server
2398  *
2399  * RETURNS
2400  *   HINTERNET a session handle on success
2401  *   NULL on failure
2402  *
2403  * NOTES:
2404  *
2405  * Windows uses 'anonymous' as the username, when given a NULL username
2406  * and a NULL password. The password is first looked up in:
2407  *
2408  * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2409  *
2410  * If this entry is not present it uses the current username as the password.
2411  *
2412  */
2413
2414 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2415         INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2416         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2417         DWORD dwInternalFlags)
2418 {
2419     static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2420                                    'M','i','c','r','o','s','o','f','t','\\',
2421                                    'W','i','n','d','o','w','s','\\',
2422                                    'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2423                                    'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2424     static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2425     static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2426     static const WCHAR szEmpty[] = {'\0'};
2427     struct sockaddr_in socketAddr;
2428     INT nsocket = -1;
2429     socklen_t sock_namelen;
2430     BOOL bSuccess = FALSE;
2431     ftp_session_t *lpwfs = NULL;
2432     char szaddr[INET_ADDRSTRLEN];
2433
2434     TRACE("%p  Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2435             hIC, debugstr_w(lpszServerName),
2436             nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2437
2438     assert( hIC->hdr.htype == WH_HINIT );
2439
2440     if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2441     {
2442         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2443         return NULL;
2444     }
2445     
2446     lpwfs = alloc_object(&hIC->hdr, &FTPSESSIONVtbl, sizeof(ftp_session_t));
2447     if (NULL == lpwfs)
2448     {
2449         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2450         return NULL;
2451     }
2452
2453     if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2454         lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2455     else
2456         lpwfs->serverport = nServerPort;
2457
2458     lpwfs->hdr.htype = WH_HFTPSESSION;
2459     lpwfs->hdr.dwFlags = dwFlags;
2460     lpwfs->hdr.dwContext = dwContext;
2461     lpwfs->hdr.dwInternalFlags |= dwInternalFlags;
2462     lpwfs->download_in_progress = NULL;
2463     lpwfs->sndSocket = -1;
2464     lpwfs->lstnSocket = -1;
2465     lpwfs->pasvSocket = -1;
2466
2467     WININET_AddRef( &hIC->hdr );
2468     lpwfs->lpAppInfo = hIC;
2469     list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2470
2471     if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
2472         if(strchrW(hIC->proxy, ' '))
2473             FIXME("Several proxies not implemented.\n");
2474         if(hIC->proxyBypass)
2475             FIXME("Proxy bypass is ignored.\n");
2476     }
2477     if (!lpszUserName || !strlenW(lpszUserName)) {
2478         HKEY key;
2479         WCHAR szPassword[MAX_PATH];
2480         DWORD len = sizeof(szPassword);
2481
2482         lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2483
2484         RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2485         if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2486             /* Nothing in the registry, get the username and use that as the password */
2487             if (!GetUserNameW(szPassword, &len)) {
2488                 /* Should never get here, but use an empty password as failsafe */
2489                 strcpyW(szPassword, szEmpty);
2490             }
2491         }
2492         RegCloseKey(key);
2493
2494         TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2495         lpwfs->lpszPassword = heap_strdupW(szPassword);
2496     }
2497     else {
2498         lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2499         lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2500     }
2501     lpwfs->servername = heap_strdupW(lpszServerName);
2502     
2503     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2504     if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2505     {
2506         INTERNET_ASYNC_RESULT iar;
2507
2508         iar.dwResult = (DWORD_PTR)lpwfs->hdr.hInternet;
2509         iar.dwError = ERROR_SUCCESS;
2510
2511         SendAsyncCallback(&hIC->hdr, dwContext,
2512                       INTERNET_STATUS_HANDLE_CREATED, &iar,
2513                       sizeof(INTERNET_ASYNC_RESULT));
2514     }
2515         
2516     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2517         (LPWSTR) lpszServerName, (strlenW(lpszServerName)+1) * sizeof(WCHAR));
2518
2519     sock_namelen = sizeof(socketAddr);
2520     if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen))
2521     {
2522         INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2523         goto lerror;
2524     }
2525
2526     if (socketAddr.sin_family != AF_INET)
2527     {
2528         WARN("unsupported address family %d\n", socketAddr.sin_family);
2529         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2530         goto lerror;
2531     }
2532
2533     inet_ntop(socketAddr.sin_family, &socketAddr.sin_addr, szaddr, sizeof(szaddr));
2534     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2535                       szaddr, strlen(szaddr)+1);
2536
2537     nsocket = socket(AF_INET,SOCK_STREAM,0);
2538     if (nsocket == -1)
2539     {
2540         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2541         goto lerror;
2542     }
2543
2544     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2545                       szaddr, strlen(szaddr)+1);
2546
2547     if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2548     {
2549         ERR("Unable to connect (%s)\n", strerror(errno));
2550         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2551         closesocket(nsocket);
2552     }
2553     else
2554     {
2555         TRACE("Connected to server\n");
2556         lpwfs->sndSocket = nsocket;
2557         SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2558                           szaddr, strlen(szaddr)+1);
2559
2560         sock_namelen = sizeof(lpwfs->socketAddress);
2561         getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2562
2563         if (FTP_ConnectToHost(lpwfs))
2564         {
2565             TRACE("Successfully logged into server\n");
2566             bSuccess = TRUE;
2567         }
2568     }
2569
2570 lerror:
2571     if (!bSuccess)
2572     {
2573         if(lpwfs)
2574             WININET_Release( &lpwfs->hdr );
2575         return NULL;
2576     }
2577
2578     return lpwfs->hdr.hInternet;
2579 }
2580
2581
2582 /***********************************************************************
2583  *           FTP_ConnectToHost (internal)
2584  *
2585  * Connect to a ftp server
2586  *
2587  * RETURNS
2588  *   TRUE on success
2589  *   NULL on failure
2590  *
2591  */
2592 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2593 {
2594     INT nResCode;
2595     BOOL bSuccess = FALSE;
2596
2597     TRACE("\n");
2598     FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2599
2600     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2601         goto lend;
2602
2603     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2604     if (nResCode)
2605     {
2606         /* Login successful... */
2607         if (nResCode == 230)
2608             bSuccess = TRUE;
2609         /* User name okay, need password... */
2610         else if (nResCode == 331)
2611             bSuccess = FTP_SendPassword(lpwfs);
2612         /* Need account for login... */
2613         else if (nResCode == 332)
2614             bSuccess = FTP_SendAccount(lpwfs);
2615         else
2616             FTP_SetResponseError(nResCode);
2617     }
2618
2619     TRACE("Returning %d\n", bSuccess);
2620 lend:
2621     return bSuccess;
2622 }
2623
2624
2625 /***********************************************************************
2626  *           FTP_SendCommandA (internal)
2627  *
2628  * Send command to server
2629  *
2630  * RETURNS
2631  *   TRUE on success
2632  *   NULL on failure
2633  *
2634  */
2635 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2636         INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2637 {
2638         DWORD len;
2639         CHAR *buf;
2640         DWORD nBytesSent = 0;
2641         int nRC = 0;
2642         DWORD dwParamLen;
2643
2644         TRACE("%d: (%s) %d\n", ftpCmd, debugstr_a(lpszParam), nSocket);
2645
2646         if (lpfnStatusCB)
2647         {
2648             lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2649         }
2650
2651         dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2652         len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2653         if (NULL == (buf = heap_alloc(len+1)))
2654         {
2655             INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2656             return FALSE;
2657         }
2658         sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2659                 dwParamLen ? lpszParam : "", szCRLF);
2660
2661         TRACE("Sending (%s) len(%d)\n", buf, len);
2662         while((nBytesSent < len) && (nRC != -1))
2663         {
2664                 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2665                 nBytesSent += nRC;
2666         }
2667     heap_free(buf);
2668
2669         if (lpfnStatusCB)
2670         {
2671             lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2672                          &nBytesSent, sizeof(DWORD));
2673         }
2674
2675         TRACE("Sent %d bytes\n", nBytesSent);
2676         return (nRC != -1);
2677 }
2678
2679 /***********************************************************************
2680  *           FTP_SendCommand (internal)
2681  *
2682  * Send command to server
2683  *
2684  * RETURNS
2685  *   TRUE on success
2686  *   NULL on failure
2687  *
2688  */
2689 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2690         INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2691 {
2692     BOOL ret;
2693     LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2694     ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2695     heap_free(lpszParamA);
2696     return ret;
2697 }
2698
2699 /***********************************************************************
2700  *           FTP_ReceiveResponse (internal)
2701  *
2702  * Receive response from server
2703  *
2704  * RETURNS
2705  *   Reply code on success
2706  *   0 on failure
2707  *
2708  */
2709 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2710 {
2711     LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2712     DWORD nRecv;
2713     INT rc = 0;
2714     char firstprefix[5];
2715     BOOL multiline = FALSE;
2716
2717     TRACE("socket(%d)\n", lpwfs->sndSocket);
2718
2719     SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2720
2721     while(1)
2722     {
2723         if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2724             goto lerror;
2725
2726         if (nRecv >= 3)
2727         {
2728             if(!multiline)
2729             {
2730                 if(lpszResponse[3] != '-')
2731                     break;
2732                 else
2733                 {  /* Start of multiline response.  Loop until we get "nnn " */
2734                     multiline = TRUE;
2735                     memcpy(firstprefix, lpszResponse, 3);
2736                     firstprefix[3] = ' ';
2737                     firstprefix[4] = '\0';
2738                 }
2739             }
2740             else
2741             {
2742                 if(!memcmp(firstprefix, lpszResponse, 4))
2743                     break;
2744             }
2745         }
2746     }
2747
2748     if (nRecv >= 3)
2749     {
2750         rc = atoi(lpszResponse);
2751
2752         SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2753                     &nRecv, sizeof(DWORD));
2754     }
2755
2756 lerror:
2757     TRACE("return %d\n", rc);
2758     return rc;
2759 }
2760
2761
2762 /***********************************************************************
2763  *           FTP_SendPassword (internal)
2764  *
2765  * Send password to ftp server
2766  *
2767  * RETURNS
2768  *   TRUE on success
2769  *   NULL on failure
2770  *
2771  */
2772 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2773 {
2774     INT nResCode;
2775     BOOL bSuccess = FALSE;
2776
2777     TRACE("\n");
2778     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2779         goto lend;
2780
2781     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2782     if (nResCode)
2783     {
2784         TRACE("Received reply code %d\n", nResCode);
2785         /* Login successful... */
2786         if (nResCode == 230)
2787             bSuccess = TRUE;
2788         /* Command not implemented, superfluous at the server site... */
2789         /* Need account for login... */
2790         else if (nResCode == 332)
2791             bSuccess = FTP_SendAccount(lpwfs);
2792         else
2793             FTP_SetResponseError(nResCode);
2794     }
2795
2796 lend:
2797     TRACE("Returning %d\n", bSuccess);
2798     return bSuccess;
2799 }
2800
2801
2802 /***********************************************************************
2803  *           FTP_SendAccount (internal)
2804  *
2805  *
2806  *
2807  * RETURNS
2808  *   TRUE on success
2809  *   FALSE on failure
2810  *
2811  */
2812 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2813 {
2814     INT nResCode;
2815     BOOL bSuccess = FALSE;
2816
2817     TRACE("\n");
2818     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2819         goto lend;
2820
2821     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2822     if (nResCode)
2823         bSuccess = TRUE;
2824     else
2825         FTP_SetResponseError(nResCode);
2826
2827 lend:
2828     return bSuccess;
2829 }
2830
2831
2832 /***********************************************************************
2833  *           FTP_SendStore (internal)
2834  *
2835  * Send request to upload file to ftp server
2836  *
2837  * RETURNS
2838  *   TRUE on success
2839  *   FALSE on failure
2840  *
2841  */
2842 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2843 {
2844     INT nResCode;
2845     BOOL bSuccess = FALSE;
2846
2847     TRACE("\n");
2848     if (!FTP_InitListenSocket(lpwfs))
2849         goto lend;
2850
2851     if (!FTP_SendType(lpwfs, dwType))
2852         goto lend;
2853
2854     if (!FTP_SendPortOrPasv(lpwfs))
2855         goto lend;
2856
2857     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2858             goto lend;
2859     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2860     if (nResCode)
2861     {
2862         if (nResCode == 150 || nResCode == 125)
2863             bSuccess = TRUE;
2864         else
2865             FTP_SetResponseError(nResCode);
2866     }
2867
2868 lend:
2869     if (!bSuccess && lpwfs->lstnSocket != -1)
2870     {
2871         closesocket(lpwfs->lstnSocket);
2872         lpwfs->lstnSocket = -1;
2873     }
2874
2875     return bSuccess;
2876 }
2877
2878
2879 /***********************************************************************
2880  *           FTP_InitListenSocket (internal)
2881  *
2882  * Create a socket to listen for server response
2883  *
2884  * RETURNS
2885  *   TRUE on success
2886  *   FALSE on failure
2887  *
2888  */
2889 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2890 {
2891     BOOL bSuccess = FALSE;
2892     socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2893
2894     TRACE("\n");
2895
2896     lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2897     if (lpwfs->lstnSocket == -1)
2898     {
2899         TRACE("Unable to create listening socket\n");
2900             goto lend;
2901     }
2902
2903     /* We obtain our ip addr from the name of the command channel socket */
2904     lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2905
2906     /* and get the system to assign us a port */
2907     lpwfs->lstnSocketAddress.sin_port = htons(0);
2908
2909     if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2910     {
2911         TRACE("Unable to bind socket\n");
2912         goto lend;
2913     }
2914
2915     if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2916     {
2917         TRACE("listen failed\n");
2918         goto lend;
2919     }
2920
2921     if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2922         bSuccess = TRUE;
2923
2924 lend:
2925     if (!bSuccess && lpwfs->lstnSocket != -1)
2926     {
2927         closesocket(lpwfs->lstnSocket);
2928         lpwfs->lstnSocket = -1;
2929     }
2930
2931     return bSuccess;
2932 }
2933
2934
2935 /***********************************************************************
2936  *           FTP_SendType (internal)
2937  *
2938  * Tell server type of data being transferred
2939  *
2940  * RETURNS
2941  *   TRUE on success
2942  *   FALSE on failure
2943  *
2944  * W98SE doesn't cache the type that's currently set
2945  * (i.e. it sends it always),
2946  * so we probably don't want to do that either.
2947  */
2948 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2949 {
2950     INT nResCode;
2951     WCHAR type[] = { 'I','\0' };
2952     BOOL bSuccess = FALSE;
2953
2954     TRACE("\n");
2955     if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2956         type[0] = 'A';
2957
2958     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2959         goto lend;
2960
2961     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2962     if (nResCode)
2963     {
2964         if (nResCode == 2)
2965             bSuccess = TRUE;
2966         else
2967             FTP_SetResponseError(nResCode);
2968     }
2969
2970 lend:
2971     return bSuccess;
2972 }
2973
2974
2975 #if 0  /* FIXME: should probably be used for FtpGetFileSize */
2976 /***********************************************************************
2977  *           FTP_GetFileSize (internal)
2978  *
2979  * Retrieves from the server the size of the given file
2980  *
2981  * RETURNS
2982  *   TRUE on success
2983  *   FALSE on failure
2984  *
2985  */
2986 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2987 {
2988     INT nResCode;
2989     BOOL bSuccess = FALSE;
2990
2991     TRACE("\n");
2992
2993     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2994         goto lend;
2995
2996     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2997     if (nResCode)
2998     {
2999         if (nResCode == 213) {
3000             /* Now parses the output to get the actual file size */
3001             int i;
3002             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3003
3004             for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3005             if (lpszResponseBuffer[i] == '\0') return FALSE;
3006             *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3007             
3008             bSuccess = TRUE;
3009         } else {
3010             FTP_SetResponseError(nResCode);
3011         }
3012     }
3013
3014 lend:
3015     return bSuccess;
3016 }
3017 #endif
3018
3019
3020 /***********************************************************************
3021  *           FTP_SendPort (internal)
3022  *
3023  * Tell server which port to use
3024  *
3025  * RETURNS
3026  *   TRUE on success
3027  *   FALSE on failure
3028  *
3029  */
3030 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3031 {
3032     static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3033     INT nResCode;
3034     WCHAR szIPAddress[64];
3035     BOOL bSuccess = FALSE;
3036     TRACE("\n");
3037
3038     sprintfW(szIPAddress, szIPFormat,
3039          lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
3040         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
3041         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
3042         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
3043         lpwfs->lstnSocketAddress.sin_port & 0xFF,
3044         (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3045
3046     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3047         goto lend;
3048
3049     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3050     if (nResCode)
3051     {
3052         if (nResCode == 200)
3053             bSuccess = TRUE;
3054         else
3055             FTP_SetResponseError(nResCode);
3056     }
3057
3058 lend:
3059     return bSuccess;
3060 }
3061
3062
3063 /***********************************************************************
3064  *           FTP_DoPassive (internal)
3065  *
3066  * Tell server that we want to do passive transfers
3067  * and connect data socket
3068  *
3069  * RETURNS
3070  *   TRUE on success
3071  *   FALSE on failure
3072  *
3073  */
3074 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3075 {
3076     INT nResCode;
3077     BOOL bSuccess = FALSE;
3078
3079     TRACE("\n");
3080     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3081         goto lend;
3082
3083     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3084     if (nResCode)
3085     {
3086         if (nResCode == 227)
3087         {
3088             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3089             LPSTR p;
3090             int f[6];
3091             int i;
3092             char *pAddr, *pPort;
3093             INT nsocket = -1;
3094             struct sockaddr_in dataSocketAddress;
3095
3096             p = lpszResponseBuffer+4; /* skip status code */
3097             while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3098
3099             if (*p == '\0')
3100             {
3101                 ERR("no address found in response, aborting\n");
3102                 goto lend;
3103             }
3104
3105             if (sscanf(p, "%d,%d,%d,%d,%d,%d",  &f[0], &f[1], &f[2], &f[3],
3106                                                 &f[4], &f[5]) != 6)
3107             {
3108                 ERR("unknown response address format '%s', aborting\n", p);
3109                 goto lend;
3110             }
3111             for (i=0; i < 6; i++)
3112                 f[i] = f[i] & 0xff;
3113
3114             dataSocketAddress = lpwfs->socketAddress;
3115             pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3116             pPort = (char *)&(dataSocketAddress.sin_port);
3117             pAddr[0] = f[0];
3118             pAddr[1] = f[1];
3119             pAddr[2] = f[2];
3120             pAddr[3] = f[3];
3121             pPort[0] = f[4];
3122             pPort[1] = f[5];
3123
3124             nsocket = socket(AF_INET,SOCK_STREAM,0);
3125             if (nsocket == -1)
3126                 goto lend;
3127
3128             if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3129             {
3130                 ERR("can't connect passive FTP data port.\n");
3131                 closesocket(nsocket);
3132                 goto lend;
3133             }
3134             lpwfs->pasvSocket = nsocket;
3135             bSuccess = TRUE;
3136         }
3137         else
3138             FTP_SetResponseError(nResCode);
3139     }
3140
3141 lend:
3142     return bSuccess;
3143 }
3144
3145
3146 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3147 {
3148     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3149     {
3150         if (!FTP_DoPassive(lpwfs))
3151             return FALSE;
3152     }
3153     else
3154     {
3155         if (!FTP_SendPort(lpwfs))
3156             return FALSE;
3157     }
3158     return TRUE;
3159 }
3160
3161
3162 /***********************************************************************
3163  *           FTP_GetDataSocket (internal)
3164  *
3165  * Either accepts an incoming data socket connection from the server
3166  * or just returns the already opened socket after a PASV command
3167  * in case of passive FTP.
3168  *
3169  *
3170  * RETURNS
3171  *   TRUE on success
3172  *   FALSE on failure
3173  *
3174  */
3175 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3176 {
3177     struct sockaddr_in saddr;
3178     socklen_t addrlen = sizeof(struct sockaddr);
3179
3180     TRACE("\n");
3181     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3182     {
3183         *nDataSocket = lpwfs->pasvSocket;
3184         lpwfs->pasvSocket = -1;
3185     }
3186     else
3187     {
3188         *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3189         closesocket(lpwfs->lstnSocket);
3190         lpwfs->lstnSocket = -1;
3191     }
3192     return *nDataSocket != -1;
3193 }
3194
3195
3196 /***********************************************************************
3197  *           FTP_SendData (internal)
3198  *
3199  * Send data to the server
3200  *
3201  * RETURNS
3202  *   TRUE on success
3203  *   FALSE on failure
3204  *
3205  */
3206 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3207 {
3208     BY_HANDLE_FILE_INFORMATION fi;
3209     DWORD nBytesRead = 0;
3210     DWORD nBytesSent = 0;
3211     DWORD nTotalSent = 0;
3212     DWORD nBytesToSend, nLen;
3213     int nRC = 1;
3214     time_t s_long_time, e_long_time;
3215     LONG nSeconds;
3216     CHAR *lpszBuffer;
3217
3218     TRACE("\n");
3219     lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3220
3221     /* Get the size of the file. */
3222     GetFileInformationByHandle(hFile, &fi);
3223     time(&s_long_time);
3224
3225     do
3226     {
3227         nBytesToSend = nBytesRead - nBytesSent;
3228
3229         if (nBytesToSend <= 0)
3230         {
3231             /* Read data from file. */
3232             nBytesSent = 0;
3233             if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3234             ERR("Failed reading from file\n");
3235
3236             if (nBytesRead > 0)
3237                 nBytesToSend = nBytesRead;
3238             else
3239                 break;
3240         }
3241
3242         nLen = DATA_PACKET_SIZE < nBytesToSend ?
3243             DATA_PACKET_SIZE : nBytesToSend;
3244         nRC  = send(nDataSocket, lpszBuffer, nLen, 0);
3245
3246         if (nRC != -1)
3247         {
3248             nBytesSent += nRC;
3249             nTotalSent += nRC;
3250         }
3251
3252         /* Do some computation to display the status. */
3253         time(&e_long_time);
3254         nSeconds = e_long_time - s_long_time;
3255         if( nSeconds / 60 > 0 )
3256         {
3257             TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3258             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3259             nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3260         }
3261         else
3262         {
3263             TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3264             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3265             (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3266         }
3267     } while (nRC != -1);
3268
3269     TRACE("file transfer complete!\n");
3270
3271     heap_free(lpszBuffer);
3272     return nTotalSent;
3273 }
3274
3275
3276 /***********************************************************************
3277  *           FTP_SendRetrieve (internal)
3278  *
3279  * Send request to retrieve a file
3280  *
3281  * RETURNS
3282  *   Number of bytes to be received on success
3283  *   0 on failure
3284  *
3285  */
3286 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3287 {
3288     INT nResCode;
3289     BOOL ret;
3290
3291     TRACE("\n");
3292     if (!(ret = FTP_InitListenSocket(lpwfs)))
3293         goto lend;
3294
3295     if (!(ret = FTP_SendType(lpwfs, dwType)))
3296         goto lend;
3297
3298     if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3299         goto lend;
3300
3301     if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3302         goto lend;
3303
3304     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3305     if ((nResCode != 125) && (nResCode != 150)) {
3306         /* That means that we got an error getting the file. */
3307         FTP_SetResponseError(nResCode);
3308         ret = FALSE;
3309     }
3310
3311 lend:
3312     if (!ret && lpwfs->lstnSocket != -1)
3313     {
3314         closesocket(lpwfs->lstnSocket);
3315         lpwfs->lstnSocket = -1;
3316     }
3317
3318     return ret;
3319 }
3320
3321
3322 /***********************************************************************
3323  *           FTP_RetrieveData  (internal)
3324  *
3325  * Retrieve data from server
3326  *
3327  * RETURNS
3328  *   TRUE on success
3329  *   FALSE on failure
3330  *
3331  */
3332 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3333 {
3334     DWORD nBytesWritten;
3335     INT nRC = 0;
3336     CHAR *lpszBuffer;
3337
3338     TRACE("\n");
3339
3340     lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3341     if (NULL == lpszBuffer)
3342     {
3343         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3344         return FALSE;
3345     }
3346
3347     while (nRC != -1)
3348     {
3349         nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3350         if (nRC != -1)
3351         {
3352             /* other side closed socket. */
3353             if (nRC == 0)
3354                 goto recv_end;
3355             WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3356         }
3357     }
3358
3359     TRACE("Data transfer complete\n");
3360
3361 recv_end:
3362     heap_free(lpszBuffer);
3363     return (nRC != -1);
3364 }
3365
3366 /***********************************************************************
3367  *           FTPFINDNEXT_Destroy (internal)
3368  *
3369  * Deallocate session handle
3370  */
3371 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3372 {
3373     LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3374     DWORD i;
3375
3376     TRACE("\n");
3377
3378     WININET_Release(&lpwfn->lpFtpSession->hdr);
3379
3380     for (i = 0; i < lpwfn->size; i++)
3381     {
3382         heap_free(lpwfn->lpafp[i].lpszName);
3383     }
3384     heap_free(lpwfn->lpafp);
3385 }
3386
3387 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3388 {
3389     WIN32_FIND_DATAW *find_data = data;
3390     DWORD res = ERROR_SUCCESS;
3391
3392     TRACE("index(%d) size(%d)\n", find->index, find->size);
3393
3394     ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3395
3396     if (find->index < find->size) {
3397         FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3398         find->index++;
3399
3400         TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3401     }else {
3402         res = ERROR_NO_MORE_FILES;
3403     }
3404
3405     if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3406     {
3407         INTERNET_ASYNC_RESULT iar;
3408
3409         iar.dwResult = (res == ERROR_SUCCESS);
3410         iar.dwError = res;
3411
3412         INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3413                               INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3414                               sizeof(INTERNET_ASYNC_RESULT));
3415     }
3416
3417     return res;
3418 }
3419
3420 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3421 {
3422     struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3423
3424     FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3425 }
3426
3427 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3428 {
3429     switch(option) {
3430     case INTERNET_OPTION_HANDLE_TYPE:
3431         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3432
3433         if (*size < sizeof(ULONG))
3434             return ERROR_INSUFFICIENT_BUFFER;
3435
3436         *size = sizeof(DWORD);
3437         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3438         return ERROR_SUCCESS;
3439     }
3440
3441     return INET_QueryOption(hdr, option, buffer, size, unicode);
3442 }
3443
3444 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3445 {
3446     WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3447
3448     if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3449     {
3450         WORKREQUEST workRequest;
3451         struct WORKREQ_FTPFINDNEXTW *req;
3452
3453         workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3454         workRequest.hdr = WININET_AddRef( &find->hdr );
3455         req = &workRequest.u.FtpFindNextW;
3456         req->lpFindFileData = data;
3457
3458         INTERNET_AsyncCall(&workRequest);
3459
3460         return ERROR_SUCCESS;
3461     }
3462
3463     return FTPFINDNEXT_FindNextFileProc(find, data);
3464 }
3465
3466 static const object_vtbl_t FTPFINDNEXTVtbl = {
3467     FTPFINDNEXT_Destroy,
3468     NULL,
3469     FTPFINDNEXT_QueryOption,
3470     INET_SetOption,
3471     NULL,
3472     NULL,
3473     NULL,
3474     NULL,
3475     FTPFINDNEXT_FindNextFileW
3476 };
3477
3478 /***********************************************************************
3479  *           FTP_ReceiveFileList (internal)
3480  *
3481  * Read file list from server
3482  *
3483  * RETURNS
3484  *   Handle to file list on success
3485  *   NULL on failure
3486  *
3487  */
3488 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3489         LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3490 {
3491     DWORD dwSize = 0;
3492     LPFILEPROPERTIESW lpafp = NULL;
3493     LPWININETFTPFINDNEXTW lpwfn = NULL;
3494
3495     TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3496
3497     if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3498     {
3499         if(lpFindFileData)
3500             FTP_ConvertFileProp(lpafp, lpFindFileData);
3501
3502         lpwfn = alloc_object(&lpwfs->hdr, &FTPFINDNEXTVtbl, sizeof(WININETFTPFINDNEXTW));
3503         if (lpwfn)
3504         {
3505             lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3506             lpwfn->hdr.dwContext = dwContext;
3507             lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3508             lpwfn->size = dwSize;
3509             lpwfn->lpafp = lpafp;
3510
3511             WININET_AddRef( &lpwfs->hdr );
3512             lpwfn->lpFtpSession = lpwfs;
3513             list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3514         }
3515     }
3516
3517     TRACE("Matched %d files\n", dwSize);
3518     return lpwfn ? lpwfn->hdr.hInternet : NULL;
3519 }
3520
3521
3522 /***********************************************************************
3523  *           FTP_ConvertFileProp (internal)
3524  *
3525  * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3526  *
3527  * RETURNS
3528  *   TRUE on success
3529  *   FALSE on failure
3530  *
3531  */
3532 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3533 {
3534     BOOL bSuccess = FALSE;
3535
3536     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3537
3538     if (lpafp)
3539     {
3540         SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3541         lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3542         lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3543         
3544         /* Not all fields are filled in */
3545         lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3546         lpFindFileData->nFileSizeLow = lpafp->nSize;
3547
3548         if (lpafp->bIsDirectory)
3549             lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3550
3551         if (lpafp->lpszName)
3552             lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3553
3554         bSuccess = TRUE;
3555     }
3556
3557     return bSuccess;
3558 }
3559
3560 /***********************************************************************
3561  *           FTP_ParseNextFile (internal)
3562  *
3563  * Parse the next line in file listing
3564  *
3565  * RETURNS
3566  *   TRUE on success
3567  *   FALSE on failure
3568  */
3569 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3570 {
3571     static const char szSpace[] = " \t";
3572     DWORD nBufLen;
3573     char *pszLine;
3574     char *pszToken;
3575     char *pszTmp;
3576     BOOL found = FALSE;
3577     int i;
3578     
3579     lpfp->lpszName = NULL;
3580     do {
3581         if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3582             return FALSE;
3583     
3584         pszToken = strtok(pszLine, szSpace);
3585         /* ls format
3586          * <Permissions> <NoLinks> <owner>   <group> <size> <date>  <time or year> <filename>
3587          *
3588          * For instance:
3589          * drwx--s---     2         pcarrier  ens     512    Sep 28  1995           pcarrier
3590          */
3591         if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3592             if(!FTP_ParsePermission(pszToken, lpfp))
3593                 lpfp->bIsDirectory = FALSE;
3594             for(i=0; i<=3; i++) {
3595               if(!(pszToken = strtok(NULL, szSpace)))
3596                   break;
3597             }
3598             if(!pszToken) continue;
3599             if(lpfp->bIsDirectory) {
3600                 TRACE("Is directory\n");
3601                 lpfp->nSize = 0;
3602             }
3603             else {
3604                 TRACE("Size: %s\n", pszToken);
3605                 lpfp->nSize = atol(pszToken);
3606             }
3607             
3608             lpfp->tmLastModified.wSecond = 0;
3609             lpfp->tmLastModified.wMinute = 0;
3610             lpfp->tmLastModified.wHour   = 0;
3611             lpfp->tmLastModified.wDay    = 0;
3612             lpfp->tmLastModified.wMonth  = 0;
3613             lpfp->tmLastModified.wYear   = 0;
3614             
3615             /* Determine month */
3616             pszToken = strtok(NULL, szSpace);
3617             if(!pszToken) continue;
3618             if(strlen(pszToken) >= 3) {
3619                 pszToken[3] = 0;
3620                 if((pszTmp = StrStrIA(szMonths, pszToken)))
3621                     lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3622             }
3623             /* Determine day */
3624             pszToken = strtok(NULL, szSpace);
3625             if(!pszToken) continue;
3626             lpfp->tmLastModified.wDay = atoi(pszToken);
3627             /* Determine time or year */
3628             pszToken = strtok(NULL, szSpace);
3629             if(!pszToken) continue;
3630             if((pszTmp = strchr(pszToken, ':'))) {
3631                 SYSTEMTIME curr_time;
3632                 *pszTmp = 0;
3633                 pszTmp++;
3634                 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3635                 lpfp->tmLastModified.wHour = atoi(pszToken);
3636                 GetLocalTime( &curr_time );
3637                 lpfp->tmLastModified.wYear = curr_time.wYear;
3638             }
3639             else {
3640                 lpfp->tmLastModified.wYear = atoi(pszToken);
3641                 lpfp->tmLastModified.wHour = 12;
3642             }
3643             TRACE("Mod time: %02d:%02d:%02d  %04d/%02d/%02d\n",
3644                   lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3645                   lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3646
3647             pszToken = strtok(NULL, szSpace);
3648             if(!pszToken) continue;
3649             lpfp->lpszName = heap_strdupAtoW(pszToken);
3650             TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3651         }
3652         /* NT way of parsing ... :
3653             
3654                 07-13-03  08:55PM       <DIR>          sakpatch
3655                 05-09-03  06:02PM             12656686 2003-04-21bgm_cmd_e.rgz
3656         */
3657         else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3658             int mon, mday, year, hour, min;
3659             lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3660             
3661             sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3662             lpfp->tmLastModified.wDay   = mday;
3663             lpfp->tmLastModified.wMonth = mon;
3664             lpfp->tmLastModified.wYear  = year;
3665
3666             /* Hacky and bad Y2K protection :-) */
3667             if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3668
3669             pszToken = strtok(NULL, szSpace);
3670             if(!pszToken) continue;
3671             sscanf(pszToken, "%d:%d", &hour, &min);
3672             lpfp->tmLastModified.wHour   = hour;
3673             lpfp->tmLastModified.wMinute = min;
3674             if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3675                 lpfp->tmLastModified.wHour += 12;
3676             }
3677             lpfp->tmLastModified.wSecond = 0;
3678
3679             TRACE("Mod time: %02d:%02d:%02d  %04d/%02d/%02d\n",
3680                   lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3681                   lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3682
3683             pszToken = strtok(NULL, szSpace);
3684             if(!pszToken) continue;
3685             if(!strcasecmp(pszToken, "<DIR>")) {
3686                 lpfp->bIsDirectory = TRUE;
3687                 lpfp->nSize = 0;
3688                 TRACE("Is directory\n");
3689             }
3690             else {
3691                 lpfp->bIsDirectory = FALSE;
3692                 lpfp->nSize = atol(pszToken);
3693                 TRACE("Size: %d\n", lpfp->nSize);
3694             }
3695             
3696             pszToken = strtok(NULL, szSpace);
3697             if(!pszToken) continue;
3698             lpfp->lpszName = heap_strdupAtoW(pszToken);
3699             TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3700         }
3701         /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3702         else if(pszToken[0] == '+') {
3703             FIXME("EPLF Format not implemented\n");
3704         }
3705         
3706         if(lpfp->lpszName) {
3707             if((lpszSearchFile == NULL) ||
3708                (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3709                 found = TRUE;
3710                 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3711             }
3712             else {
3713                 heap_free(lpfp->lpszName);
3714                 lpfp->lpszName = NULL;
3715             }
3716         }
3717     } while(!found);
3718     return TRUE;
3719 }
3720
3721 /***********************************************************************
3722  *           FTP_ParseDirectory (internal)
3723  *
3724  * Parse string of directory information
3725  *
3726  * RETURNS
3727  *   TRUE on success
3728  *   FALSE on failure
3729  */
3730 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3731     LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3732 {
3733     BOOL bSuccess = TRUE;
3734     INT sizeFilePropArray = 500;/*20; */
3735     INT indexFilePropArray = -1;
3736
3737     TRACE("\n");
3738
3739     /* Allocate initial file properties array */
3740     *lpafp = heap_alloc_zero(sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3741     if (!*lpafp)
3742         return FALSE;
3743
3744     do {
3745         if (indexFilePropArray+1 >= sizeFilePropArray)
3746         {
3747             LPFILEPROPERTIESW tmpafp;
3748             
3749             sizeFilePropArray *= 2;
3750             tmpafp = heap_realloc_zero(*lpafp, sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3751             if (NULL == tmpafp)
3752             {
3753                 bSuccess = FALSE;
3754                 break;
3755             }
3756
3757             *lpafp = tmpafp;
3758         }
3759         indexFilePropArray++;
3760     } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3761
3762     if (bSuccess && indexFilePropArray)
3763     {
3764         if (indexFilePropArray < sizeFilePropArray - 1)
3765         {
3766             LPFILEPROPERTIESW tmpafp;
3767
3768             tmpafp = heap_realloc(*lpafp, sizeof(FILEPROPERTIESW)*indexFilePropArray);
3769             if (NULL != tmpafp)
3770                 *lpafp = tmpafp;
3771         }
3772         *dwfp = indexFilePropArray;
3773     }
3774     else
3775     {
3776         heap_free(*lpafp);
3777         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3778         bSuccess = FALSE;
3779     }
3780
3781     return bSuccess;
3782 }
3783
3784
3785 /***********************************************************************
3786  *           FTP_ParsePermission (internal)
3787  *
3788  * Parse permission string of directory information
3789  *
3790  * RETURNS
3791  *   TRUE on success
3792  *   FALSE on failure
3793  *
3794  */
3795 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3796 {
3797     BOOL bSuccess = TRUE;
3798     unsigned short nPermission = 0;
3799     INT nPos = 1;
3800     INT nLast  = 9;
3801
3802     TRACE("\n");
3803     if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3804     {
3805         bSuccess = FALSE;
3806         return bSuccess;
3807     }
3808
3809     lpfp->bIsDirectory = (*lpszPermission == 'd');
3810     do
3811     {
3812         switch (nPos)
3813         {
3814             case 1:
3815                 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3816                 break;
3817             case 2:
3818                 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3819                 break;
3820             case 3:
3821                 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3822                 break;
3823             case 4:
3824                 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3825                 break;
3826             case 5:
3827                 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3828                 break;
3829             case 6:
3830                 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3831                 break;
3832             case 7:
3833                 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3834                 break;
3835             case 8:
3836                 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3837                 break;
3838             case 9:
3839                 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3840                 break;
3841         }
3842         nPos++;
3843     }while (nPos <= nLast);
3844
3845     lpfp->permissions = nPermission;
3846     return bSuccess;
3847 }
3848
3849
3850 /***********************************************************************
3851  *           FTP_SetResponseError (internal)
3852  *
3853  * Set the appropriate error code for a given response from the server
3854  *
3855  * RETURNS
3856  *
3857  */
3858 static DWORD FTP_SetResponseError(DWORD dwResponse)
3859 {
3860     DWORD dwCode = 0;
3861
3862     switch(dwResponse)
3863     {
3864     case 425: /* Cannot open data connection. */
3865         dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3866         break;
3867
3868     case 426: /* Connection closed, transer aborted. */
3869         dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3870         break;
3871
3872     case 530: /* Not logged in. Login incorrect. */
3873         dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3874         break;
3875
3876     case 421: /* Service not available - Server may be shutting down. */
3877     case 450: /* File action not taken. File may be busy. */
3878     case 451: /* Action aborted. Server error. */
3879     case 452: /* Action not taken. Insufficient storage space on server. */
3880     case 500: /* Syntax error. Command unrecognized. */
3881     case 501: /* Syntax error. Error in parameters or arguments. */
3882     case 502: /* Command not implemented. */
3883     case 503: /* Bad sequence of commands. */
3884     case 504: /* Command not implemented for that parameter. */
3885     case 532: /* Need account for storing files */
3886     case 550: /* File action not taken. File not found or no access. */
3887     case 551: /* Requested action aborted. Page type unknown */
3888     case 552: /* Action aborted. Exceeded storage allocation */
3889     case 553: /* Action not taken. File name not allowed. */
3890
3891     default:
3892         dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3893         break;
3894     }
3895
3896     INTERNET_SetLastError(dwCode);
3897     return dwCode;
3898 }