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