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