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