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