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