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