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