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