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