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