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