mshtml: Added IHTMLElement2::get_readyState implementation.
[wine] / dlls / wininet / ftp.c
1 /*
2  * WININET - Ftp implementation
3  *
4  * Copyright 1999 Corel Corporation
5  * Copyright 2004 Mike McCormack for CodeWeavers
6  * Copyright 2004 Kevin Koltzau
7  * Copyright 2007 Hans Leidekker
8  *
9  * Ulrich Czekalla
10  * Noureddine Jemmali
11  *
12  * Copyright 2000 Andreas Mohr
13  * Copyright 2002 Jaco Greeff
14  *
15  * This library is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU Lesser General Public
17  * License as published by the Free Software Foundation; either
18  * version 2.1 of the License, or (at your option) any later version.
19  *
20  * This library is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23  * Lesser General Public License for more details.
24  *
25  * You should have received a copy of the GNU Lesser General Public
26  * License along with this library; if not, write to the Free Software
27  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28  */
29
30 #include "config.h"
31 #include "wine/port.h"
32
33 #if defined(__MINGW32__) || defined (_MSC_VER)
34 #include <ws2tcpip.h>
35 #endif
36
37 #include <errno.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/types.h>
43 #ifdef HAVE_SYS_SOCKET_H
44 # include <sys/socket.h>
45 #endif
46 #ifdef HAVE_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 (ret && 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 DWORD 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 ? ERROR_SUCCESS : sock_get_error(errno);
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.dwInternalFlags = 0;
1297         lpwh->hdr.refs = 1;
1298         lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1299         lpwh->nDataSocket = nDataSocket;
1300         lpwh->session_deleted = FALSE;
1301
1302         WININET_AddRef( &lpwfs->hdr );
1303         lpwh->lpFtpSession = lpwfs;
1304         list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1305         
1306         handle = WININET_AllocHandle( &lpwh->hdr );
1307         if( !handle )
1308             goto lend;
1309
1310         /* Indicate that a download is currently in progress */
1311         lpwfs->download_in_progress = lpwh;
1312     }
1313
1314     if (lpwfs->lstnSocket != -1)
1315     {
1316         closesocket(lpwfs->lstnSocket);
1317         lpwfs->lstnSocket = -1;
1318     }
1319
1320     hIC = lpwfs->lpAppInfo;
1321     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1322     {
1323         INTERNET_ASYNC_RESULT iar;
1324
1325         if (lpwh)
1326         {
1327             iar.dwResult = (DWORD_PTR)handle;
1328             iar.dwError = ERROR_SUCCESS;
1329             SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1330                 &iar, sizeof(INTERNET_ASYNC_RESULT));
1331         }
1332
1333         if(bSuccess) {
1334             FTP_ReceiveRequestData(lpwh, TRUE);
1335         }else {
1336             iar.dwResult = 0;
1337             iar.dwError = INTERNET_GetLastError();
1338             SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1339                     &iar, sizeof(INTERNET_ASYNC_RESULT));
1340         }
1341     }
1342
1343 lend:
1344     if( lpwh )
1345         WININET_Release( &lpwh->hdr );
1346
1347     return handle;
1348 }
1349
1350
1351 /***********************************************************************
1352  *           FtpOpenFileA (WININET.@)
1353  *
1354  * Open a remote file for writing or reading
1355  *
1356  * RETURNS
1357  *    HINTERNET handle on success
1358  *    NULL on failure
1359  *
1360  */
1361 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1362     LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1363     DWORD_PTR dwContext)
1364 {
1365     LPWSTR lpwzFileName;
1366     HINTERNET ret;
1367
1368     lpwzFileName = heap_strdupAtoW(lpszFileName);
1369     ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1370     HeapFree(GetProcessHeap(), 0, lpwzFileName);
1371     return ret;
1372 }
1373
1374
1375 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1376 {
1377     struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1378     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1379
1380     TRACE("%p\n", lpwfs);
1381
1382     FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1383         req->dwAccess, req->dwFlags, req->dwContext);
1384     HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1385 }
1386
1387 /***********************************************************************
1388  *           FtpOpenFileW (WININET.@)
1389  *
1390  * Open a remote file for writing or reading
1391  *
1392  * RETURNS
1393  *    HINTERNET handle on success
1394  *    NULL on failure
1395  *
1396  */
1397 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1398     LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1399     DWORD_PTR dwContext)
1400 {
1401     ftp_session_t *lpwfs;
1402     appinfo_t *hIC = NULL;
1403     HINTERNET r = NULL;
1404
1405     TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1406         debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1407
1408     lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1409     if (!lpwfs)
1410     {
1411         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1412         return FALSE;
1413     }
1414
1415     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1416     {
1417         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1418         goto lend;
1419     }
1420
1421     if ((!lpszFileName) ||
1422         ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1423         ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1424     {
1425         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1426         goto lend;
1427     }
1428
1429     if (lpwfs->download_in_progress != NULL)
1430     {
1431         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1432         goto lend;
1433     }
1434
1435     hIC = lpwfs->lpAppInfo;
1436     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1437     {
1438         WORKREQUEST workRequest;
1439         struct WORKREQ_FTPOPENFILEW *req;
1440
1441         workRequest.asyncproc = AsyncFtpOpenFileProc;
1442         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1443         req = &workRequest.u.FtpOpenFileW;
1444         req->lpszFilename = heap_strdupW(lpszFileName);
1445         req->dwAccess = fdwAccess;
1446         req->dwFlags = dwFlags;
1447         req->dwContext = dwContext;
1448
1449         INTERNET_AsyncCall(&workRequest);
1450         r = NULL;
1451     }
1452     else
1453     {
1454         r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1455     }
1456
1457 lend:
1458     WININET_Release( &lpwfs->hdr );
1459
1460     return r;
1461 }
1462
1463
1464 /***********************************************************************
1465  *           FtpGetFileA (WININET.@)
1466  *
1467  * Retrieve file from the FTP server
1468  *
1469  * RETURNS
1470  *    TRUE on success
1471  *    FALSE on failure
1472  *
1473  */
1474 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1475     BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1476     DWORD_PTR dwContext)
1477 {
1478     LPWSTR lpwzRemoteFile;
1479     LPWSTR lpwzNewFile;
1480     BOOL ret;
1481     
1482     lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1483     lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1484     ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1485         dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1486     HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1487     HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1488     return ret;
1489 }
1490
1491
1492 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1493 {
1494     struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1495     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1496
1497     TRACE("%p\n", lpwfs);
1498
1499     FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1500              req->lpszNewFile, req->fFailIfExists,
1501              req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1502     HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1503     HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1504 }
1505
1506
1507 /***********************************************************************
1508  *           FtpGetFileW (WININET.@)
1509  *
1510  * Retrieve file from the FTP server
1511  *
1512  * RETURNS
1513  *    TRUE on success
1514  *    FALSE on failure
1515  *
1516  */
1517 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1518     BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1519     DWORD_PTR dwContext)
1520 {
1521     ftp_session_t *lpwfs;
1522     appinfo_t *hIC = NULL;
1523     BOOL r = FALSE;
1524
1525     if (!lpszRemoteFile || !lpszNewFile)
1526     {
1527         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1528         return FALSE;
1529     }
1530
1531     lpwfs = (ftp_session_t*) WININET_GetObject( hInternet );
1532     if (!lpwfs)
1533     {
1534         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1535         return FALSE;
1536     }
1537
1538     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1539     {
1540         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1541         goto lend;
1542     }
1543
1544     if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1545     {
1546         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1547         goto lend;
1548     }
1549
1550     if (lpwfs->download_in_progress != NULL)
1551     {
1552         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1553         goto lend;
1554     }
1555     
1556     hIC = lpwfs->lpAppInfo;
1557     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1558     {
1559         WORKREQUEST workRequest;
1560         struct WORKREQ_FTPGETFILEW *req;
1561
1562         workRequest.asyncproc = AsyncFtpGetFileProc;
1563         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1564         req = &workRequest.u.FtpGetFileW;
1565         req->lpszRemoteFile = heap_strdupW(lpszRemoteFile);
1566         req->lpszNewFile = heap_strdupW(lpszNewFile);
1567         req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1568         req->fFailIfExists = fFailIfExists;
1569         req->dwFlags = dwInternetFlags;
1570         req->dwContext = dwContext;
1571
1572         r = INTERNET_AsyncCall(&workRequest);
1573     }
1574     else
1575     {
1576         r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1577            fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1578     }
1579
1580 lend:
1581     WININET_Release( &lpwfs->hdr );
1582
1583     return r;
1584 }
1585
1586
1587 /***********************************************************************
1588  *           FTP_FtpGetFileW (Internal)
1589  *
1590  * Retrieve file from the FTP server
1591  *
1592  * RETURNS
1593  *    TRUE on success
1594  *    FALSE on failure
1595  *
1596  */
1597 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1598         BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1599         DWORD_PTR dwContext)
1600 {
1601     BOOL bSuccess = FALSE;
1602     HANDLE hFile;
1603     appinfo_t *hIC = NULL;
1604
1605     TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1606
1607     /* Clear any error information */
1608     INTERNET_SetLastError(0);
1609
1610     /* Ensure we can write to lpszNewfile by opening it */
1611     hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1612         CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1613     if (INVALID_HANDLE_VALUE == hFile)
1614         return FALSE;
1615
1616     /* Set up socket to retrieve data */
1617     if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1618     {
1619         INT nDataSocket;
1620
1621         /* Get data socket to server */
1622         if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1623         {
1624             INT nResCode;
1625
1626             /* Receive data */
1627             FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1628             closesocket(nDataSocket);
1629
1630             nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1631             if (nResCode)
1632             {
1633                 if (nResCode == 226)
1634                     bSuccess = TRUE;
1635                 else
1636                     FTP_SetResponseError(nResCode);
1637             }
1638         }
1639     }
1640
1641     if (lpwfs->lstnSocket != -1)
1642     {
1643         closesocket(lpwfs->lstnSocket);
1644         lpwfs->lstnSocket = -1;
1645     }
1646
1647     CloseHandle(hFile);
1648
1649     hIC = lpwfs->lpAppInfo;
1650     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1651     {
1652         INTERNET_ASYNC_RESULT iar;
1653
1654         iar.dwResult = (DWORD)bSuccess;
1655         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1656         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1657             &iar, sizeof(INTERNET_ASYNC_RESULT));
1658     }
1659
1660     if (!bSuccess) DeleteFileW(lpszNewFile);
1661     return bSuccess;
1662 }
1663
1664 /***********************************************************************
1665  *           FtpGetFileSize  (WININET.@)
1666  */
1667 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1668 {
1669     FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1670
1671     if (lpdwFileSizeHigh)
1672         *lpdwFileSizeHigh = 0;
1673
1674     return 0;
1675 }
1676
1677 /***********************************************************************
1678  *           FtpDeleteFileA  (WININET.@)
1679  *
1680  * Delete a file on the ftp server
1681  *
1682  * RETURNS
1683  *    TRUE on success
1684  *    FALSE on failure
1685  *
1686  */
1687 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1688 {
1689     LPWSTR lpwzFileName;
1690     BOOL ret;
1691     
1692     lpwzFileName = heap_strdupAtoW(lpszFileName);
1693     ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1694     HeapFree(GetProcessHeap(), 0, lpwzFileName);
1695     return ret;
1696 }
1697
1698 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1699 {
1700     struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1701     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1702
1703     TRACE("%p\n", lpwfs);
1704
1705     FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1706     HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1707 }
1708
1709 /***********************************************************************
1710  *           FtpDeleteFileW  (WININET.@)
1711  *
1712  * Delete a file on the ftp server
1713  *
1714  * RETURNS
1715  *    TRUE on success
1716  *    FALSE on failure
1717  *
1718  */
1719 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1720 {
1721     ftp_session_t *lpwfs;
1722     appinfo_t *hIC = NULL;
1723     BOOL r = FALSE;
1724
1725     lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1726     if (!lpwfs)
1727     {
1728         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1729         return FALSE;
1730     }
1731
1732     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1733     {
1734         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1735         goto lend;
1736     }
1737
1738     if (lpwfs->download_in_progress != NULL)
1739     {
1740         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1741         goto lend;
1742     }
1743
1744     if (!lpszFileName)
1745     {
1746         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1747         goto lend;
1748     }
1749
1750     hIC = lpwfs->lpAppInfo;
1751     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1752     {
1753         WORKREQUEST workRequest;
1754         struct WORKREQ_FTPDELETEFILEW *req;
1755
1756         workRequest.asyncproc = AsyncFtpDeleteFileProc;
1757         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1758         req = &workRequest.u.FtpDeleteFileW;
1759         req->lpszFilename = heap_strdupW(lpszFileName);
1760
1761         r = INTERNET_AsyncCall(&workRequest);
1762     }
1763     else
1764     {
1765         r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1766     }
1767
1768 lend:
1769     WININET_Release( &lpwfs->hdr );
1770
1771     return r;
1772 }
1773
1774 /***********************************************************************
1775  *           FTP_FtpDeleteFileW  (Internal)
1776  *
1777  * Delete a file on the ftp server
1778  *
1779  * RETURNS
1780  *    TRUE on success
1781  *    FALSE on failure
1782  *
1783  */
1784 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1785 {
1786     INT nResCode;
1787     BOOL bSuccess = FALSE;
1788     appinfo_t *hIC = NULL;
1789
1790     TRACE("%p\n", lpwfs);
1791
1792     /* Clear any error information */
1793     INTERNET_SetLastError(0);
1794
1795     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1796         goto lend;
1797
1798     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1799     if (nResCode)
1800     {
1801         if (nResCode == 250)
1802             bSuccess = TRUE;
1803         else
1804             FTP_SetResponseError(nResCode);
1805     }
1806 lend:
1807     hIC = lpwfs->lpAppInfo;
1808     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1809     {
1810         INTERNET_ASYNC_RESULT iar;
1811
1812         iar.dwResult = (DWORD)bSuccess;
1813         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1814         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1815             &iar, sizeof(INTERNET_ASYNC_RESULT));
1816     }
1817
1818     return bSuccess;
1819 }
1820
1821
1822 /***********************************************************************
1823  *           FtpRemoveDirectoryA  (WININET.@)
1824  *
1825  * Remove a directory on the ftp server
1826  *
1827  * RETURNS
1828  *    TRUE on success
1829  *    FALSE on failure
1830  *
1831  */
1832 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1833 {
1834     LPWSTR lpwzDirectory;
1835     BOOL ret;
1836     
1837     lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1838     ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1839     HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1840     return ret;
1841 }
1842
1843 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1844 {
1845     struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1846     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1847
1848     TRACE("%p\n", lpwfs);
1849
1850     FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1851     HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1852 }
1853
1854 /***********************************************************************
1855  *           FtpRemoveDirectoryW  (WININET.@)
1856  *
1857  * Remove a directory on the ftp server
1858  *
1859  * RETURNS
1860  *    TRUE on success
1861  *    FALSE on failure
1862  *
1863  */
1864 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1865 {
1866     ftp_session_t *lpwfs;
1867     appinfo_t *hIC = NULL;
1868     BOOL r = FALSE;
1869
1870     lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1871     if (!lpwfs)
1872     {
1873         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1874         return FALSE;
1875     }
1876
1877     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1878     {
1879         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1880         goto lend;
1881     }
1882
1883     if (lpwfs->download_in_progress != NULL)
1884     {
1885         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1886         goto lend;
1887     }
1888
1889     if (!lpszDirectory)
1890     {
1891         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1892         goto lend;
1893     }
1894
1895     hIC = lpwfs->lpAppInfo;
1896     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1897     {
1898         WORKREQUEST workRequest;
1899         struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1900
1901         workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1902         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1903         req = &workRequest.u.FtpRemoveDirectoryW;
1904         req->lpszDirectory = heap_strdupW(lpszDirectory);
1905
1906         r = INTERNET_AsyncCall(&workRequest);
1907     }
1908     else
1909     {
1910         r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1911     }
1912
1913 lend:
1914     WININET_Release( &lpwfs->hdr );
1915
1916     return r;
1917 }
1918
1919 /***********************************************************************
1920  *           FTP_FtpRemoveDirectoryW  (Internal)
1921  *
1922  * Remove a directory on the ftp server
1923  *
1924  * RETURNS
1925  *    TRUE on success
1926  *    FALSE on failure
1927  *
1928  */
1929 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
1930 {
1931     INT nResCode;
1932     BOOL bSuccess = FALSE;
1933     appinfo_t *hIC = NULL;
1934
1935     TRACE("\n");
1936
1937     /* Clear any error information */
1938     INTERNET_SetLastError(0);
1939
1940     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1941         goto lend;
1942
1943     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1944     if (nResCode)
1945     {
1946         if (nResCode == 250)
1947             bSuccess = TRUE;
1948         else
1949             FTP_SetResponseError(nResCode);
1950     }
1951
1952 lend:
1953     hIC = lpwfs->lpAppInfo;
1954     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1955     {
1956         INTERNET_ASYNC_RESULT iar;
1957
1958         iar.dwResult = (DWORD)bSuccess;
1959         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1960         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1961             &iar, sizeof(INTERNET_ASYNC_RESULT));
1962     }
1963
1964     return bSuccess;
1965 }
1966
1967
1968 /***********************************************************************
1969  *           FtpRenameFileA  (WININET.@)
1970  *
1971  * Rename a file on the ftp server
1972  *
1973  * RETURNS
1974  *    TRUE on success
1975  *    FALSE on failure
1976  *
1977  */
1978 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1979 {
1980     LPWSTR lpwzSrc;
1981     LPWSTR lpwzDest;
1982     BOOL ret;
1983     
1984     lpwzSrc = heap_strdupAtoW(lpszSrc);
1985     lpwzDest = heap_strdupAtoW(lpszDest);
1986     ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1987     HeapFree(GetProcessHeap(), 0, lpwzSrc);
1988     HeapFree(GetProcessHeap(), 0, lpwzDest);
1989     return ret;
1990 }
1991
1992 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
1993 {
1994     struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
1995     ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1996
1997     TRACE("%p\n", lpwfs);
1998
1999     FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
2000     HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
2001     HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
2002 }
2003
2004 /***********************************************************************
2005  *           FtpRenameFileW  (WININET.@)
2006  *
2007  * Rename a file on the ftp server
2008  *
2009  * RETURNS
2010  *    TRUE on success
2011  *    FALSE on failure
2012  *
2013  */
2014 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2015 {
2016     ftp_session_t *lpwfs;
2017     appinfo_t *hIC = NULL;
2018     BOOL r = FALSE;
2019
2020     lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
2021     if (!lpwfs)
2022     {
2023         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2024         return FALSE;
2025     }
2026
2027     if (WH_HFTPSESSION != lpwfs->hdr.htype)
2028     {
2029         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2030         goto lend;
2031     }
2032
2033     if (lpwfs->download_in_progress != NULL)
2034     {
2035         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2036         goto lend;
2037     }
2038
2039     if (!lpszSrc || !lpszDest)
2040     {
2041         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2042         goto lend;
2043     }
2044
2045     hIC = lpwfs->lpAppInfo;
2046     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2047     {
2048         WORKREQUEST workRequest;
2049         struct WORKREQ_FTPRENAMEFILEW *req;
2050
2051         workRequest.asyncproc = AsyncFtpRenameFileProc;
2052         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
2053         req = &workRequest.u.FtpRenameFileW;
2054         req->lpszSrcFile = heap_strdupW(lpszSrc);
2055         req->lpszDestFile = heap_strdupW(lpszDest);
2056
2057         r = INTERNET_AsyncCall(&workRequest);
2058     }
2059     else
2060     {
2061         r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2062     }
2063
2064 lend:
2065     WININET_Release( &lpwfs->hdr );
2066
2067     return r;
2068 }
2069
2070 /***********************************************************************
2071  *           FTP_FtpRenameFileW  (Internal)
2072  *
2073  * Rename a file on the ftp server
2074  *
2075  * RETURNS
2076  *    TRUE on success
2077  *    FALSE on failure
2078  *
2079  */
2080 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2081 {
2082     INT nResCode;
2083     BOOL bSuccess = FALSE;
2084     appinfo_t *hIC = NULL;
2085
2086     TRACE("\n");
2087
2088     /* Clear any error information */
2089     INTERNET_SetLastError(0);
2090
2091     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2092         goto lend;
2093
2094     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2095     if (nResCode == 350)
2096     {
2097         if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2098             goto lend;
2099
2100         nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2101     }
2102
2103     if (nResCode == 250)
2104         bSuccess = TRUE;
2105     else
2106         FTP_SetResponseError(nResCode);
2107
2108 lend:
2109     hIC = lpwfs->lpAppInfo;
2110     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2111     {
2112         INTERNET_ASYNC_RESULT iar;
2113
2114         iar.dwResult = (DWORD)bSuccess;
2115         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2116         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2117             &iar, sizeof(INTERNET_ASYNC_RESULT));
2118     }
2119
2120     return bSuccess;
2121 }
2122
2123 /***********************************************************************
2124  *           FtpCommandA  (WININET.@)
2125  */
2126 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2127                          LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2128 {
2129     BOOL r;
2130     WCHAR *cmdW;
2131
2132     TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2133           debugstr_a(lpszCommand), dwContext, phFtpCommand);
2134
2135     if (fExpectResponse)
2136     {
2137         FIXME("data connection not supported\n");
2138         return FALSE;
2139     }
2140
2141     if (!lpszCommand || !lpszCommand[0])
2142     {
2143         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2144         return FALSE;
2145     }
2146
2147     if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2148     {
2149         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2150         return FALSE;
2151     }
2152
2153     r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2154
2155     HeapFree(GetProcessHeap(), 0, cmdW);
2156     return r;
2157 }
2158
2159 /***********************************************************************
2160  *           FtpCommandW  (WININET.@)
2161  */
2162 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2163                          LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2164 {
2165     BOOL r = FALSE;
2166     ftp_session_t *lpwfs;
2167     LPSTR cmd = NULL;
2168     DWORD len, nBytesSent= 0;
2169     INT nResCode, nRC = 0;
2170
2171     TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2172            debugstr_w(lpszCommand), dwContext, phFtpCommand);
2173
2174     if (!lpszCommand || !lpszCommand[0])
2175     {
2176         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2177         return FALSE;
2178     }
2179
2180     if (fExpectResponse)
2181     {
2182         FIXME("data connection not supported\n");
2183         return FALSE;
2184     }
2185
2186     lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
2187     if (!lpwfs)
2188     {
2189         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2190         return FALSE;
2191     }
2192
2193     if (WH_HFTPSESSION != lpwfs->hdr.htype)
2194     {
2195         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2196         goto lend;
2197     }
2198
2199     if (lpwfs->download_in_progress != NULL)
2200     {
2201         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2202         goto lend;
2203     }
2204
2205     len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2206     if ((cmd = HeapAlloc(GetProcessHeap(), 0, len )))
2207         WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2208     else
2209     {
2210         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2211         goto lend;
2212     }
2213
2214     strcat(cmd, szCRLF);
2215     len--;
2216
2217     TRACE("Sending (%s) len(%d)\n", cmd, len);
2218     while ((nBytesSent < len) && (nRC != -1))
2219     {
2220         nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2221         if (nRC != -1)
2222         {
2223             nBytesSent += nRC;
2224             TRACE("Sent %d bytes\n", nRC);
2225         }
2226     }
2227
2228     if (nBytesSent)
2229     {
2230         nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2231         if (nResCode > 0 && nResCode < 400)
2232             r = TRUE;
2233         else
2234             FTP_SetResponseError(nResCode);
2235     }
2236
2237 lend:
2238     WININET_Release( &lpwfs->hdr );
2239     HeapFree(GetProcessHeap(), 0, cmd);
2240     return r;
2241 }
2242
2243
2244 /***********************************************************************
2245  *           FTPSESSION_Destroy (internal)
2246  *
2247  * Deallocate session handle
2248  */
2249 static void FTPSESSION_Destroy(object_header_t *hdr)
2250 {
2251     ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2252
2253     TRACE("\n");
2254
2255     WININET_Release(&lpwfs->lpAppInfo->hdr);
2256
2257     HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2258     HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2259     HeapFree(GetProcessHeap(), 0, lpwfs);
2260 }
2261
2262 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2263 {
2264     ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2265
2266     TRACE("\n");
2267
2268     SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2269                       INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2270
2271     if (lpwfs->download_in_progress != NULL)
2272         lpwfs->download_in_progress->session_deleted = TRUE;
2273
2274      if (lpwfs->sndSocket != -1)
2275          closesocket(lpwfs->sndSocket);
2276
2277      if (lpwfs->lstnSocket != -1)
2278          closesocket(lpwfs->lstnSocket);
2279
2280     if (lpwfs->pasvSocket != -1)
2281         closesocket(lpwfs->pasvSocket);
2282
2283     SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2284                       INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2285 }
2286
2287 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2288 {
2289     switch(option) {
2290     case INTERNET_OPTION_HANDLE_TYPE:
2291         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2292
2293         if (*size < sizeof(ULONG))
2294             return ERROR_INSUFFICIENT_BUFFER;
2295
2296         *size = sizeof(DWORD);
2297         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2298         return ERROR_SUCCESS;
2299     }
2300
2301     return INET_QueryOption(option, buffer, size, unicode);
2302 }
2303
2304 static const object_vtbl_t FTPSESSIONVtbl = {
2305     FTPSESSION_Destroy,
2306     FTPSESSION_CloseConnection,
2307     FTPSESSION_QueryOption,
2308     NULL,
2309     NULL,
2310     NULL,
2311     NULL,
2312     NULL,
2313     NULL
2314 };
2315
2316
2317 /***********************************************************************
2318  *           FTP_Connect (internal)
2319  *
2320  * Connect to a ftp server
2321  *
2322  * RETURNS
2323  *   HINTERNET a session handle on success
2324  *   NULL on failure
2325  *
2326  * NOTES:
2327  *
2328  * Windows uses 'anonymous' as the username, when given a NULL username
2329  * and a NULL password. The password is first looked up in:
2330  *
2331  * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2332  *
2333  * If this entry is not present it uses the current username as the password.
2334  *
2335  */
2336
2337 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2338         INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2339         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2340         DWORD dwInternalFlags)
2341 {
2342     static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2343                                    'M','i','c','r','o','s','o','f','t','\\',
2344                                    'W','i','n','d','o','w','s','\\',
2345                                    'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2346                                    'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2347     static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2348     static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2349     static const WCHAR szEmpty[] = {'\0'};
2350     struct sockaddr_in socketAddr;
2351     INT nsocket = -1;
2352     UINT sock_namelen;
2353     BOOL bSuccess = FALSE;
2354     ftp_session_t *lpwfs = NULL;
2355     HINTERNET handle = NULL;
2356
2357     TRACE("%p  Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2358             hIC, debugstr_w(lpszServerName),
2359             nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2360
2361     assert( hIC->hdr.htype == WH_HINIT );
2362
2363     if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2364     {
2365         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2366         goto lerror;
2367     }
2368     
2369     lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(ftp_session_t));
2370     if (NULL == lpwfs)
2371     {
2372         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2373         goto lerror;
2374     }
2375
2376     if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2377         nServerPort = INTERNET_DEFAULT_FTP_PORT;
2378
2379     lpwfs->hdr.htype = WH_HFTPSESSION;
2380     lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
2381     lpwfs->hdr.dwFlags = dwFlags;
2382     lpwfs->hdr.dwContext = dwContext;
2383     lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2384     lpwfs->hdr.refs = 1;
2385     lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2386     lpwfs->download_in_progress = NULL;
2387     lpwfs->sndSocket = -1;
2388     lpwfs->lstnSocket = -1;
2389     lpwfs->pasvSocket = -1;
2390
2391     WININET_AddRef( &hIC->hdr );
2392     lpwfs->lpAppInfo = hIC;
2393     list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2394
2395     handle = WININET_AllocHandle( &lpwfs->hdr );
2396     if( !handle )
2397     {
2398         ERR("Failed to alloc handle\n");
2399         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2400         goto lerror;
2401     }
2402
2403     if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2404         if(strchrW(hIC->lpszProxy, ' '))
2405             FIXME("Several proxies not implemented.\n");
2406         if(hIC->lpszProxyBypass)
2407             FIXME("Proxy bypass is ignored.\n");
2408     }
2409     if (!lpszUserName || !strlenW(lpszUserName)) {
2410         HKEY key;
2411         WCHAR szPassword[MAX_PATH];
2412         DWORD len = sizeof(szPassword);
2413
2414         lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2415
2416         RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2417         if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2418             /* Nothing in the registry, get the username and use that as the password */
2419             if (!GetUserNameW(szPassword, &len)) {
2420                 /* Should never get here, but use an empty password as failsafe */
2421                 strcpyW(szPassword, szEmpty);
2422             }
2423         }
2424         RegCloseKey(key);
2425
2426         TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2427         lpwfs->lpszPassword = heap_strdupW(szPassword);
2428     }
2429     else {
2430         lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2431         lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2432     }
2433     
2434     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2435     if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2436     {
2437         INTERNET_ASYNC_RESULT iar;
2438
2439         iar.dwResult = (DWORD_PTR)handle;
2440         iar.dwError = ERROR_SUCCESS;
2441
2442         SendAsyncCallback(&hIC->hdr, dwContext,
2443                       INTERNET_STATUS_HANDLE_CREATED, &iar,
2444                       sizeof(INTERNET_ASYNC_RESULT));
2445     }
2446         
2447     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2448         (LPWSTR) lpszServerName, strlenW(lpszServerName));
2449
2450     sock_namelen = sizeof(socketAddr);
2451     if (!GetAddress(lpszServerName, nServerPort,
2452         (struct sockaddr *)&socketAddr, &sock_namelen))
2453     {
2454         INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2455         goto lerror;
2456     }
2457
2458     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2459         (LPWSTR) lpszServerName, strlenW(lpszServerName));
2460
2461     if (socketAddr.sin_family != AF_INET)
2462     {
2463         WARN("unsupported address family %d\n", socketAddr.sin_family);
2464         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2465         goto lerror;
2466     }
2467     nsocket = socket(AF_INET,SOCK_STREAM,0);
2468     if (nsocket == -1)
2469     {
2470         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2471         goto lerror;
2472     }
2473
2474     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2475         &socketAddr, sock_namelen);
2476
2477     if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2478     {
2479         ERR("Unable to connect (%s)\n", strerror(errno));
2480         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2481         closesocket(nsocket);
2482     }
2483     else
2484     {
2485         TRACE("Connected to server\n");
2486         lpwfs->sndSocket = nsocket;
2487         SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2488             &socketAddr, sock_namelen);
2489
2490         sock_namelen = sizeof(lpwfs->socketAddress);
2491         getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2492
2493         if (FTP_ConnectToHost(lpwfs))
2494         {
2495             TRACE("Successfully logged into server\n");
2496             bSuccess = TRUE;
2497         }
2498     }
2499
2500 lerror:
2501     if (lpwfs) WININET_Release( &lpwfs->hdr );
2502
2503     if (!bSuccess && handle)
2504     {
2505         WININET_FreeHandle( handle );
2506         handle = NULL;
2507     }
2508
2509     return handle;
2510 }
2511
2512
2513 /***********************************************************************
2514  *           FTP_ConnectToHost (internal)
2515  *
2516  * Connect to a ftp server
2517  *
2518  * RETURNS
2519  *   TRUE on success
2520  *   NULL on failure
2521  *
2522  */
2523 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2524 {
2525     INT nResCode;
2526     BOOL bSuccess = FALSE;
2527
2528     TRACE("\n");
2529     FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2530
2531     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2532         goto lend;
2533
2534     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2535     if (nResCode)
2536     {
2537         /* Login successful... */
2538         if (nResCode == 230)
2539             bSuccess = TRUE;
2540         /* User name okay, need password... */
2541         else if (nResCode == 331)
2542             bSuccess = FTP_SendPassword(lpwfs);
2543         /* Need account for login... */
2544         else if (nResCode == 332)
2545             bSuccess = FTP_SendAccount(lpwfs);
2546         else
2547             FTP_SetResponseError(nResCode);
2548     }
2549
2550     TRACE("Returning %d\n", bSuccess);
2551 lend:
2552     return bSuccess;
2553 }
2554
2555
2556 /***********************************************************************
2557  *           FTP_SendCommandA (internal)
2558  *
2559  * Send command to server
2560  *
2561  * RETURNS
2562  *   TRUE on success
2563  *   NULL on failure
2564  *
2565  */
2566 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2567         INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2568 {
2569         DWORD len;
2570         CHAR *buf;
2571         DWORD nBytesSent = 0;
2572         int nRC = 0;
2573         DWORD dwParamLen;
2574
2575         TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2576
2577         if (lpfnStatusCB)
2578         {
2579             lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2580         }
2581
2582         dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2583         len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2584         if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2585         {
2586             INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2587             return FALSE;
2588         }
2589         sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2590                 dwParamLen ? lpszParam : "", szCRLF);
2591
2592         TRACE("Sending (%s) len(%d)\n", buf, len);
2593         while((nBytesSent < len) && (nRC != -1))
2594         {
2595                 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2596                 nBytesSent += nRC;
2597         }
2598
2599         HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2600
2601         if (lpfnStatusCB)
2602         {
2603             lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2604                          &nBytesSent, sizeof(DWORD));
2605         }
2606
2607         TRACE("Sent %d bytes\n", nBytesSent);
2608         return (nRC != -1);
2609 }
2610
2611 /***********************************************************************
2612  *           FTP_SendCommand (internal)
2613  *
2614  * Send command to server
2615  *
2616  * RETURNS
2617  *   TRUE on success
2618  *   NULL on failure
2619  *
2620  */
2621 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2622         INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2623 {
2624     BOOL ret;
2625     LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2626     ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2627     HeapFree(GetProcessHeap(), 0, lpszParamA);
2628     return ret;
2629 }
2630
2631 /***********************************************************************
2632  *           FTP_ReceiveResponse (internal)
2633  *
2634  * Receive response from server
2635  *
2636  * RETURNS
2637  *   Reply code on success
2638  *   0 on failure
2639  *
2640  */
2641 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2642 {
2643     LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2644     DWORD nRecv;
2645     INT rc = 0;
2646     char firstprefix[5];
2647     BOOL multiline = FALSE;
2648
2649     TRACE("socket(%d)\n", lpwfs->sndSocket);
2650
2651     SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2652
2653     while(1)
2654     {
2655         if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2656             goto lerror;
2657
2658         if (nRecv >= 3)
2659         {
2660             if(!multiline)
2661             {
2662                 if(lpszResponse[3] != '-')
2663                     break;
2664                 else
2665                 {  /* Start of multiline response.  Loop until we get "nnn " */
2666                     multiline = TRUE;
2667                     memcpy(firstprefix, lpszResponse, 3);
2668                     firstprefix[3] = ' ';
2669                     firstprefix[4] = '\0';
2670                 }
2671             }
2672             else
2673             {
2674                 if(!memcmp(firstprefix, lpszResponse, 4))
2675                     break;
2676             }
2677         }
2678     }
2679
2680     if (nRecv >= 3)
2681     {
2682         rc = atoi(lpszResponse);
2683
2684         SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2685                     &nRecv, sizeof(DWORD));
2686     }
2687
2688 lerror:
2689     TRACE("return %d\n", rc);
2690     return rc;
2691 }
2692
2693
2694 /***********************************************************************
2695  *           FTP_SendPassword (internal)
2696  *
2697  * Send password to ftp server
2698  *
2699  * RETURNS
2700  *   TRUE on success
2701  *   NULL on failure
2702  *
2703  */
2704 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2705 {
2706     INT nResCode;
2707     BOOL bSuccess = FALSE;
2708
2709     TRACE("\n");
2710     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2711         goto lend;
2712
2713     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2714     if (nResCode)
2715     {
2716         TRACE("Received reply code %d\n", nResCode);
2717         /* Login successful... */
2718         if (nResCode == 230)
2719             bSuccess = TRUE;
2720         /* Command not implemented, superfluous at the server site... */
2721         /* Need account for login... */
2722         else if (nResCode == 332)
2723             bSuccess = FTP_SendAccount(lpwfs);
2724         else
2725             FTP_SetResponseError(nResCode);
2726     }
2727
2728 lend:
2729     TRACE("Returning %d\n", bSuccess);
2730     return bSuccess;
2731 }
2732
2733
2734 /***********************************************************************
2735  *           FTP_SendAccount (internal)
2736  *
2737  *
2738  *
2739  * RETURNS
2740  *   TRUE on success
2741  *   FALSE on failure
2742  *
2743  */
2744 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2745 {
2746     INT nResCode;
2747     BOOL bSuccess = FALSE;
2748
2749     TRACE("\n");
2750     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2751         goto lend;
2752
2753     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2754     if (nResCode)
2755         bSuccess = TRUE;
2756     else
2757         FTP_SetResponseError(nResCode);
2758
2759 lend:
2760     return bSuccess;
2761 }
2762
2763
2764 /***********************************************************************
2765  *           FTP_SendStore (internal)
2766  *
2767  * Send request to upload file to ftp server
2768  *
2769  * RETURNS
2770  *   TRUE on success
2771  *   FALSE on failure
2772  *
2773  */
2774 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2775 {
2776     INT nResCode;
2777     BOOL bSuccess = FALSE;
2778
2779     TRACE("\n");
2780     if (!FTP_InitListenSocket(lpwfs))
2781         goto lend;
2782
2783     if (!FTP_SendType(lpwfs, dwType))
2784         goto lend;
2785
2786     if (!FTP_SendPortOrPasv(lpwfs))
2787         goto lend;
2788
2789     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2790             goto lend;
2791     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2792     if (nResCode)
2793     {
2794         if (nResCode == 150 || nResCode == 125)
2795             bSuccess = TRUE;
2796         else
2797             FTP_SetResponseError(nResCode);
2798     }
2799
2800 lend:
2801     if (!bSuccess && lpwfs->lstnSocket != -1)
2802     {
2803         closesocket(lpwfs->lstnSocket);
2804         lpwfs->lstnSocket = -1;
2805     }
2806
2807     return bSuccess;
2808 }
2809
2810
2811 /***********************************************************************
2812  *           FTP_InitListenSocket (internal)
2813  *
2814  * Create a socket to listen for server response
2815  *
2816  * RETURNS
2817  *   TRUE on success
2818  *   FALSE on failure
2819  *
2820  */
2821 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2822 {
2823     BOOL bSuccess = FALSE;
2824     socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2825
2826     TRACE("\n");
2827
2828     lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2829     if (lpwfs->lstnSocket == -1)
2830     {
2831         TRACE("Unable to create listening socket\n");
2832             goto lend;
2833     }
2834
2835     /* We obtain our ip addr from the name of the command channel socket */
2836     lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2837
2838     /* and get the system to assign us a port */
2839     lpwfs->lstnSocketAddress.sin_port = htons(0);
2840
2841     if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2842     {
2843         TRACE("Unable to bind socket\n");
2844         goto lend;
2845     }
2846
2847     if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2848     {
2849         TRACE("listen failed\n");
2850         goto lend;
2851     }
2852
2853     if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2854         bSuccess = TRUE;
2855
2856 lend:
2857     if (!bSuccess && lpwfs->lstnSocket != -1)
2858     {
2859         closesocket(lpwfs->lstnSocket);
2860         lpwfs->lstnSocket = -1;
2861     }
2862
2863     return bSuccess;
2864 }
2865
2866
2867 /***********************************************************************
2868  *           FTP_SendType (internal)
2869  *
2870  * Tell server type of data being transferred
2871  *
2872  * RETURNS
2873  *   TRUE on success
2874  *   FALSE on failure
2875  *
2876  * W98SE doesn't cache the type that's currently set
2877  * (i.e. it sends it always),
2878  * so we probably don't want to do that either.
2879  */
2880 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2881 {
2882     INT nResCode;
2883     WCHAR type[] = { 'I','\0' };
2884     BOOL bSuccess = FALSE;
2885
2886     TRACE("\n");
2887     if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2888         type[0] = 'A';
2889
2890     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2891         goto lend;
2892
2893     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2894     if (nResCode)
2895     {
2896         if (nResCode == 2)
2897             bSuccess = TRUE;
2898         else
2899             FTP_SetResponseError(nResCode);
2900     }
2901
2902 lend:
2903     return bSuccess;
2904 }
2905
2906
2907 #if 0  /* FIXME: should probably be used for FtpGetFileSize */
2908 /***********************************************************************
2909  *           FTP_GetFileSize (internal)
2910  *
2911  * Retrieves from the server the size of the given file
2912  *
2913  * RETURNS
2914  *   TRUE on success
2915  *   FALSE on failure
2916  *
2917  */
2918 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2919 {
2920     INT nResCode;
2921     BOOL bSuccess = FALSE;
2922
2923     TRACE("\n");
2924
2925     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2926         goto lend;
2927
2928     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2929     if (nResCode)
2930     {
2931         if (nResCode == 213) {
2932             /* Now parses the output to get the actual file size */
2933             int i;
2934             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2935
2936             for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2937             if (lpszResponseBuffer[i] == '\0') return FALSE;
2938             *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2939             
2940             bSuccess = TRUE;
2941         } else {
2942             FTP_SetResponseError(nResCode);
2943         }
2944     }
2945
2946 lend:
2947     return bSuccess;
2948 }
2949 #endif
2950
2951
2952 /***********************************************************************
2953  *           FTP_SendPort (internal)
2954  *
2955  * Tell server which port to use
2956  *
2957  * RETURNS
2958  *   TRUE on success
2959  *   FALSE on failure
2960  *
2961  */
2962 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
2963 {
2964     static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2965     INT nResCode;
2966     WCHAR szIPAddress[64];
2967     BOOL bSuccess = FALSE;
2968     TRACE("\n");
2969
2970     sprintfW(szIPAddress, szIPFormat,
2971          lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2972         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2973         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2974         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2975         lpwfs->lstnSocketAddress.sin_port & 0xFF,
2976         (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2977
2978     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2979         goto lend;
2980
2981     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2982     if (nResCode)
2983     {
2984         if (nResCode == 200)
2985             bSuccess = TRUE;
2986         else
2987             FTP_SetResponseError(nResCode);
2988     }
2989
2990 lend:
2991     return bSuccess;
2992 }
2993
2994
2995 /***********************************************************************
2996  *           FTP_DoPassive (internal)
2997  *
2998  * Tell server that we want to do passive transfers
2999  * and connect data socket
3000  *
3001  * RETURNS
3002  *   TRUE on success
3003  *   FALSE on failure
3004  *
3005  */
3006 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3007 {
3008     INT nResCode;
3009     BOOL bSuccess = FALSE;
3010
3011     TRACE("\n");
3012     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3013         goto lend;
3014
3015     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3016     if (nResCode)
3017     {
3018         if (nResCode == 227)
3019         {
3020             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3021             LPSTR p;
3022             int f[6];
3023             int i;
3024             char *pAddr, *pPort;
3025             INT nsocket = -1;
3026             struct sockaddr_in dataSocketAddress;
3027
3028             p = lpszResponseBuffer+4; /* skip status code */
3029             while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3030
3031             if (*p == '\0')
3032             {
3033                 ERR("no address found in response, aborting\n");
3034                 goto lend;
3035             }
3036
3037             if (sscanf(p, "%d,%d,%d,%d,%d,%d",  &f[0], &f[1], &f[2], &f[3],
3038                                                 &f[4], &f[5]) != 6)
3039             {
3040                 ERR("unknown response address format '%s', aborting\n", p);
3041                 goto lend;
3042             }
3043             for (i=0; i < 6; i++)
3044                 f[i] = f[i] & 0xff;
3045
3046             dataSocketAddress = lpwfs->socketAddress;
3047             pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3048             pPort = (char *)&(dataSocketAddress.sin_port);
3049             pAddr[0] = f[0];
3050             pAddr[1] = f[1];
3051             pAddr[2] = f[2];
3052             pAddr[3] = f[3];
3053             pPort[0] = f[4];
3054             pPort[1] = f[5];
3055
3056             nsocket = socket(AF_INET,SOCK_STREAM,0);
3057             if (nsocket == -1)
3058                 goto lend;
3059
3060             if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3061             {
3062                 ERR("can't connect passive FTP data port.\n");
3063                 closesocket(nsocket);
3064                 goto lend;
3065             }
3066             lpwfs->pasvSocket = nsocket;
3067             bSuccess = TRUE;
3068         }
3069         else
3070             FTP_SetResponseError(nResCode);
3071     }
3072
3073 lend:
3074     return bSuccess;
3075 }
3076
3077
3078 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3079 {
3080     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3081     {
3082         if (!FTP_DoPassive(lpwfs))
3083             return FALSE;
3084     }
3085     else
3086     {
3087         if (!FTP_SendPort(lpwfs))
3088             return FALSE;
3089     }
3090     return TRUE;
3091 }
3092
3093
3094 /***********************************************************************
3095  *           FTP_GetDataSocket (internal)
3096  *
3097  * Either accepts an incoming data socket connection from the server
3098  * or just returns the already opened socket after a PASV command
3099  * in case of passive FTP.
3100  *
3101  *
3102  * RETURNS
3103  *   TRUE on success
3104  *   FALSE on failure
3105  *
3106  */
3107 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3108 {
3109     struct sockaddr_in saddr;
3110     socklen_t addrlen = sizeof(struct sockaddr);
3111
3112     TRACE("\n");
3113     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3114     {
3115         *nDataSocket = lpwfs->pasvSocket;
3116         lpwfs->pasvSocket = -1;
3117     }
3118     else
3119     {
3120         *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3121         closesocket(lpwfs->lstnSocket);
3122         lpwfs->lstnSocket = -1;
3123     }
3124     return *nDataSocket != -1;
3125 }
3126
3127
3128 /***********************************************************************
3129  *           FTP_SendData (internal)
3130  *
3131  * Send data to the server
3132  *
3133  * RETURNS
3134  *   TRUE on success
3135  *   FALSE on failure
3136  *
3137  */
3138 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3139 {
3140     BY_HANDLE_FILE_INFORMATION fi;
3141     DWORD nBytesRead = 0;
3142     DWORD nBytesSent = 0;
3143     DWORD nTotalSent = 0;
3144     DWORD nBytesToSend, nLen;
3145     int nRC = 1;
3146     time_t s_long_time, e_long_time;
3147     LONG nSeconds;
3148     CHAR *lpszBuffer;
3149
3150     TRACE("\n");
3151     lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3152
3153     /* Get the size of the file. */
3154     GetFileInformationByHandle(hFile, &fi);
3155     time(&s_long_time);
3156
3157     do
3158     {
3159         nBytesToSend = nBytesRead - nBytesSent;
3160
3161         if (nBytesToSend <= 0)
3162         {
3163             /* Read data from file. */
3164             nBytesSent = 0;
3165             if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3166             ERR("Failed reading from file\n");
3167
3168             if (nBytesRead > 0)
3169                 nBytesToSend = nBytesRead;
3170             else
3171                 break;
3172         }
3173
3174         nLen = DATA_PACKET_SIZE < nBytesToSend ?
3175             DATA_PACKET_SIZE : nBytesToSend;
3176         nRC  = send(nDataSocket, lpszBuffer, nLen, 0);
3177
3178         if (nRC != -1)
3179         {
3180             nBytesSent += nRC;
3181             nTotalSent += nRC;
3182         }
3183
3184         /* Do some computation to display the status. */
3185         time(&e_long_time);
3186         nSeconds = e_long_time - s_long_time;
3187         if( nSeconds / 60 > 0 )
3188         {
3189             TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3190             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3191             nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3192         }
3193         else
3194         {
3195             TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3196             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3197             (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3198         }
3199     } while (nRC != -1);
3200
3201     TRACE("file transfer complete!\n");
3202
3203     HeapFree(GetProcessHeap(), 0, lpszBuffer);
3204
3205     return nTotalSent;
3206 }
3207
3208
3209 /***********************************************************************
3210  *           FTP_SendRetrieve (internal)
3211  *
3212  * Send request to retrieve a file
3213  *
3214  * RETURNS
3215  *   Number of bytes to be received on success
3216  *   0 on failure
3217  *
3218  */
3219 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3220 {
3221     INT nResCode;
3222     BOOL ret;
3223
3224     TRACE("\n");
3225     if (!(ret = FTP_InitListenSocket(lpwfs)))
3226         goto lend;
3227
3228     if (!(ret = FTP_SendType(lpwfs, dwType)))
3229         goto lend;
3230
3231     if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3232         goto lend;
3233
3234     if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3235         goto lend;
3236
3237     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3238     if ((nResCode != 125) && (nResCode != 150)) {
3239         /* That means that we got an error getting the file. */
3240         FTP_SetResponseError(nResCode);
3241         ret = FALSE;
3242     }
3243
3244 lend:
3245     if (!ret && lpwfs->lstnSocket != -1)
3246     {
3247         closesocket(lpwfs->lstnSocket);
3248         lpwfs->lstnSocket = -1;
3249     }
3250
3251     return ret;
3252 }
3253
3254
3255 /***********************************************************************
3256  *           FTP_RetrieveData  (internal)
3257  *
3258  * Retrieve data from server
3259  *
3260  * RETURNS
3261  *   TRUE on success
3262  *   FALSE on failure
3263  *
3264  */
3265 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3266 {
3267     DWORD nBytesWritten;
3268     INT nRC = 0;
3269     CHAR *lpszBuffer;
3270
3271     TRACE("\n");
3272
3273     lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3274     if (NULL == lpszBuffer)
3275     {
3276         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3277         return FALSE;
3278     }
3279
3280     while (nRC != -1)
3281     {
3282         nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3283         if (nRC != -1)
3284         {
3285             /* other side closed socket. */
3286             if (nRC == 0)
3287                 goto recv_end;
3288             WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3289         }
3290     }
3291
3292     TRACE("Data transfer complete\n");
3293
3294 recv_end:
3295     HeapFree(GetProcessHeap(), 0, lpszBuffer);
3296
3297     return  (nRC != -1);
3298 }
3299
3300 /***********************************************************************
3301  *           FTPFINDNEXT_Destroy (internal)
3302  *
3303  * Deallocate session handle
3304  */
3305 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3306 {
3307     LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3308     DWORD i;
3309
3310     TRACE("\n");
3311
3312     WININET_Release(&lpwfn->lpFtpSession->hdr);
3313
3314     for (i = 0; i < lpwfn->size; i++)
3315     {
3316         HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3317     }
3318
3319     HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3320     HeapFree(GetProcessHeap(), 0, lpwfn);
3321 }
3322
3323 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3324 {
3325     WIN32_FIND_DATAW *find_data = data;
3326     DWORD res = ERROR_SUCCESS;
3327
3328     TRACE("index(%d) size(%d)\n", find->index, find->size);
3329
3330     ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3331
3332     if (find->index < find->size) {
3333         FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3334         find->index++;
3335
3336         TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3337     }else {
3338         res = ERROR_NO_MORE_FILES;
3339     }
3340
3341     if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3342     {
3343         INTERNET_ASYNC_RESULT iar;
3344
3345         iar.dwResult = (res == ERROR_SUCCESS);
3346         iar.dwError = res;
3347
3348         INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3349                               INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3350                               sizeof(INTERNET_ASYNC_RESULT));
3351     }
3352
3353     return res;
3354 }
3355
3356 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3357 {
3358     struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3359
3360     FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3361 }
3362
3363 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3364 {
3365     switch(option) {
3366     case INTERNET_OPTION_HANDLE_TYPE:
3367         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3368
3369         if (*size < sizeof(ULONG))
3370             return ERROR_INSUFFICIENT_BUFFER;
3371
3372         *size = sizeof(DWORD);
3373         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3374         return ERROR_SUCCESS;
3375     }
3376
3377     return INET_QueryOption(option, buffer, size, unicode);
3378 }
3379
3380 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3381 {
3382     WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3383
3384     if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3385     {
3386         WORKREQUEST workRequest;
3387         struct WORKREQ_FTPFINDNEXTW *req;
3388
3389         workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3390         workRequest.hdr = WININET_AddRef( &find->hdr );
3391         req = &workRequest.u.FtpFindNextW;
3392         req->lpFindFileData = data;
3393
3394         INTERNET_AsyncCall(&workRequest);
3395
3396         return ERROR_SUCCESS;
3397     }
3398
3399     return FTPFINDNEXT_FindNextFileProc(find, data);
3400 }
3401
3402 static const object_vtbl_t FTPFINDNEXTVtbl = {
3403     FTPFINDNEXT_Destroy,
3404     NULL,
3405     FTPFINDNEXT_QueryOption,
3406     NULL,
3407     NULL,
3408     NULL,
3409     NULL,
3410     NULL,
3411     NULL,
3412     FTPFINDNEXT_FindNextFileW
3413 };
3414
3415 /***********************************************************************
3416  *           FTP_ReceiveFileList (internal)
3417  *
3418  * Read file list from server
3419  *
3420  * RETURNS
3421  *   Handle to file list on success
3422  *   NULL on failure
3423  *
3424  */
3425 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3426         LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3427 {
3428     DWORD dwSize = 0;
3429     LPFILEPROPERTIESW lpafp = NULL;
3430     LPWININETFTPFINDNEXTW lpwfn = NULL;
3431     HINTERNET handle = 0;
3432
3433     TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3434
3435     if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3436     {
3437         if(lpFindFileData)
3438             FTP_ConvertFileProp(lpafp, lpFindFileData);
3439
3440         lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3441         if (lpwfn)
3442         {
3443             lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3444             lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3445             lpwfn->hdr.dwContext = dwContext;
3446             lpwfn->hdr.refs = 1;
3447             lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3448             lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3449             lpwfn->size = dwSize;
3450             lpwfn->lpafp = lpafp;
3451
3452             WININET_AddRef( &lpwfs->hdr );
3453             lpwfn->lpFtpSession = lpwfs;
3454             list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3455
3456             handle = WININET_AllocHandle( &lpwfn->hdr );
3457         }
3458     }
3459
3460     if( lpwfn )
3461         WININET_Release( &lpwfn->hdr );
3462
3463     TRACE("Matched %d files\n", dwSize);
3464     return handle;
3465 }
3466
3467
3468 /***********************************************************************
3469  *           FTP_ConvertFileProp (internal)
3470  *
3471  * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3472  *
3473  * RETURNS
3474  *   TRUE on success
3475  *   FALSE on failure
3476  *
3477  */
3478 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3479 {
3480     BOOL bSuccess = FALSE;
3481
3482     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3483
3484     if (lpafp)
3485     {
3486         SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3487         lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3488         lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3489         
3490         /* Not all fields are filled in */
3491         lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3492         lpFindFileData->nFileSizeLow = lpafp->nSize;
3493
3494         if (lpafp->bIsDirectory)
3495             lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3496
3497         if (lpafp->lpszName)
3498             lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3499
3500         bSuccess = TRUE;
3501     }
3502
3503     return bSuccess;
3504 }
3505
3506 /***********************************************************************
3507  *           FTP_ParseNextFile (internal)
3508  *
3509  * Parse the next line in file listing
3510  *
3511  * RETURNS
3512  *   TRUE on success
3513  *   FALSE on failure
3514  */
3515 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3516 {
3517     static const char szSpace[] = " \t";
3518     DWORD nBufLen;
3519     char *pszLine;
3520     char *pszToken;
3521     char *pszTmp;
3522     BOOL found = FALSE;
3523     int i;
3524     
3525     lpfp->lpszName = NULL;
3526     do {
3527         if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3528             return FALSE;
3529     
3530         pszToken = strtok(pszLine, szSpace);
3531         /* ls format
3532          * <Permissions> <NoLinks> <owner>   <group> <size> <date>  <time or year> <filename>
3533          *
3534          * For instance:
3535          * drwx--s---     2         pcarrier  ens     512    Sep 28  1995           pcarrier
3536          */
3537         if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3538             if(!FTP_ParsePermission(pszToken, lpfp))
3539                 lpfp->bIsDirectory = FALSE;
3540             for(i=0; i<=3; i++) {
3541               if(!(pszToken = strtok(NULL, szSpace)))
3542                   break;
3543             }
3544             if(!pszToken) continue;
3545             if(lpfp->bIsDirectory) {
3546                 TRACE("Is directory\n");
3547                 lpfp->nSize = 0;
3548             }
3549             else {
3550                 TRACE("Size: %s\n", pszToken);
3551                 lpfp->nSize = atol(pszToken);
3552             }
3553             
3554             lpfp->tmLastModified.wSecond = 0;
3555             lpfp->tmLastModified.wMinute = 0;
3556             lpfp->tmLastModified.wHour   = 0;
3557             lpfp->tmLastModified.wDay    = 0;
3558             lpfp->tmLastModified.wMonth  = 0;
3559             lpfp->tmLastModified.wYear   = 0;
3560             
3561             /* Determine month */
3562             pszToken = strtok(NULL, szSpace);
3563             if(!pszToken) continue;
3564             if(strlen(pszToken) >= 3) {
3565                 pszToken[3] = 0;
3566                 if((pszTmp = StrStrIA(szMonths, pszToken)))
3567                     lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3568             }
3569             /* Determine day */
3570             pszToken = strtok(NULL, szSpace);
3571             if(!pszToken) continue;
3572             lpfp->tmLastModified.wDay = atoi(pszToken);
3573             /* Determine time or year */
3574             pszToken = strtok(NULL, szSpace);
3575             if(!pszToken) continue;
3576             if((pszTmp = strchr(pszToken, ':'))) {
3577                 SYSTEMTIME curr_time;
3578                 *pszTmp = 0;
3579                 pszTmp++;
3580                 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3581                 lpfp->tmLastModified.wHour = atoi(pszToken);
3582                 GetLocalTime( &curr_time );
3583                 lpfp->tmLastModified.wYear = curr_time.wYear;
3584             }
3585             else {
3586                 lpfp->tmLastModified.wYear = atoi(pszToken);
3587                 lpfp->tmLastModified.wHour = 12;
3588             }
3589             TRACE("Mod time: %02d:%02d:%02d  %04d/%02d/%02d\n",
3590                   lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3591                   lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3592
3593             pszToken = strtok(NULL, szSpace);
3594             if(!pszToken) continue;
3595             lpfp->lpszName = heap_strdupAtoW(pszToken);
3596             TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3597         }
3598         /* NT way of parsing ... :
3599             
3600                 07-13-03  08:55PM       <DIR>          sakpatch
3601                 05-09-03  06:02PM             12656686 2003-04-21bgm_cmd_e.rgz
3602         */
3603         else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3604             int mon, mday, year, hour, min;
3605             lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3606             
3607             sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3608             lpfp->tmLastModified.wDay   = mday;
3609             lpfp->tmLastModified.wMonth = mon;
3610             lpfp->tmLastModified.wYear  = year;
3611
3612             /* Hacky and bad Y2K protection :-) */
3613             if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3614
3615             pszToken = strtok(NULL, szSpace);
3616             if(!pszToken) continue;
3617             sscanf(pszToken, "%d:%d", &hour, &min);
3618             lpfp->tmLastModified.wHour   = hour;
3619             lpfp->tmLastModified.wMinute = min;
3620             if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3621                 lpfp->tmLastModified.wHour += 12;
3622             }
3623             lpfp->tmLastModified.wSecond = 0;
3624
3625             TRACE("Mod time: %02d:%02d:%02d  %04d/%02d/%02d\n",
3626                   lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3627                   lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3628
3629             pszToken = strtok(NULL, szSpace);
3630             if(!pszToken) continue;
3631             if(!strcasecmp(pszToken, "<DIR>")) {
3632                 lpfp->bIsDirectory = TRUE;
3633                 lpfp->nSize = 0;
3634                 TRACE("Is directory\n");
3635             }
3636             else {
3637                 lpfp->bIsDirectory = FALSE;
3638                 lpfp->nSize = atol(pszToken);
3639                 TRACE("Size: %d\n", lpfp->nSize);
3640             }
3641             
3642             pszToken = strtok(NULL, szSpace);
3643             if(!pszToken) continue;
3644             lpfp->lpszName = heap_strdupAtoW(pszToken);
3645             TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3646         }
3647         /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3648         else if(pszToken[0] == '+') {
3649             FIXME("EPLF Format not implemented\n");
3650         }
3651         
3652         if(lpfp->lpszName) {
3653             if((lpszSearchFile == NULL) ||
3654                (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3655                 found = TRUE;
3656                 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3657             }
3658             else {
3659                 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3660                 lpfp->lpszName = NULL;
3661             }
3662         }
3663     } while(!found);
3664     return TRUE;
3665 }
3666
3667 /***********************************************************************
3668  *           FTP_ParseDirectory (internal)
3669  *
3670  * Parse string of directory information
3671  *
3672  * RETURNS
3673  *   TRUE on success
3674  *   FALSE on failure
3675  */
3676 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3677     LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3678 {
3679     BOOL bSuccess = TRUE;
3680     INT sizeFilePropArray = 500;/*20; */
3681     INT indexFilePropArray = -1;
3682
3683     TRACE("\n");
3684
3685     /* Allocate initial file properties array */
3686     *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3687     if (!*lpafp)
3688         return FALSE;
3689
3690     do {
3691         if (indexFilePropArray+1 >= sizeFilePropArray)
3692         {
3693             LPFILEPROPERTIESW tmpafp;
3694             
3695             sizeFilePropArray *= 2;
3696             tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3697                 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3698             if (NULL == tmpafp)
3699             {
3700                 bSuccess = FALSE;
3701                 break;
3702             }
3703
3704             *lpafp = tmpafp;
3705         }
3706         indexFilePropArray++;
3707     } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3708
3709     if (bSuccess && indexFilePropArray)
3710     {
3711         if (indexFilePropArray < sizeFilePropArray - 1)
3712         {
3713             LPFILEPROPERTIESW tmpafp;
3714
3715             tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3716                 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3717             if (NULL != tmpafp)
3718                 *lpafp = tmpafp;
3719         }
3720         *dwfp = indexFilePropArray;
3721     }
3722     else
3723     {
3724         HeapFree(GetProcessHeap(), 0, *lpafp);
3725         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3726         bSuccess = FALSE;
3727     }
3728
3729     return bSuccess;
3730 }
3731
3732
3733 /***********************************************************************
3734  *           FTP_ParsePermission (internal)
3735  *
3736  * Parse permission string of directory information
3737  *
3738  * RETURNS
3739  *   TRUE on success
3740  *   FALSE on failure
3741  *
3742  */
3743 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3744 {
3745     BOOL bSuccess = TRUE;
3746     unsigned short nPermission = 0;
3747     INT nPos = 1;
3748     INT nLast  = 9;
3749
3750     TRACE("\n");
3751     if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3752     {
3753         bSuccess = FALSE;
3754         return bSuccess;
3755     }
3756
3757     lpfp->bIsDirectory = (*lpszPermission == 'd');
3758     do
3759     {
3760         switch (nPos)
3761         {
3762             case 1:
3763                 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3764                 break;
3765             case 2:
3766                 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3767                 break;
3768             case 3:
3769                 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3770                 break;
3771             case 4:
3772                 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3773                 break;
3774             case 5:
3775                 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3776                 break;
3777             case 6:
3778                 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3779                 break;
3780             case 7:
3781                 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3782                 break;
3783             case 8:
3784                 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3785                 break;
3786             case 9:
3787                 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3788                 break;
3789         }
3790         nPos++;
3791     }while (nPos <= nLast);
3792
3793     lpfp->permissions = nPermission;
3794     return bSuccess;
3795 }
3796
3797
3798 /***********************************************************************
3799  *           FTP_SetResponseError (internal)
3800  *
3801  * Set the appropriate error code for a given response from the server
3802  *
3803  * RETURNS
3804  *
3805  */
3806 static DWORD FTP_SetResponseError(DWORD dwResponse)
3807 {
3808     DWORD dwCode = 0;
3809
3810     switch(dwResponse)
3811     {
3812     case 425: /* Cannot open data connection. */
3813         dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3814         break;
3815
3816     case 426: /* Connection closed, transer aborted. */
3817         dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3818         break;
3819
3820     case 530: /* Not logged in. Login incorrect. */
3821         dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3822         break;
3823
3824     case 421: /* Service not available - Server may be shutting down. */
3825     case 450: /* File action not taken. File may be busy. */
3826     case 451: /* Action aborted. Server error. */
3827     case 452: /* Action not taken. Insufficient storage space on server. */
3828     case 500: /* Syntax error. Command unrecognized. */
3829     case 501: /* Syntax error. Error in parameters or arguments. */
3830     case 502: /* Command not implemented. */
3831     case 503: /* Bad sequence of commands. */
3832     case 504: /* Command not implemented for that parameter. */
3833     case 532: /* Need account for storing files */
3834     case 550: /* File action not taken. File not found or no access. */
3835     case 551: /* Requested action aborted. Page type unknown */
3836     case 552: /* Action aborted. Exceeded storage allocation */
3837     case 553: /* Action not taken. File name not allowed. */
3838
3839     default:
3840         dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3841         break;
3842     }
3843
3844     INTERNET_SetLastError(dwCode);
3845     return dwCode;
3846 }