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