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