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