ole32: Fix testing of HRESULT types with not operator instead of comparing against...
[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     return INET_QueryOption(option, buffer, size, unicode);
1231 }
1232
1233 static DWORD FTPFILE_ReadFile(WININETHANDLEHEADER *hdr, void *buffer, DWORD size, DWORD *read)
1234 {
1235     WININETFTPFILE *file = (WININETFTPFILE*)hdr;
1236     int res;
1237
1238     if (file->nDataSocket == -1)
1239         return ERROR_INTERNET_DISCONNECTED;
1240
1241     /* FIXME: FTP should use NETCON_ stuff */
1242     res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1243     *read = res>0 ? res : 0;
1244
1245     return res>=0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME*/
1246 }
1247
1248 static BOOL FTPFILE_WriteFile(WININETHANDLEHEADER *hdr, const void *buffer, DWORD size, DWORD *written)
1249 {
1250     LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
1251     int res;
1252
1253     res = send(lpwh->nDataSocket, buffer, size, 0);
1254
1255     *written = res>0 ? res : 0;
1256     return res >= 0;
1257 }
1258
1259 static const HANDLEHEADERVtbl FTPFILEVtbl = {
1260     FTPFILE_Destroy,
1261     NULL,
1262     FTPFILE_QueryOption,
1263     NULL,
1264     FTPFILE_ReadFile,
1265     NULL,
1266     FTPFILE_WriteFile,
1267     NULL,
1268     NULL
1269 };
1270
1271 /***********************************************************************
1272  *           FTP_FtpOpenFileW (Internal)
1273  *
1274  * Open a remote file for writing or reading
1275  *
1276  * RETURNS
1277  *    HINTERNET handle on success
1278  *    NULL on failure
1279  *
1280  */
1281 HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
1282         LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1283         DWORD_PTR dwContext)
1284 {
1285     INT nDataSocket;
1286     BOOL bSuccess = FALSE;
1287     LPWININETFTPFILE lpwh = NULL;
1288     LPWININETAPPINFOW hIC = NULL;
1289     HINTERNET handle = NULL;
1290
1291     TRACE("\n");
1292
1293     /* Clear any error information */
1294     INTERNET_SetLastError(0);
1295
1296     if (GENERIC_READ == fdwAccess)
1297     {
1298         /* Set up socket to retrieve data */
1299         bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1300     }
1301     else if (GENERIC_WRITE == fdwAccess)
1302     {
1303         /* Set up socket to send data */
1304         bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1305     }
1306
1307     /* Get data socket to server */
1308     if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1309     {
1310         lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPFILE));
1311         lpwh->hdr.htype = WH_HFILE;
1312         lpwh->hdr.vtbl = &FTPFILEVtbl;
1313         lpwh->hdr.dwFlags = dwFlags;
1314         lpwh->hdr.dwContext = dwContext;
1315         lpwh->hdr.refs = 1;
1316         lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1317         lpwh->nDataSocket = nDataSocket;
1318         lpwh->session_deleted = FALSE;
1319
1320         WININET_AddRef( &lpwfs->hdr );
1321         lpwh->lpFtpSession = lpwfs;
1322         list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1323         
1324         handle = WININET_AllocHandle( &lpwh->hdr );
1325         if( !handle )
1326             goto lend;
1327
1328         /* Indicate that a download is currently in progress */
1329         lpwfs->download_in_progress = lpwh;
1330     }
1331
1332     if (lpwfs->lstnSocket != -1)
1333         closesocket(lpwfs->lstnSocket);
1334
1335     hIC = lpwfs->lpAppInfo;
1336     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1337     {
1338         INTERNET_ASYNC_RESULT iar;
1339
1340         if (lpwh)
1341         {
1342             iar.dwResult = (DWORD)handle;
1343             iar.dwError = ERROR_SUCCESS;
1344             SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1345                 &iar, sizeof(INTERNET_ASYNC_RESULT));
1346         }
1347
1348         iar.dwResult = (DWORD)bSuccess;
1349         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1350         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1351             &iar, sizeof(INTERNET_ASYNC_RESULT));
1352     }
1353
1354 lend:
1355     if( lpwh )
1356         WININET_Release( &lpwh->hdr );
1357
1358     return handle;
1359 }
1360
1361
1362 /***********************************************************************
1363  *           FtpGetFileA (WININET.@)
1364  *
1365  * Retrieve file from the FTP server
1366  *
1367  * RETURNS
1368  *    TRUE on success
1369  *    FALSE on failure
1370  *
1371  */
1372 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1373     BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1374     DWORD_PTR dwContext)
1375 {
1376     LPWSTR lpwzRemoteFile;
1377     LPWSTR lpwzNewFile;
1378     BOOL ret;
1379     
1380     lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
1381     lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
1382     ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1383         dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1384     HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1385     HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1386     return ret;
1387 }
1388
1389
1390 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1391 {
1392     struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1393     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1394
1395     TRACE("%p\n", lpwfs);
1396
1397     FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1398              req->lpszNewFile, req->fFailIfExists,
1399              req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1400     HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1401     HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1402 }
1403
1404
1405 /***********************************************************************
1406  *           FtpGetFileW (WININET.@)
1407  *
1408  * Retrieve file from the FTP server
1409  *
1410  * RETURNS
1411  *    TRUE on success
1412  *    FALSE on failure
1413  *
1414  */
1415 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1416     BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1417     DWORD_PTR dwContext)
1418 {
1419     LPWININETFTPSESSIONW lpwfs;
1420     LPWININETAPPINFOW hIC = NULL;
1421     BOOL r = FALSE;
1422
1423     if (!lpszRemoteFile || !lpszNewFile)
1424     {
1425         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1426         return FALSE;
1427     }
1428
1429     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
1430     if (!lpwfs)
1431     {
1432         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1433         return FALSE;
1434     }
1435
1436     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1437     {
1438         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1439         goto lend;
1440     }
1441
1442     if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1443     {
1444         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1445         goto lend;
1446     }
1447
1448     if (lpwfs->download_in_progress != NULL)
1449     {
1450         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1451         goto lend;
1452     }
1453     
1454     hIC = lpwfs->lpAppInfo;
1455     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1456     {
1457         WORKREQUEST workRequest;
1458         struct WORKREQ_FTPGETFILEW *req;
1459
1460         workRequest.asyncproc = AsyncFtpGetFileProc;
1461         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1462         req = &workRequest.u.FtpGetFileW;
1463         req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
1464         req->lpszNewFile = WININET_strdupW(lpszNewFile);
1465         req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1466         req->fFailIfExists = fFailIfExists;
1467         req->dwFlags = dwInternetFlags;
1468         req->dwContext = dwContext;
1469
1470         r = INTERNET_AsyncCall(&workRequest);
1471     }
1472     else
1473     {
1474         r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1475            fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1476     }
1477
1478 lend:
1479     WININET_Release( &lpwfs->hdr );
1480
1481     return r;
1482 }
1483
1484
1485 /***********************************************************************
1486  *           FTP_FtpGetFileW (Internal)
1487  *
1488  * Retrieve file from the FTP server
1489  *
1490  * RETURNS
1491  *    TRUE on success
1492  *    FALSE on failure
1493  *
1494  */
1495 static BOOL FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1496         BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1497         DWORD_PTR dwContext)
1498 {
1499     BOOL bSuccess = FALSE;
1500     HANDLE hFile;
1501     LPWININETAPPINFOW hIC = NULL;
1502
1503     TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1504
1505     /* Clear any error information */
1506     INTERNET_SetLastError(0);
1507
1508     /* Ensure we can write to lpszNewfile by opening it */
1509     hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1510         CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1511     if (INVALID_HANDLE_VALUE == hFile)
1512         return FALSE;
1513
1514     /* Set up socket to retrieve data */
1515     if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1516     {
1517         INT nDataSocket;
1518
1519         /* Get data socket to server */
1520         if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1521         {
1522             INT nResCode;
1523
1524             /* Receive data */
1525             FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1526             closesocket(nDataSocket);
1527
1528             nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1529             if (nResCode)
1530             {
1531                 if (nResCode == 226)
1532                     bSuccess = TRUE;
1533                 else
1534                     FTP_SetResponseError(nResCode);
1535             }
1536         }
1537     }
1538
1539     if (lpwfs->lstnSocket != -1)
1540         closesocket(lpwfs->lstnSocket);
1541
1542     CloseHandle(hFile);
1543
1544     hIC = lpwfs->lpAppInfo;
1545     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1546     {
1547         INTERNET_ASYNC_RESULT iar;
1548
1549         iar.dwResult = (DWORD)bSuccess;
1550         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1551         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1552             &iar, sizeof(INTERNET_ASYNC_RESULT));
1553     }
1554
1555     if (!bSuccess) DeleteFileW(lpszNewFile);
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     return INET_QueryOption(option, buffer, size, unicode);
2198 }
2199
2200 static const HANDLEHEADERVtbl FTPSESSIONVtbl = {
2201     FTPSESSION_Destroy,
2202     FTPSESSION_CloseConnection,
2203     FTPSESSION_QueryOption,
2204     NULL,
2205     NULL,
2206     NULL,
2207     NULL,
2208     NULL,
2209     NULL
2210 };
2211
2212
2213 /***********************************************************************
2214  *           FTP_Connect (internal)
2215  *
2216  * Connect to a ftp server
2217  *
2218  * RETURNS
2219  *   HINTERNET a session handle on success
2220  *   NULL on failure
2221  *
2222  * NOTES:
2223  *
2224  * Windows uses 'anonymous' as the username, when given a NULL username
2225  * and a NULL password. The password is first looked up in:
2226  *
2227  * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2228  *
2229  * If this entry is not present it uses the current username as the password.
2230  *
2231  */
2232
2233 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
2234         INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2235         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2236         DWORD dwInternalFlags)
2237 {
2238     static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2239                                    'M','i','c','r','o','s','o','f','t','\\',
2240                                    'W','i','n','d','o','w','s','\\',
2241                                    'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2242                                    'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2243     static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2244     static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2245     static const WCHAR szEmpty[] = {'\0'};
2246     struct sockaddr_in socketAddr;
2247     INT nsocket = -1;
2248     UINT sock_namelen;
2249     BOOL bSuccess = FALSE;
2250     LPWININETFTPSESSIONW lpwfs = NULL;
2251     HINTERNET handle = NULL;
2252
2253     TRACE("%p  Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2254             hIC, debugstr_w(lpszServerName),
2255             nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2256
2257     assert( hIC->hdr.htype == WH_HINIT );
2258
2259     if (NULL == lpszUserName && NULL != lpszPassword)
2260     {
2261         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2262         goto lerror;
2263     }
2264     
2265     lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
2266     if (NULL == lpwfs)
2267     {
2268         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2269         goto lerror;
2270     }
2271
2272     if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2273         nServerPort = INTERNET_DEFAULT_FTP_PORT;
2274
2275     lpwfs->hdr.htype = WH_HFTPSESSION;
2276     lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
2277     lpwfs->hdr.dwFlags = dwFlags;
2278     lpwfs->hdr.dwContext = dwContext;
2279     lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2280     lpwfs->hdr.refs = 1;
2281     lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2282     lpwfs->download_in_progress = NULL;
2283     lpwfs->sndSocket = -1;
2284     lpwfs->lstnSocket = -1;
2285     lpwfs->pasvSocket = -1;
2286
2287     WININET_AddRef( &hIC->hdr );
2288     lpwfs->lpAppInfo = hIC;
2289     list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2290
2291     handle = WININET_AllocHandle( &lpwfs->hdr );
2292     if( !handle )
2293     {
2294         ERR("Failed to alloc handle\n");
2295         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2296         goto lerror;
2297     }
2298
2299     if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2300         if(strchrW(hIC->lpszProxy, ' '))
2301             FIXME("Several proxies not implemented.\n");
2302         if(hIC->lpszProxyBypass)
2303             FIXME("Proxy bypass is ignored.\n");
2304     }
2305     if ( !lpszUserName) {
2306         HKEY key;
2307         WCHAR szPassword[MAX_PATH];
2308         DWORD len = sizeof(szPassword);
2309
2310         lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
2311
2312         RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2313         if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2314             /* Nothing in the registry, get the username and use that as the password */
2315             if (!GetUserNameW(szPassword, &len)) {
2316                 /* Should never get here, but use an empty password as failsafe */
2317                 strcpyW(szPassword, szEmpty);
2318             }
2319         }
2320         RegCloseKey(key);
2321
2322         TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2323         lpwfs->lpszPassword = WININET_strdupW(szPassword);
2324     }
2325     else {
2326         lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
2327
2328         if (lpszPassword)
2329             lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
2330         else
2331             lpwfs->lpszPassword = WININET_strdupW(szEmpty);
2332     }
2333     
2334     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2335     if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2336     {
2337         INTERNET_ASYNC_RESULT iar;
2338
2339         iar.dwResult = (DWORD)handle;
2340         iar.dwError = ERROR_SUCCESS;
2341
2342         SendAsyncCallback(&hIC->hdr, dwContext,
2343                       INTERNET_STATUS_HANDLE_CREATED, &iar,
2344                       sizeof(INTERNET_ASYNC_RESULT));
2345     }
2346         
2347     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2348         (LPWSTR) lpszServerName, strlenW(lpszServerName));
2349
2350     if (!GetAddress(lpszServerName, nServerPort, &socketAddr))
2351     {
2352         INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2353         goto lerror;
2354     }
2355
2356     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2357         (LPWSTR) lpszServerName, strlenW(lpszServerName));
2358
2359     nsocket = socket(AF_INET,SOCK_STREAM,0);
2360     if (nsocket == -1)
2361     {
2362         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2363         goto lerror;
2364     }
2365
2366     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2367         &socketAddr, sizeof(struct sockaddr_in));
2368
2369     if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
2370     {
2371         ERR("Unable to connect (%s)\n", strerror(errno));
2372         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2373     }
2374     else
2375     {
2376         TRACE("Connected to server\n");
2377         lpwfs->sndSocket = nsocket;
2378         SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2379             &socketAddr, sizeof(struct sockaddr_in));
2380
2381         sock_namelen = sizeof(lpwfs->socketAddress);
2382         getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2383
2384         if (FTP_ConnectToHost(lpwfs))
2385         {
2386             TRACE("Successfully logged into server\n");
2387             bSuccess = TRUE;
2388         }
2389     }
2390
2391 lerror:
2392     if (lpwfs) WININET_Release( &lpwfs->hdr );
2393
2394     if (!bSuccess && handle)
2395     {
2396         WININET_FreeHandle( handle );
2397         handle = NULL;
2398     }
2399
2400     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2401     {
2402         INTERNET_ASYNC_RESULT iar;
2403
2404         iar.dwResult = bSuccess ? (DWORD_PTR)lpwfs : 0;
2405         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2406         SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2407             &iar, sizeof(INTERNET_ASYNC_RESULT));
2408     }
2409
2410     return handle;
2411 }
2412
2413
2414 /***********************************************************************
2415  *           FTP_ConnectToHost (internal)
2416  *
2417  * Connect to a ftp server
2418  *
2419  * RETURNS
2420  *   TRUE on success
2421  *   NULL on failure
2422  *
2423  */
2424 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
2425 {
2426     INT nResCode;
2427     BOOL bSuccess = FALSE;
2428
2429     TRACE("\n");
2430     FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2431
2432     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2433         goto lend;
2434
2435     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2436     if (nResCode)
2437     {
2438         /* Login successful... */
2439         if (nResCode == 230)
2440             bSuccess = TRUE;
2441         /* User name okay, need password... */
2442         else if (nResCode == 331)
2443             bSuccess = FTP_SendPassword(lpwfs);
2444         /* Need account for login... */
2445         else if (nResCode == 332)
2446             bSuccess = FTP_SendAccount(lpwfs);
2447         else
2448             FTP_SetResponseError(nResCode);
2449     }
2450
2451     TRACE("Returning %d\n", bSuccess);
2452 lend:
2453     return bSuccess;
2454 }
2455
2456
2457 /***********************************************************************
2458  *           FTP_SendCommandA (internal)
2459  *
2460  * Send command to server
2461  *
2462  * RETURNS
2463  *   TRUE on success
2464  *   NULL on failure
2465  *
2466  */
2467 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2468         INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
2469 {
2470         DWORD len;
2471         CHAR *buf;
2472         DWORD nBytesSent = 0;
2473         int nRC = 0;
2474         DWORD dwParamLen;
2475
2476         TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2477
2478         if (lpfnStatusCB)
2479         {
2480             lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2481         }
2482
2483         dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2484         len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2485         if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2486         {
2487             INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2488             return FALSE;
2489         }
2490         sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2491                 dwParamLen ? lpszParam : "", szCRLF);
2492
2493         TRACE("Sending (%s) len(%d)\n", buf, len);
2494         while((nBytesSent < len) && (nRC != -1))
2495         {
2496                 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2497                 nBytesSent += nRC;
2498         }
2499
2500         HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2501
2502         if (lpfnStatusCB)
2503         {
2504             lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2505                          &nBytesSent, sizeof(DWORD));
2506         }
2507
2508         TRACE("Sent %d bytes\n", nBytesSent);
2509         return (nRC != -1);
2510 }
2511
2512 /***********************************************************************
2513  *           FTP_SendCommand (internal)
2514  *
2515  * Send command to server
2516  *
2517  * RETURNS
2518  *   TRUE on success
2519  *   NULL on failure
2520  *
2521  */
2522 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2523         INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
2524 {
2525     BOOL ret;
2526     LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
2527     ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2528     HeapFree(GetProcessHeap(), 0, lpszParamA);
2529     return ret;
2530 }
2531
2532 /***********************************************************************
2533  *           FTP_ReceiveResponse (internal)
2534  *
2535  * Receive response from server
2536  *
2537  * RETURNS
2538  *   Reply code on success
2539  *   0 on failure
2540  *
2541  */
2542 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD_PTR dwContext)
2543 {
2544     LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2545     DWORD nRecv;
2546     INT rc = 0;
2547     char firstprefix[5];
2548     BOOL multiline = FALSE;
2549
2550     TRACE("socket(%d)\n", lpwfs->sndSocket);
2551
2552     SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2553
2554     while(1)
2555     {
2556         if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2557             goto lerror;
2558
2559         if (nRecv >= 3)
2560         {
2561             if(!multiline)
2562             {
2563                 if(lpszResponse[3] != '-')
2564                     break;
2565                 else
2566                 {  /* Start of multiline response.  Loop until we get "nnn " */
2567                     multiline = TRUE;
2568                     memcpy(firstprefix, lpszResponse, 3);
2569                     firstprefix[3] = ' ';
2570                     firstprefix[4] = '\0';
2571                 }
2572             }
2573             else
2574             {
2575                 if(!memcmp(firstprefix, lpszResponse, 4))
2576                     break;
2577             }
2578         }
2579     }
2580
2581     if (nRecv >= 3)
2582     {
2583         rc = atoi(lpszResponse);
2584
2585         SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2586                     &nRecv, sizeof(DWORD));
2587     }
2588
2589 lerror:
2590     TRACE("return %d\n", rc);
2591     return rc;
2592 }
2593
2594
2595 /***********************************************************************
2596  *           FTP_SendPassword (internal)
2597  *
2598  * Send password to ftp server
2599  *
2600  * RETURNS
2601  *   TRUE on success
2602  *   NULL on failure
2603  *
2604  */
2605 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2606 {
2607     INT nResCode;
2608     BOOL bSuccess = FALSE;
2609
2610     TRACE("\n");
2611     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2612         goto lend;
2613
2614     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2615     if (nResCode)
2616     {
2617         TRACE("Received reply code %d\n", nResCode);
2618         /* Login successful... */
2619         if (nResCode == 230)
2620             bSuccess = TRUE;
2621         /* Command not implemented, superfluous at the server site... */
2622         /* Need account for login... */
2623         else if (nResCode == 332)
2624             bSuccess = FTP_SendAccount(lpwfs);
2625         else
2626             FTP_SetResponseError(nResCode);
2627     }
2628
2629 lend:
2630     TRACE("Returning %d\n", bSuccess);
2631     return bSuccess;
2632 }
2633
2634
2635 /***********************************************************************
2636  *           FTP_SendAccount (internal)
2637  *
2638  *
2639  *
2640  * RETURNS
2641  *   TRUE on success
2642  *   FALSE on failure
2643  *
2644  */
2645 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2646 {
2647     INT nResCode;
2648     BOOL bSuccess = FALSE;
2649
2650     TRACE("\n");
2651     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2652         goto lend;
2653
2654     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2655     if (nResCode)
2656         bSuccess = TRUE;
2657     else
2658         FTP_SetResponseError(nResCode);
2659
2660 lend:
2661     return bSuccess;
2662 }
2663
2664
2665 /***********************************************************************
2666  *           FTP_SendStore (internal)
2667  *
2668  * Send request to upload file to ftp server
2669  *
2670  * RETURNS
2671  *   TRUE on success
2672  *   FALSE on failure
2673  *
2674  */
2675 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2676 {
2677     INT nResCode;
2678     BOOL bSuccess = FALSE;
2679
2680     TRACE("\n");
2681     if (!FTP_InitListenSocket(lpwfs))
2682         goto lend;
2683
2684     if (!FTP_SendType(lpwfs, dwType))
2685         goto lend;
2686
2687     if (!FTP_SendPortOrPasv(lpwfs))
2688         goto lend;
2689
2690     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2691             goto lend;
2692     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2693     if (nResCode)
2694     {
2695         if (nResCode == 150 || nResCode == 125)
2696             bSuccess = TRUE;
2697         else
2698             FTP_SetResponseError(nResCode);
2699     }
2700
2701 lend:
2702     if (!bSuccess && lpwfs->lstnSocket != -1)
2703     {
2704         closesocket(lpwfs->lstnSocket);
2705         lpwfs->lstnSocket = -1;
2706     }
2707
2708     return bSuccess;
2709 }
2710
2711
2712 /***********************************************************************
2713  *           FTP_InitListenSocket (internal)
2714  *
2715  * Create a socket to listen for server response
2716  *
2717  * RETURNS
2718  *   TRUE on success
2719  *   FALSE on failure
2720  *
2721  */
2722 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2723 {
2724     BOOL bSuccess = FALSE;
2725     socklen_t namelen = sizeof(struct sockaddr_in);
2726
2727     TRACE("\n");
2728
2729     lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2730     if (lpwfs->lstnSocket == -1)
2731     {
2732         TRACE("Unable to create listening socket\n");
2733             goto lend;
2734     }
2735
2736     /* We obtain our ip addr from the name of the command channel socket */
2737     lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2738
2739     /* and get the system to assign us a port */
2740     lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
2741
2742     if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2743     {
2744         TRACE("Unable to bind socket\n");
2745         goto lend;
2746     }
2747
2748     if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2749     {
2750         TRACE("listen failed\n");
2751         goto lend;
2752     }
2753
2754     if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2755         bSuccess = TRUE;
2756
2757 lend:
2758     if (!bSuccess && lpwfs->lstnSocket != -1)
2759     {
2760         closesocket(lpwfs->lstnSocket);
2761         lpwfs->lstnSocket = -1;
2762     }
2763
2764     return bSuccess;
2765 }
2766
2767
2768 /***********************************************************************
2769  *           FTP_SendType (internal)
2770  *
2771  * Tell server type of data being transferred
2772  *
2773  * RETURNS
2774  *   TRUE on success
2775  *   FALSE on failure
2776  *
2777  * W98SE doesn't cache the type that's currently set
2778  * (i.e. it sends it always),
2779  * so we probably don't want to do that either.
2780  */
2781 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2782 {
2783     INT nResCode;
2784     WCHAR type[] = { 'I','\0' };
2785     BOOL bSuccess = FALSE;
2786
2787     TRACE("\n");
2788     if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2789         type[0] = 'A';
2790
2791     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2792         goto lend;
2793
2794     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2795     if (nResCode)
2796     {
2797         if (nResCode == 2)
2798             bSuccess = TRUE;
2799         else
2800             FTP_SetResponseError(nResCode);
2801     }
2802
2803 lend:
2804     return bSuccess;
2805 }
2806
2807
2808 #if 0  /* FIXME: should probably be used for FtpGetFileSize */
2809 /***********************************************************************
2810  *           FTP_GetFileSize (internal)
2811  *
2812  * Retrieves from the server the size of the given file
2813  *
2814  * RETURNS
2815  *   TRUE on success
2816  *   FALSE on failure
2817  *
2818  */
2819 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2820 {
2821     INT nResCode;
2822     BOOL bSuccess = FALSE;
2823
2824     TRACE("\n");
2825
2826     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2827         goto lend;
2828
2829     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2830     if (nResCode)
2831     {
2832         if (nResCode == 213) {
2833             /* Now parses the output to get the actual file size */
2834             int i;
2835             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2836
2837             for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2838             if (lpszResponseBuffer[i] == '\0') return FALSE;
2839             *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2840             
2841             bSuccess = TRUE;
2842         } else {
2843             FTP_SetResponseError(nResCode);
2844         }
2845     }
2846
2847 lend:
2848     return bSuccess;
2849 }
2850 #endif
2851
2852
2853 /***********************************************************************
2854  *           FTP_SendPort (internal)
2855  *
2856  * Tell server which port to use
2857  *
2858  * RETURNS
2859  *   TRUE on success
2860  *   FALSE on failure
2861  *
2862  */
2863 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2864 {
2865     static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2866     INT nResCode;
2867     WCHAR szIPAddress[64];
2868     BOOL bSuccess = FALSE;
2869     TRACE("\n");
2870
2871     sprintfW(szIPAddress, szIPFormat,
2872          lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2873         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2874         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2875         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2876         lpwfs->lstnSocketAddress.sin_port & 0xFF,
2877         (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2878
2879     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2880         goto lend;
2881
2882     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2883     if (nResCode)
2884     {
2885         if (nResCode == 200)
2886             bSuccess = TRUE;
2887         else
2888             FTP_SetResponseError(nResCode);
2889     }
2890
2891 lend:
2892     return bSuccess;
2893 }
2894
2895
2896 /***********************************************************************
2897  *           FTP_DoPassive (internal)
2898  *
2899  * Tell server that we want to do passive transfers
2900  * and connect data socket
2901  *
2902  * RETURNS
2903  *   TRUE on success
2904  *   FALSE on failure
2905  *
2906  */
2907 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2908 {
2909     INT nResCode;
2910     BOOL bSuccess = FALSE;
2911
2912     TRACE("\n");
2913     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2914         goto lend;
2915
2916     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2917     if (nResCode)
2918     {
2919         if (nResCode == 227)
2920         {
2921             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2922             LPSTR p;
2923             int f[6];
2924             int i;
2925             char *pAddr, *pPort;
2926             INT nsocket = -1;
2927             struct sockaddr_in dataSocketAddress;
2928
2929             p = lpszResponseBuffer+4; /* skip status code */
2930             while (*p != '\0' && (*p < '0' || *p > '9')) p++;
2931
2932             if (*p == '\0')
2933             {
2934                 ERR("no address found in response, aborting\n");
2935                 goto lend;
2936             }
2937
2938             if (sscanf(p, "%d,%d,%d,%d,%d,%d",  &f[0], &f[1], &f[2], &f[3],
2939                                                 &f[4], &f[5]) != 6)
2940             {
2941                 ERR("unknown response address format '%s', aborting\n", p);
2942                 goto lend;
2943             }
2944             for (i=0; i < 6; i++)
2945                 f[i] = f[i] & 0xff;
2946
2947             dataSocketAddress = lpwfs->socketAddress;
2948             pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2949             pPort = (char *)&(dataSocketAddress.sin_port);
2950             pAddr[0] = f[0];
2951             pAddr[1] = f[1];
2952             pAddr[2] = f[2];
2953             pAddr[3] = f[3];
2954             pPort[0] = f[4];
2955             pPort[1] = f[5];
2956
2957             nsocket = socket(AF_INET,SOCK_STREAM,0);
2958             if (nsocket == -1)
2959                 goto lend;
2960
2961             if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2962             {
2963                 ERR("can't connect passive FTP data port.\n");
2964                 closesocket(nsocket);
2965                 goto lend;
2966             }
2967             lpwfs->pasvSocket = nsocket;
2968             bSuccess = TRUE;
2969         }
2970         else
2971             FTP_SetResponseError(nResCode);
2972     }
2973
2974 lend:
2975     return bSuccess;
2976 }
2977
2978
2979 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2980 {
2981     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2982     {
2983         if (!FTP_DoPassive(lpwfs))
2984             return FALSE;
2985     }
2986     else
2987     {
2988         if (!FTP_SendPort(lpwfs))
2989             return FALSE;
2990     }
2991     return TRUE;
2992 }
2993
2994
2995 /***********************************************************************
2996  *           FTP_GetDataSocket (internal)
2997  *
2998  * Either accepts an incoming data socket connection from the server
2999  * or just returns the already opened socket after a PASV command
3000  * in case of passive FTP.
3001  *
3002  *
3003  * RETURNS
3004  *   TRUE on success
3005  *   FALSE on failure
3006  *
3007  */
3008 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
3009 {
3010     struct sockaddr_in saddr;
3011     socklen_t addrlen = sizeof(struct sockaddr);
3012
3013     TRACE("\n");
3014     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3015     {
3016         *nDataSocket = lpwfs->pasvSocket;
3017     }
3018     else
3019     {
3020         *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3021         closesocket(lpwfs->lstnSocket);
3022         lpwfs->lstnSocket = -1;
3023     }
3024     return *nDataSocket != -1;
3025 }
3026
3027
3028 /***********************************************************************
3029  *           FTP_SendData (internal)
3030  *
3031  * Send data to the server
3032  *
3033  * RETURNS
3034  *   TRUE on success
3035  *   FALSE on failure
3036  *
3037  */
3038 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
3039 {
3040     BY_HANDLE_FILE_INFORMATION fi;
3041     DWORD nBytesRead = 0;
3042     DWORD nBytesSent = 0;
3043     DWORD nTotalSent = 0;
3044     DWORD nBytesToSend, nLen;
3045     int nRC = 1;
3046     time_t s_long_time, e_long_time;
3047     LONG nSeconds;
3048     CHAR *lpszBuffer;
3049
3050     TRACE("\n");
3051     lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3052
3053     /* Get the size of the file. */
3054     GetFileInformationByHandle(hFile, &fi);
3055     time(&s_long_time);
3056
3057     do
3058     {
3059         nBytesToSend = nBytesRead - nBytesSent;
3060
3061         if (nBytesToSend <= 0)
3062         {
3063             /* Read data from file. */
3064             nBytesSent = 0;
3065             if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3066             ERR("Failed reading from file\n");
3067
3068             if (nBytesRead > 0)
3069                 nBytesToSend = nBytesRead;
3070             else
3071                 break;
3072         }
3073
3074         nLen = DATA_PACKET_SIZE < nBytesToSend ?
3075             DATA_PACKET_SIZE : nBytesToSend;
3076         nRC  = send(nDataSocket, lpszBuffer, nLen, 0);
3077
3078         if (nRC != -1)
3079         {
3080             nBytesSent += nRC;
3081             nTotalSent += nRC;
3082         }
3083
3084         /* Do some computation to display the status. */
3085         time(&e_long_time);
3086         nSeconds = e_long_time - s_long_time;
3087         if( nSeconds / 60 > 0 )
3088         {
3089             TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3090             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3091             nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3092         }
3093         else
3094         {
3095             TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3096             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3097             (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3098         }
3099     } while (nRC != -1);
3100
3101     TRACE("file transfer complete!\n");
3102
3103     HeapFree(GetProcessHeap(), 0, lpszBuffer);
3104
3105     return nTotalSent;
3106 }
3107
3108
3109 /***********************************************************************
3110  *           FTP_SendRetrieve (internal)
3111  *
3112  * Send request to retrieve a file
3113  *
3114  * RETURNS
3115  *   Number of bytes to be received on success
3116  *   0 on failure
3117  *
3118  */
3119 static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3120 {
3121     INT nResCode;
3122     BOOL ret;
3123
3124     TRACE("\n");
3125     if (!(ret = FTP_InitListenSocket(lpwfs)))
3126         goto lend;
3127
3128     if (!(ret = FTP_SendType(lpwfs, dwType)))
3129         goto lend;
3130
3131     if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3132         goto lend;
3133
3134     if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3135         goto lend;
3136
3137     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3138     if ((nResCode != 125) && (nResCode != 150)) {
3139         /* That means that we got an error getting the file. */
3140         FTP_SetResponseError(nResCode);
3141         ret = FALSE;
3142     }
3143
3144 lend:
3145     if (!ret && lpwfs->lstnSocket != -1)
3146     {
3147         closesocket(lpwfs->lstnSocket);
3148         lpwfs->lstnSocket = -1;
3149     }
3150
3151     return ret;
3152 }
3153
3154
3155 /***********************************************************************
3156  *           FTP_RetrieveData  (internal)
3157  *
3158  * Retrieve data from server
3159  *
3160  * RETURNS
3161  *   TRUE on success
3162  *   FALSE on failure
3163  *
3164  */
3165 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
3166 {
3167     DWORD nBytesWritten;
3168     INT nRC = 0;
3169     CHAR *lpszBuffer;
3170
3171     TRACE("\n");
3172
3173     lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3174     if (NULL == lpszBuffer)
3175     {
3176         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3177         return FALSE;
3178     }
3179
3180     while (nRC != -1)
3181     {
3182         nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3183         if (nRC != -1)
3184         {
3185             /* other side closed socket. */
3186             if (nRC == 0)
3187                 goto recv_end;
3188             WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3189         }
3190     }
3191
3192     TRACE("Data transfer complete\n");
3193
3194 recv_end:
3195     HeapFree(GetProcessHeap(), 0, lpszBuffer);
3196
3197     return  (nRC != -1);
3198 }
3199
3200 /***********************************************************************
3201  *           FTPFINDNEXT_Destroy (internal)
3202  *
3203  * Deallocate session handle
3204  */
3205 static void FTPFINDNEXT_Destroy(WININETHANDLEHEADER *hdr)
3206 {
3207     LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3208     DWORD i;
3209
3210     TRACE("\n");
3211
3212     WININET_Release(&lpwfn->lpFtpSession->hdr);
3213
3214     for (i = 0; i < lpwfn->size; i++)
3215     {
3216         HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3217     }
3218
3219     HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3220     HeapFree(GetProcessHeap(), 0, lpwfn);
3221 }
3222
3223 static DWORD WINAPI FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3224 {
3225     WIN32_FIND_DATAW *find_data = data;
3226     DWORD res = ERROR_SUCCESS;
3227
3228     TRACE("index(%d) size(%d)\n", find->index, find->size);
3229
3230     ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3231
3232     if (find->index < find->size) {
3233         FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3234         find->index++;
3235
3236         TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3237     }else {
3238         res = ERROR_NO_MORE_FILES;
3239     }
3240
3241     if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3242     {
3243         INTERNET_ASYNC_RESULT iar;
3244
3245         iar.dwResult = (res == ERROR_SUCCESS);
3246         iar.dwError = res;
3247
3248         INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3249                               INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3250                               sizeof(INTERNET_ASYNC_RESULT));
3251     }
3252
3253     return res;
3254 }
3255
3256 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3257 {
3258     struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3259
3260     FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3261 }
3262
3263 static DWORD FTPFINDNEXT_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3264 {
3265     switch(option) {
3266     case INTERNET_OPTION_HANDLE_TYPE:
3267         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3268
3269         if (*size < sizeof(ULONG))
3270             return ERROR_INSUFFICIENT_BUFFER;
3271
3272         *size = sizeof(DWORD);
3273         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3274         return ERROR_SUCCESS;
3275     }
3276
3277     return INET_QueryOption(option, buffer, size, unicode);
3278 }
3279
3280 static DWORD FTPFINDNEXT_FindNextFileW(WININETHANDLEHEADER *hdr, void *data)
3281 {
3282     WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3283
3284     if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3285     {
3286         WORKREQUEST workRequest;
3287         struct WORKREQ_FTPFINDNEXTW *req;
3288
3289         workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3290         workRequest.hdr = WININET_AddRef( &find->hdr );
3291         req = &workRequest.u.FtpFindNextW;
3292         req->lpFindFileData = data;
3293
3294         INTERNET_AsyncCall(&workRequest);
3295
3296         return ERROR_SUCCESS;
3297     }
3298
3299     return FTPFINDNEXT_FindNextFileProc(find, data);
3300 }
3301
3302 static const HANDLEHEADERVtbl FTPFINDNEXTVtbl = {
3303     FTPFINDNEXT_Destroy,
3304     NULL,
3305     FTPFINDNEXT_QueryOption,
3306     NULL,
3307     NULL,
3308     NULL,
3309     NULL,
3310     NULL,
3311     FTPFINDNEXT_FindNextFileW
3312 };
3313
3314 /***********************************************************************
3315  *           FTP_ReceiveFileList (internal)
3316  *
3317  * Read file list from server
3318  *
3319  * RETURNS
3320  *   Handle to file list on success
3321  *   NULL on failure
3322  *
3323  */
3324 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3325         LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3326 {
3327     DWORD dwSize = 0;
3328     LPFILEPROPERTIESW lpafp = NULL;
3329     LPWININETFTPFINDNEXTW lpwfn = NULL;
3330     HINTERNET handle = 0;
3331
3332     TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3333
3334     if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3335     {
3336         if(lpFindFileData)
3337             FTP_ConvertFileProp(lpafp, lpFindFileData);
3338
3339         lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3340         if (lpwfn)
3341         {
3342             lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3343             lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3344             lpwfn->hdr.dwContext = dwContext;
3345             lpwfn->hdr.refs = 1;
3346             lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3347             lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3348             lpwfn->size = dwSize;
3349             lpwfn->lpafp = lpafp;
3350
3351             WININET_AddRef( &lpwfs->hdr );
3352             lpwfn->lpFtpSession = lpwfs;
3353             list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3354
3355             handle = WININET_AllocHandle( &lpwfn->hdr );
3356         }
3357     }
3358
3359     if( lpwfn )
3360         WININET_Release( &lpwfn->hdr );
3361
3362     TRACE("Matched %d files\n", dwSize);
3363     return handle;
3364 }
3365
3366
3367 /***********************************************************************
3368  *           FTP_ConvertFileProp (internal)
3369  *
3370  * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3371  *
3372  * RETURNS
3373  *   TRUE on success
3374  *   FALSE on failure
3375  *
3376  */
3377 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3378 {
3379     BOOL bSuccess = FALSE;
3380
3381     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3382
3383     if (lpafp)
3384     {
3385         /* Convert 'Unix' time to Windows time */
3386         RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
3387                                   (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
3388         lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3389         lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3390         
3391         /* Not all fields are filled in */
3392         lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3393         lpFindFileData->nFileSizeLow = lpafp->nSize;
3394
3395         if (lpafp->bIsDirectory)
3396             lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3397
3398         if (lpafp->lpszName)
3399             lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3400
3401         bSuccess = TRUE;
3402     }
3403
3404     return bSuccess;
3405 }
3406
3407 /***********************************************************************
3408  *           FTP_ParseNextFile (internal)
3409  *
3410  * Parse the next line in file listing
3411  *
3412  * RETURNS
3413  *   TRUE on success
3414  *   FALSE on failure
3415  */
3416 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3417 {
3418     static const char szSpace[] = " \t";
3419     DWORD nBufLen;
3420     char *pszLine;
3421     char *pszToken;
3422     char *pszTmp;
3423     BOOL found = FALSE;
3424     int i;
3425     
3426     lpfp->lpszName = NULL;
3427     do {
3428         if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3429             return FALSE;
3430     
3431         pszToken = strtok(pszLine, szSpace);
3432         /* ls format
3433          * <Permissions> <NoLinks> <owner>   <group> <size> <date>  <time or year> <filename>
3434          *
3435          * For instance:
3436          * drwx--s---     2         pcarrier  ens     512    Sep 28  1995           pcarrier
3437          */
3438         if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3439             if(!FTP_ParsePermission(pszToken, lpfp))
3440                 lpfp->bIsDirectory = FALSE;
3441             for(i=0; i<=3; i++) {
3442               if(!(pszToken = strtok(NULL, szSpace)))
3443                   break;
3444             }
3445             if(!pszToken) continue;
3446             if(lpfp->bIsDirectory) {
3447                 TRACE("Is directory\n");
3448                 lpfp->nSize = 0;
3449             }
3450             else {
3451                 TRACE("Size: %s\n", pszToken);
3452                 lpfp->nSize = atol(pszToken);
3453             }
3454             
3455             lpfp->tmLastModified.tm_sec  = 0;
3456             lpfp->tmLastModified.tm_min  = 0;
3457             lpfp->tmLastModified.tm_hour = 0;
3458             lpfp->tmLastModified.tm_mday = 0;
3459             lpfp->tmLastModified.tm_mon  = 0;
3460             lpfp->tmLastModified.tm_year = 0;
3461             
3462             /* Determine month */
3463             pszToken = strtok(NULL, szSpace);
3464             if(!pszToken) continue;
3465             if(strlen(pszToken) >= 3) {
3466                 pszToken[3] = 0;
3467                 if((pszTmp = StrStrIA(szMonths, pszToken)))
3468                     lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
3469             }
3470             /* Determine day */
3471             pszToken = strtok(NULL, szSpace);
3472             if(!pszToken) continue;
3473             lpfp->tmLastModified.tm_mday = atoi(pszToken);
3474             /* Determine time or year */
3475             pszToken = strtok(NULL, szSpace);
3476             if(!pszToken) continue;
3477             if((pszTmp = strchr(pszToken, ':'))) {
3478                 struct tm* apTM;
3479                 time_t aTime;
3480                 *pszTmp = 0;
3481                 pszTmp++;
3482                 lpfp->tmLastModified.tm_min = atoi(pszTmp);
3483                 lpfp->tmLastModified.tm_hour = atoi(pszToken);
3484                 time(&aTime);
3485                 apTM = localtime(&aTime);
3486                 lpfp->tmLastModified.tm_year = apTM->tm_year;
3487             }
3488             else {
3489                 lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
3490                 lpfp->tmLastModified.tm_hour = 12;
3491             }
3492             TRACE("Mod time: %02d:%02d:%02d  %02d/%02d/%02d\n",
3493                 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3494                 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3495                 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3496
3497             pszToken = strtok(NULL, szSpace);
3498             if(!pszToken) continue;
3499             lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3500             TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3501         }
3502         /* NT way of parsing ... :
3503             
3504                 07-13-03  08:55PM       <DIR>          sakpatch
3505                 05-09-03  06:02PM             12656686 2003-04-21bgm_cmd_e.rgz
3506         */
3507         else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3508             lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3509             
3510             sscanf(pszToken, "%d-%d-%d",
3511                 &lpfp->tmLastModified.tm_mon,
3512                 &lpfp->tmLastModified.tm_mday,
3513                 &lpfp->tmLastModified.tm_year);
3514
3515             /* Hacky and bad Y2K protection :-) */
3516             if (lpfp->tmLastModified.tm_year < 70)
3517                 lpfp->tmLastModified.tm_year += 100;
3518             
3519             pszToken = strtok(NULL, szSpace);
3520             if(!pszToken) continue;
3521             sscanf(pszToken, "%d:%d",
3522                 &lpfp->tmLastModified.tm_hour,
3523                 &lpfp->tmLastModified.tm_min);
3524             if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3525                 lpfp->tmLastModified.tm_hour += 12;
3526             }
3527             lpfp->tmLastModified.tm_sec = 0;
3528
3529             TRACE("Mod time: %02d:%02d:%02d  %02d/%02d/%02d\n",
3530                 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3531                 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3532                 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3533             
3534             pszToken = strtok(NULL, szSpace);
3535             if(!pszToken) continue;
3536             if(!strcasecmp(pszToken, "<DIR>")) {
3537                 lpfp->bIsDirectory = TRUE;
3538                 lpfp->nSize = 0;
3539                 TRACE("Is directory\n");
3540             }
3541             else {
3542                 lpfp->bIsDirectory = FALSE;
3543                 lpfp->nSize = atol(pszToken);
3544                 TRACE("Size: %d\n", lpfp->nSize);
3545             }
3546             
3547             pszToken = strtok(NULL, szSpace);
3548             if(!pszToken) continue;
3549             lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3550             TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3551         }
3552         /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3553         else if(pszToken[0] == '+') {
3554             FIXME("EPLF Format not implemented\n");
3555         }
3556         
3557         if(lpfp->lpszName) {
3558             if((lpszSearchFile == NULL) ||
3559                (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3560                 found = TRUE;
3561                 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3562             }
3563             else {
3564                 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3565                 lpfp->lpszName = NULL;
3566             }
3567         }
3568     } while(!found);
3569     return TRUE;
3570 }
3571
3572 /***********************************************************************
3573  *           FTP_ParseDirectory (internal)
3574  *
3575  * Parse string of directory information
3576  *
3577  * RETURNS
3578  *   TRUE on success
3579  *   FALSE on failure
3580  */
3581 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3582     LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3583 {
3584     BOOL bSuccess = TRUE;
3585     INT sizeFilePropArray = 500;/*20; */
3586     INT indexFilePropArray = -1;
3587
3588     TRACE("\n");
3589
3590     /* Allocate initial file properties array */
3591     *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3592     if (!*lpafp)
3593         return FALSE;
3594
3595     do {
3596         if (indexFilePropArray+1 >= sizeFilePropArray)
3597         {
3598             LPFILEPROPERTIESW tmpafp;
3599             
3600             sizeFilePropArray *= 2;
3601             tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3602                 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3603             if (NULL == tmpafp)
3604             {
3605                 bSuccess = FALSE;
3606                 break;
3607             }
3608
3609             *lpafp = tmpafp;
3610         }
3611         indexFilePropArray++;
3612     } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3613
3614     if (bSuccess && indexFilePropArray)
3615     {
3616         if (indexFilePropArray < sizeFilePropArray - 1)
3617         {
3618             LPFILEPROPERTIESW tmpafp;
3619
3620             tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3621                 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3622             if (NULL != tmpafp)
3623                 *lpafp = tmpafp;
3624         }
3625         *dwfp = indexFilePropArray;
3626     }
3627     else
3628     {
3629         HeapFree(GetProcessHeap(), 0, *lpafp);
3630         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3631         bSuccess = FALSE;
3632     }
3633
3634     return bSuccess;
3635 }
3636
3637
3638 /***********************************************************************
3639  *           FTP_ParsePermission (internal)
3640  *
3641  * Parse permission string of directory information
3642  *
3643  * RETURNS
3644  *   TRUE on success
3645  *   FALSE on failure
3646  *
3647  */
3648 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3649 {
3650     BOOL bSuccess = TRUE;
3651     unsigned short nPermission = 0;
3652     INT nPos = 1;
3653     INT nLast  = 9;
3654
3655     TRACE("\n");
3656     if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3657     {
3658         bSuccess = FALSE;
3659         return bSuccess;
3660     }
3661
3662     lpfp->bIsDirectory = (*lpszPermission == 'd');
3663     do
3664     {
3665         switch (nPos)
3666         {
3667             case 1:
3668                 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3669                 break;
3670             case 2:
3671                 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3672                 break;
3673             case 3:
3674                 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3675                 break;
3676             case 4:
3677                 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3678                 break;
3679             case 5:
3680                 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3681                 break;
3682             case 6:
3683                 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3684                 break;
3685             case 7:
3686                 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3687                 break;
3688             case 8:
3689                 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3690                 break;
3691             case 9:
3692                 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3693                 break;
3694         }
3695         nPos++;
3696     }while (nPos <= nLast);
3697
3698     lpfp->permissions = nPermission;
3699     return bSuccess;
3700 }
3701
3702
3703 /***********************************************************************
3704  *           FTP_SetResponseError (internal)
3705  *
3706  * Set the appropriate error code for a given response from the server
3707  *
3708  * RETURNS
3709  *
3710  */
3711 static DWORD FTP_SetResponseError(DWORD dwResponse)
3712 {
3713     DWORD dwCode = 0;
3714
3715     switch(dwResponse)
3716     {
3717     case 425: /* Cannot open data connection. */
3718         dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3719         break;
3720
3721     case 426: /* Connection closed, transer aborted. */
3722         dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3723         break;
3724
3725     case 530: /* Not logged in. Login incorrect. */
3726         dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3727         break;
3728
3729     case 421: /* Service not available - Server may be shutting down. */
3730     case 450: /* File action not taken. File may be busy. */
3731     case 451: /* Action aborted. Server error. */
3732     case 452: /* Action not taken. Insufficient storage space on server. */
3733     case 500: /* Syntax error. Command unrecognized. */
3734     case 501: /* Syntax error. Error in parameters or arguments. */
3735     case 502: /* Command not implemented. */
3736     case 503: /* Bad sequence of commands. */
3737     case 504: /* Command not implemented for that parameter. */
3738     case 532: /* Need account for storing files */
3739     case 550: /* File action not taken. File not found or no access. */
3740     case 551: /* Requested action aborted. Page type unknown */
3741     case 552: /* Action aborted. Exceeded storage allocation */
3742     case 553: /* Action not taken. File name not allowed. */
3743
3744     default:
3745         dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3746         break;
3747     }
3748
3749     INTERNET_SetLastError(dwCode);
3750     return dwCode;
3751 }