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