jscript: Make String_slice generic.
[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 _ftp_session_t ftp_session_t;
72
73 typedef struct
74 {
75     object_header_t hdr;
76     ftp_session_t *lpFtpSession;
77     BOOL session_deleted;
78     int nDataSocket;
79 } ftp_file_t;
80
81 struct _ftp_session_t
82 {
83     object_header_t hdr;
84     appinfo_t *lpAppInfo;
85     int sndSocket;
86     int lstnSocket;
87     int pasvSocket; /* data socket connected by us in case of passive FTP */
88     ftp_file_t *download_in_progress;
89     struct sockaddr_in socketAddress;
90     struct sockaddr_in lstnSocketAddress;
91     LPWSTR  lpszPassword;
92     LPWSTR  lpszUserName;
93 };
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     object_header_t hdr;
107     ftp_session_t *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, object_header_t *hdr, DWORD_PTR dwContext);
176 static BOOL FTP_SendStore(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
177 static BOOL FTP_GetDataSocket(ftp_session_t*, LPINT nDataSocket);
178 static BOOL FTP_SendData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
179 static INT FTP_ReceiveResponse(ftp_session_t*, DWORD_PTR dwContext);
180 static BOOL FTP_SendRetrieve(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
181 static BOOL FTP_RetrieveFileData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
182 static BOOL FTP_InitListenSocket(ftp_session_t*);
183 static BOOL FTP_ConnectToHost(ftp_session_t*);
184 static BOOL FTP_SendPassword(ftp_session_t*);
185 static BOOL FTP_SendAccount(ftp_session_t*);
186 static BOOL FTP_SendType(ftp_session_t*, DWORD dwType);
187 static BOOL FTP_SendPort(ftp_session_t*);
188 static BOOL FTP_DoPassive(ftp_session_t*);
189 static BOOL FTP_SendPortOrPasv(ftp_session_t*);
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(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
193         LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
194 static HINTERNET FTP_ReceiveFileList(ftp_session_t*, 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(ftp_session_t*, LPCWSTR lpszLocalFile,
199         LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
200 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
201 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
202 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t*,
203         LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
204 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t*, LPWSTR lpszCurrentDirectory,
205         LPDWORD lpdwCurrentDirectory);
206 static BOOL FTP_FtpRenameFileW(ftp_session_t*, LPCWSTR lpszSrc, LPCWSTR lpszDest);
207 static BOOL FTP_FtpRemoveDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
208 static BOOL FTP_FtpDeleteFileW(ftp_session_t*, LPCWSTR lpszFileName);
209 static BOOL FTP_FtpGetFileW(ftp_session_t*, 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     ftp_session_t *lpwfs = (ftp_session_t*) 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     ftp_session_t *lpwfs;
268     appinfo_t *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 = (ftp_session_t*) 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(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
340     LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
341 {
342     HANDLE hFile;
343     BOOL bSuccess = FALSE;
344     appinfo_t *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     ftp_session_t *lpwfs = (ftp_session_t*) 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     ftp_session_t *lpwfs = NULL;
447     appinfo_t *hIC = NULL;
448     BOOL r = FALSE;
449
450     if (!lpszDirectory)
451     {
452         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
453         goto lend;
454     }
455
456     lpwfs = (ftp_session_t*) 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(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
508 {
509     INT nResCode;
510     appinfo_t *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     ftp_session_t *lpwfs = (ftp_session_t*) 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     ftp_session_t *lpwfs;
593     appinfo_t *hIC = NULL;
594     BOOL r = FALSE;
595
596     lpwfs = (ftp_session_t*) 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(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
656 {
657     INT nResCode;
658     BOOL bSuccess = FALSE;
659     appinfo_t *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     ftp_session_t *lpwfs = (ftp_session_t*) 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     ftp_session_t *lpwfs;
749     appinfo_t *hIC = NULL;
750     HINTERNET r = NULL;
751
752     lpwfs = (ftp_session_t*) 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(ftp_session_t *lpwfs,
806     LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
807 {
808     INT nResCode;
809     appinfo_t *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     ftp_session_t *lpwfs = (ftp_session_t*) 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     ftp_session_t *lpwfs;
942     appinfo_t *hIC = NULL;
943     BOOL r = FALSE;
944
945     TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
946
947     lpwfs = (ftp_session_t*) 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(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
1017         LPDWORD lpdwCurrentDirectory)
1018 {
1019     INT nResCode;
1020     appinfo_t *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(object_header_t *hdr)
1088 {
1089     ftp_file_t *lpwh = (ftp_file_t*) hdr;
1090     ftp_session_t *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(object_header_t *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(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
1127 {
1128     ftp_file_t *file = (ftp_file_t*)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(object_header_t *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(object_header_t *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(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1154 {
1155     ftp_file_t *lpwh = (ftp_file_t*) 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(ftp_file_t *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     ftp_file_t *file = (ftp_file_t*)workRequest->hdr;
1189
1190     FTP_ReceiveRequestData(file, FALSE);
1191 }
1192
1193 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1194 {
1195     ftp_file_t *file = (ftp_file_t*) 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 object_vtbl_t 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(ftp_session_t *lpwfs,
1257         LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1258         DWORD_PTR dwContext)
1259 {
1260     INT nDataSocket;
1261     BOOL bSuccess = FALSE;
1262     ftp_file_t *lpwh = NULL;
1263     appinfo_t *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(ftp_file_t));
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     ftp_session_t *lpwfs = (ftp_session_t*) 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     ftp_session_t *lpwfs;
1392     appinfo_t *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 = (ftp_session_t*) 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     ftp_session_t *lpwfs = (ftp_session_t*) 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     ftp_session_t *lpwfs;
1512     appinfo_t *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 = (ftp_session_t*) 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(ftp_session_t *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     appinfo_t *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     ftp_session_t *lpwfs = (ftp_session_t*) 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     ftp_session_t *lpwfs;
1709     appinfo_t *hIC = NULL;
1710     BOOL r = FALSE;
1711
1712     lpwfs = (ftp_session_t*) 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(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1772 {
1773     INT nResCode;
1774     BOOL bSuccess = FALSE;
1775     appinfo_t *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     ftp_session_t *lpwfs = (ftp_session_t*) 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     ftp_session_t *lpwfs;
1854     appinfo_t *hIC = NULL;
1855     BOOL r = FALSE;
1856
1857     lpwfs = (ftp_session_t*) 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(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
1917 {
1918     INT nResCode;
1919     BOOL bSuccess = FALSE;
1920     appinfo_t *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     ftp_session_t *lpwfs = (ftp_session_t*) 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     ftp_session_t *lpwfs;
2004     appinfo_t *hIC = NULL;
2005     BOOL r = FALSE;
2006
2007     lpwfs = (ftp_session_t*) 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(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2068 {
2069     INT nResCode;
2070     BOOL bSuccess = FALSE;
2071     appinfo_t *hIC = NULL;
2072
2073     TRACE("\n");
2074
2075     /* Clear any error information */
2076     INTERNET_SetLastError(0);
2077
2078     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2079         goto lend;
2080
2081     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2082     if (nResCode == 350)
2083     {
2084         if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2085             goto lend;
2086
2087         nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2088     }
2089
2090     if (nResCode == 250)
2091         bSuccess = TRUE;
2092     else
2093         FTP_SetResponseError(nResCode);
2094
2095 lend:
2096     hIC = lpwfs->lpAppInfo;
2097     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2098     {
2099         INTERNET_ASYNC_RESULT iar;
2100
2101         iar.dwResult = (DWORD)bSuccess;
2102         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2103         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2104             &iar, sizeof(INTERNET_ASYNC_RESULT));
2105     }
2106
2107     return bSuccess;
2108 }
2109
2110 /***********************************************************************
2111  *           FtpCommandA  (WININET.@)
2112  */
2113 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2114                          LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2115 {
2116     BOOL r;
2117     WCHAR *cmdW;
2118
2119     TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2120           debugstr_a(lpszCommand), dwContext, phFtpCommand);
2121
2122     if (fExpectResponse)
2123     {
2124         FIXME("data connection not supported\n");
2125         return FALSE;
2126     }
2127
2128     if (!lpszCommand || !lpszCommand[0])
2129     {
2130         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2131         return FALSE;
2132     }
2133
2134     if (!(cmdW = WININET_strdup_AtoW(lpszCommand)))
2135     {
2136         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2137         return FALSE;
2138     }
2139
2140     r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2141
2142     HeapFree(GetProcessHeap(), 0, cmdW);
2143     return r;
2144 }
2145
2146 /***********************************************************************
2147  *           FtpCommandW  (WININET.@)
2148  */
2149 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2150                          LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2151 {
2152     BOOL r = FALSE;
2153     ftp_session_t *lpwfs;
2154     LPSTR cmd = NULL;
2155     DWORD len, nBytesSent= 0;
2156     INT nResCode, nRC = 0;
2157
2158     TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2159            debugstr_w(lpszCommand), dwContext, phFtpCommand);
2160
2161     if (!lpszCommand || !lpszCommand[0])
2162     {
2163         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2164         return FALSE;
2165     }
2166
2167     if (fExpectResponse)
2168     {
2169         FIXME("data connection not supported\n");
2170         return FALSE;
2171     }
2172
2173     lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
2174     if (!lpwfs)
2175     {
2176         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2177         return FALSE;
2178     }
2179
2180     if (WH_HFTPSESSION != lpwfs->hdr.htype)
2181     {
2182         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2183         goto lend;
2184     }
2185
2186     if (lpwfs->download_in_progress != NULL)
2187     {
2188         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2189         goto lend;
2190     }
2191
2192     len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2193     if ((cmd = HeapAlloc(GetProcessHeap(), 0, len )))
2194         WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2195     else
2196     {
2197         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2198         goto lend;
2199     }
2200
2201     strcat(cmd, szCRLF);
2202     len--;
2203
2204     TRACE("Sending (%s) len(%d)\n", cmd, len);
2205     while ((nBytesSent < len) && (nRC != -1))
2206     {
2207         nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2208         if (nRC != -1)
2209         {
2210             nBytesSent += nRC;
2211             TRACE("Sent %d bytes\n", nRC);
2212         }
2213     }
2214
2215     if (nBytesSent)
2216     {
2217         nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2218         if (nResCode > 0 && nResCode < 400)
2219             r = TRUE;
2220         else
2221             FTP_SetResponseError(nResCode);
2222     }
2223
2224 lend:
2225     WININET_Release( &lpwfs->hdr );
2226     HeapFree(GetProcessHeap(), 0, cmd);
2227     return r;
2228 }
2229
2230
2231 /***********************************************************************
2232  *           FTPSESSION_Destroy (internal)
2233  *
2234  * Deallocate session handle
2235  */
2236 static void FTPSESSION_Destroy(object_header_t *hdr)
2237 {
2238     ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2239
2240     TRACE("\n");
2241
2242     WININET_Release(&lpwfs->lpAppInfo->hdr);
2243
2244     HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2245     HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2246     HeapFree(GetProcessHeap(), 0, lpwfs);
2247 }
2248
2249 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2250 {
2251     ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2252
2253     TRACE("\n");
2254
2255     SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2256                       INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2257
2258     if (lpwfs->download_in_progress != NULL)
2259         lpwfs->download_in_progress->session_deleted = TRUE;
2260
2261      if (lpwfs->sndSocket != -1)
2262          closesocket(lpwfs->sndSocket);
2263
2264      if (lpwfs->lstnSocket != -1)
2265          closesocket(lpwfs->lstnSocket);
2266
2267     if (lpwfs->pasvSocket != -1)
2268         closesocket(lpwfs->pasvSocket);
2269
2270     SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2271                       INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2272 }
2273
2274 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2275 {
2276     switch(option) {
2277     case INTERNET_OPTION_HANDLE_TYPE:
2278         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2279
2280         if (*size < sizeof(ULONG))
2281             return ERROR_INSUFFICIENT_BUFFER;
2282
2283         *size = sizeof(DWORD);
2284         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2285         return ERROR_SUCCESS;
2286     }
2287
2288     return INET_QueryOption(option, buffer, size, unicode);
2289 }
2290
2291 static const object_vtbl_t FTPSESSIONVtbl = {
2292     FTPSESSION_Destroy,
2293     FTPSESSION_CloseConnection,
2294     FTPSESSION_QueryOption,
2295     NULL,
2296     NULL,
2297     NULL,
2298     NULL,
2299     NULL,
2300     NULL
2301 };
2302
2303
2304 /***********************************************************************
2305  *           FTP_Connect (internal)
2306  *
2307  * Connect to a ftp server
2308  *
2309  * RETURNS
2310  *   HINTERNET a session handle on success
2311  *   NULL on failure
2312  *
2313  * NOTES:
2314  *
2315  * Windows uses 'anonymous' as the username, when given a NULL username
2316  * and a NULL password. The password is first looked up in:
2317  *
2318  * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2319  *
2320  * If this entry is not present it uses the current username as the password.
2321  *
2322  */
2323
2324 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2325         INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2326         LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2327         DWORD dwInternalFlags)
2328 {
2329     static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2330                                    'M','i','c','r','o','s','o','f','t','\\',
2331                                    'W','i','n','d','o','w','s','\\',
2332                                    'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2333                                    'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2334     static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2335     static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2336     static const WCHAR szEmpty[] = {'\0'};
2337     struct sockaddr_in socketAddr;
2338     INT nsocket = -1;
2339     UINT sock_namelen;
2340     BOOL bSuccess = FALSE;
2341     ftp_session_t *lpwfs = NULL;
2342     HINTERNET handle = NULL;
2343
2344     TRACE("%p  Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2345             hIC, debugstr_w(lpszServerName),
2346             nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2347
2348     assert( hIC->hdr.htype == WH_HINIT );
2349
2350     if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2351     {
2352         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2353         goto lerror;
2354     }
2355     
2356     lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(ftp_session_t));
2357     if (NULL == lpwfs)
2358     {
2359         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2360         goto lerror;
2361     }
2362
2363     if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2364         nServerPort = INTERNET_DEFAULT_FTP_PORT;
2365
2366     lpwfs->hdr.htype = WH_HFTPSESSION;
2367     lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
2368     lpwfs->hdr.dwFlags = dwFlags;
2369     lpwfs->hdr.dwContext = dwContext;
2370     lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2371     lpwfs->hdr.refs = 1;
2372     lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2373     lpwfs->download_in_progress = NULL;
2374     lpwfs->sndSocket = -1;
2375     lpwfs->lstnSocket = -1;
2376     lpwfs->pasvSocket = -1;
2377
2378     WININET_AddRef( &hIC->hdr );
2379     lpwfs->lpAppInfo = hIC;
2380     list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2381
2382     handle = WININET_AllocHandle( &lpwfs->hdr );
2383     if( !handle )
2384     {
2385         ERR("Failed to alloc handle\n");
2386         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2387         goto lerror;
2388     }
2389
2390     if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2391         if(strchrW(hIC->lpszProxy, ' '))
2392             FIXME("Several proxies not implemented.\n");
2393         if(hIC->lpszProxyBypass)
2394             FIXME("Proxy bypass is ignored.\n");
2395     }
2396     if (!lpszUserName || !strlenW(lpszUserName)) {
2397         HKEY key;
2398         WCHAR szPassword[MAX_PATH];
2399         DWORD len = sizeof(szPassword);
2400
2401         lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
2402
2403         RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2404         if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2405             /* Nothing in the registry, get the username and use that as the password */
2406             if (!GetUserNameW(szPassword, &len)) {
2407                 /* Should never get here, but use an empty password as failsafe */
2408                 strcpyW(szPassword, szEmpty);
2409             }
2410         }
2411         RegCloseKey(key);
2412
2413         TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2414         lpwfs->lpszPassword = WININET_strdupW(szPassword);
2415     }
2416     else {
2417         lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
2418
2419         if (lpszPassword)
2420             lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
2421         else
2422             lpwfs->lpszPassword = WININET_strdupW(szEmpty);
2423     }
2424     
2425     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2426     if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2427     {
2428         INTERNET_ASYNC_RESULT iar;
2429
2430         iar.dwResult = (DWORD_PTR)handle;
2431         iar.dwError = ERROR_SUCCESS;
2432
2433         SendAsyncCallback(&hIC->hdr, dwContext,
2434                       INTERNET_STATUS_HANDLE_CREATED, &iar,
2435                       sizeof(INTERNET_ASYNC_RESULT));
2436     }
2437         
2438     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2439         (LPWSTR) lpszServerName, strlenW(lpszServerName));
2440
2441     sock_namelen = sizeof(socketAddr);
2442     if (!GetAddress(lpszServerName, nServerPort,
2443         (struct sockaddr *)&socketAddr, &sock_namelen))
2444     {
2445         INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2446         goto lerror;
2447     }
2448
2449     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2450         (LPWSTR) lpszServerName, strlenW(lpszServerName));
2451
2452     if (socketAddr.sin_family != AF_INET)
2453     {
2454         WARN("unsupported address family %d\n", socketAddr.sin_family);
2455         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2456         goto lerror;
2457     }
2458     nsocket = socket(AF_INET,SOCK_STREAM,0);
2459     if (nsocket == -1)
2460     {
2461         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2462         goto lerror;
2463     }
2464
2465     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2466         &socketAddr, sock_namelen);
2467
2468     if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2469     {
2470         ERR("Unable to connect (%s)\n", strerror(errno));
2471         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2472         closesocket(nsocket);
2473     }
2474     else
2475     {
2476         TRACE("Connected to server\n");
2477         lpwfs->sndSocket = nsocket;
2478         SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2479             &socketAddr, sock_namelen);
2480
2481         sock_namelen = sizeof(lpwfs->socketAddress);
2482         getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2483
2484         if (FTP_ConnectToHost(lpwfs))
2485         {
2486             TRACE("Successfully logged into server\n");
2487             bSuccess = TRUE;
2488         }
2489     }
2490
2491 lerror:
2492     if (lpwfs) WININET_Release( &lpwfs->hdr );
2493
2494     if (!bSuccess && handle)
2495     {
2496         WININET_FreeHandle( handle );
2497         handle = NULL;
2498     }
2499
2500     return handle;
2501 }
2502
2503
2504 /***********************************************************************
2505  *           FTP_ConnectToHost (internal)
2506  *
2507  * Connect to a ftp server
2508  *
2509  * RETURNS
2510  *   TRUE on success
2511  *   NULL on failure
2512  *
2513  */
2514 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2515 {
2516     INT nResCode;
2517     BOOL bSuccess = FALSE;
2518
2519     TRACE("\n");
2520     FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2521
2522     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2523         goto lend;
2524
2525     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2526     if (nResCode)
2527     {
2528         /* Login successful... */
2529         if (nResCode == 230)
2530             bSuccess = TRUE;
2531         /* User name okay, need password... */
2532         else if (nResCode == 331)
2533             bSuccess = FTP_SendPassword(lpwfs);
2534         /* Need account for login... */
2535         else if (nResCode == 332)
2536             bSuccess = FTP_SendAccount(lpwfs);
2537         else
2538             FTP_SetResponseError(nResCode);
2539     }
2540
2541     TRACE("Returning %d\n", bSuccess);
2542 lend:
2543     return bSuccess;
2544 }
2545
2546
2547 /***********************************************************************
2548  *           FTP_SendCommandA (internal)
2549  *
2550  * Send command to server
2551  *
2552  * RETURNS
2553  *   TRUE on success
2554  *   NULL on failure
2555  *
2556  */
2557 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2558         INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2559 {
2560         DWORD len;
2561         CHAR *buf;
2562         DWORD nBytesSent = 0;
2563         int nRC = 0;
2564         DWORD dwParamLen;
2565
2566         TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2567
2568         if (lpfnStatusCB)
2569         {
2570             lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2571         }
2572
2573         dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2574         len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2575         if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2576         {
2577             INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2578             return FALSE;
2579         }
2580         sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2581                 dwParamLen ? lpszParam : "", szCRLF);
2582
2583         TRACE("Sending (%s) len(%d)\n", buf, len);
2584         while((nBytesSent < len) && (nRC != -1))
2585         {
2586                 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2587                 nBytesSent += nRC;
2588         }
2589
2590         HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2591
2592         if (lpfnStatusCB)
2593         {
2594             lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2595                          &nBytesSent, sizeof(DWORD));
2596         }
2597
2598         TRACE("Sent %d bytes\n", nBytesSent);
2599         return (nRC != -1);
2600 }
2601
2602 /***********************************************************************
2603  *           FTP_SendCommand (internal)
2604  *
2605  * Send command to server
2606  *
2607  * RETURNS
2608  *   TRUE on success
2609  *   NULL on failure
2610  *
2611  */
2612 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2613         INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2614 {
2615     BOOL ret;
2616     LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
2617     ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2618     HeapFree(GetProcessHeap(), 0, lpszParamA);
2619     return ret;
2620 }
2621
2622 /***********************************************************************
2623  *           FTP_ReceiveResponse (internal)
2624  *
2625  * Receive response from server
2626  *
2627  * RETURNS
2628  *   Reply code on success
2629  *   0 on failure
2630  *
2631  */
2632 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2633 {
2634     LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2635     DWORD nRecv;
2636     INT rc = 0;
2637     char firstprefix[5];
2638     BOOL multiline = FALSE;
2639
2640     TRACE("socket(%d)\n", lpwfs->sndSocket);
2641
2642     SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2643
2644     while(1)
2645     {
2646         if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2647             goto lerror;
2648
2649         if (nRecv >= 3)
2650         {
2651             if(!multiline)
2652             {
2653                 if(lpszResponse[3] != '-')
2654                     break;
2655                 else
2656                 {  /* Start of multiline response.  Loop until we get "nnn " */
2657                     multiline = TRUE;
2658                     memcpy(firstprefix, lpszResponse, 3);
2659                     firstprefix[3] = ' ';
2660                     firstprefix[4] = '\0';
2661                 }
2662             }
2663             else
2664             {
2665                 if(!memcmp(firstprefix, lpszResponse, 4))
2666                     break;
2667             }
2668         }
2669     }
2670
2671     if (nRecv >= 3)
2672     {
2673         rc = atoi(lpszResponse);
2674
2675         SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2676                     &nRecv, sizeof(DWORD));
2677     }
2678
2679 lerror:
2680     TRACE("return %d\n", rc);
2681     return rc;
2682 }
2683
2684
2685 /***********************************************************************
2686  *           FTP_SendPassword (internal)
2687  *
2688  * Send password to ftp server
2689  *
2690  * RETURNS
2691  *   TRUE on success
2692  *   NULL on failure
2693  *
2694  */
2695 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2696 {
2697     INT nResCode;
2698     BOOL bSuccess = FALSE;
2699
2700     TRACE("\n");
2701     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2702         goto lend;
2703
2704     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2705     if (nResCode)
2706     {
2707         TRACE("Received reply code %d\n", nResCode);
2708         /* Login successful... */
2709         if (nResCode == 230)
2710             bSuccess = TRUE;
2711         /* Command not implemented, superfluous at the server site... */
2712         /* Need account for login... */
2713         else if (nResCode == 332)
2714             bSuccess = FTP_SendAccount(lpwfs);
2715         else
2716             FTP_SetResponseError(nResCode);
2717     }
2718
2719 lend:
2720     TRACE("Returning %d\n", bSuccess);
2721     return bSuccess;
2722 }
2723
2724
2725 /***********************************************************************
2726  *           FTP_SendAccount (internal)
2727  *
2728  *
2729  *
2730  * RETURNS
2731  *   TRUE on success
2732  *   FALSE on failure
2733  *
2734  */
2735 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2736 {
2737     INT nResCode;
2738     BOOL bSuccess = FALSE;
2739
2740     TRACE("\n");
2741     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2742         goto lend;
2743
2744     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2745     if (nResCode)
2746         bSuccess = TRUE;
2747     else
2748         FTP_SetResponseError(nResCode);
2749
2750 lend:
2751     return bSuccess;
2752 }
2753
2754
2755 /***********************************************************************
2756  *           FTP_SendStore (internal)
2757  *
2758  * Send request to upload file to ftp server
2759  *
2760  * RETURNS
2761  *   TRUE on success
2762  *   FALSE on failure
2763  *
2764  */
2765 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2766 {
2767     INT nResCode;
2768     BOOL bSuccess = FALSE;
2769
2770     TRACE("\n");
2771     if (!FTP_InitListenSocket(lpwfs))
2772         goto lend;
2773
2774     if (!FTP_SendType(lpwfs, dwType))
2775         goto lend;
2776
2777     if (!FTP_SendPortOrPasv(lpwfs))
2778         goto lend;
2779
2780     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2781             goto lend;
2782     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2783     if (nResCode)
2784     {
2785         if (nResCode == 150 || nResCode == 125)
2786             bSuccess = TRUE;
2787         else
2788             FTP_SetResponseError(nResCode);
2789     }
2790
2791 lend:
2792     if (!bSuccess && lpwfs->lstnSocket != -1)
2793     {
2794         closesocket(lpwfs->lstnSocket);
2795         lpwfs->lstnSocket = -1;
2796     }
2797
2798     return bSuccess;
2799 }
2800
2801
2802 /***********************************************************************
2803  *           FTP_InitListenSocket (internal)
2804  *
2805  * Create a socket to listen for server response
2806  *
2807  * RETURNS
2808  *   TRUE on success
2809  *   FALSE on failure
2810  *
2811  */
2812 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2813 {
2814     BOOL bSuccess = FALSE;
2815     socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2816
2817     TRACE("\n");
2818
2819     lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2820     if (lpwfs->lstnSocket == -1)
2821     {
2822         TRACE("Unable to create listening socket\n");
2823             goto lend;
2824     }
2825
2826     /* We obtain our ip addr from the name of the command channel socket */
2827     lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2828
2829     /* and get the system to assign us a port */
2830     lpwfs->lstnSocketAddress.sin_port = htons(0);
2831
2832     if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2833     {
2834         TRACE("Unable to bind socket\n");
2835         goto lend;
2836     }
2837
2838     if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2839     {
2840         TRACE("listen failed\n");
2841         goto lend;
2842     }
2843
2844     if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2845         bSuccess = TRUE;
2846
2847 lend:
2848     if (!bSuccess && lpwfs->lstnSocket != -1)
2849     {
2850         closesocket(lpwfs->lstnSocket);
2851         lpwfs->lstnSocket = -1;
2852     }
2853
2854     return bSuccess;
2855 }
2856
2857
2858 /***********************************************************************
2859  *           FTP_SendType (internal)
2860  *
2861  * Tell server type of data being transferred
2862  *
2863  * RETURNS
2864  *   TRUE on success
2865  *   FALSE on failure
2866  *
2867  * W98SE doesn't cache the type that's currently set
2868  * (i.e. it sends it always),
2869  * so we probably don't want to do that either.
2870  */
2871 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2872 {
2873     INT nResCode;
2874     WCHAR type[] = { 'I','\0' };
2875     BOOL bSuccess = FALSE;
2876
2877     TRACE("\n");
2878     if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2879         type[0] = 'A';
2880
2881     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2882         goto lend;
2883
2884     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2885     if (nResCode)
2886     {
2887         if (nResCode == 2)
2888             bSuccess = TRUE;
2889         else
2890             FTP_SetResponseError(nResCode);
2891     }
2892
2893 lend:
2894     return bSuccess;
2895 }
2896
2897
2898 #if 0  /* FIXME: should probably be used for FtpGetFileSize */
2899 /***********************************************************************
2900  *           FTP_GetFileSize (internal)
2901  *
2902  * Retrieves from the server the size of the given file
2903  *
2904  * RETURNS
2905  *   TRUE on success
2906  *   FALSE on failure
2907  *
2908  */
2909 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2910 {
2911     INT nResCode;
2912     BOOL bSuccess = FALSE;
2913
2914     TRACE("\n");
2915
2916     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2917         goto lend;
2918
2919     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2920     if (nResCode)
2921     {
2922         if (nResCode == 213) {
2923             /* Now parses the output to get the actual file size */
2924             int i;
2925             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2926
2927             for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2928             if (lpszResponseBuffer[i] == '\0') return FALSE;
2929             *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2930             
2931             bSuccess = TRUE;
2932         } else {
2933             FTP_SetResponseError(nResCode);
2934         }
2935     }
2936
2937 lend:
2938     return bSuccess;
2939 }
2940 #endif
2941
2942
2943 /***********************************************************************
2944  *           FTP_SendPort (internal)
2945  *
2946  * Tell server which port to use
2947  *
2948  * RETURNS
2949  *   TRUE on success
2950  *   FALSE on failure
2951  *
2952  */
2953 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
2954 {
2955     static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2956     INT nResCode;
2957     WCHAR szIPAddress[64];
2958     BOOL bSuccess = FALSE;
2959     TRACE("\n");
2960
2961     sprintfW(szIPAddress, szIPFormat,
2962          lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2963         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2964         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2965         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2966         lpwfs->lstnSocketAddress.sin_port & 0xFF,
2967         (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2968
2969     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2970         goto lend;
2971
2972     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2973     if (nResCode)
2974     {
2975         if (nResCode == 200)
2976             bSuccess = TRUE;
2977         else
2978             FTP_SetResponseError(nResCode);
2979     }
2980
2981 lend:
2982     return bSuccess;
2983 }
2984
2985
2986 /***********************************************************************
2987  *           FTP_DoPassive (internal)
2988  *
2989  * Tell server that we want to do passive transfers
2990  * and connect data socket
2991  *
2992  * RETURNS
2993  *   TRUE on success
2994  *   FALSE on failure
2995  *
2996  */
2997 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
2998 {
2999     INT nResCode;
3000     BOOL bSuccess = FALSE;
3001
3002     TRACE("\n");
3003     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3004         goto lend;
3005
3006     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3007     if (nResCode)
3008     {
3009         if (nResCode == 227)
3010         {
3011             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3012             LPSTR p;
3013             int f[6];
3014             int i;
3015             char *pAddr, *pPort;
3016             INT nsocket = -1;
3017             struct sockaddr_in dataSocketAddress;
3018
3019             p = lpszResponseBuffer+4; /* skip status code */
3020             while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3021
3022             if (*p == '\0')
3023             {
3024                 ERR("no address found in response, aborting\n");
3025                 goto lend;
3026             }
3027
3028             if (sscanf(p, "%d,%d,%d,%d,%d,%d",  &f[0], &f[1], &f[2], &f[3],
3029                                                 &f[4], &f[5]) != 6)
3030             {
3031                 ERR("unknown response address format '%s', aborting\n", p);
3032                 goto lend;
3033             }
3034             for (i=0; i < 6; i++)
3035                 f[i] = f[i] & 0xff;
3036
3037             dataSocketAddress = lpwfs->socketAddress;
3038             pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3039             pPort = (char *)&(dataSocketAddress.sin_port);
3040             pAddr[0] = f[0];
3041             pAddr[1] = f[1];
3042             pAddr[2] = f[2];
3043             pAddr[3] = f[3];
3044             pPort[0] = f[4];
3045             pPort[1] = f[5];
3046
3047             nsocket = socket(AF_INET,SOCK_STREAM,0);
3048             if (nsocket == -1)
3049                 goto lend;
3050
3051             if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3052             {
3053                 ERR("can't connect passive FTP data port.\n");
3054                 closesocket(nsocket);
3055                 goto lend;
3056             }
3057             lpwfs->pasvSocket = nsocket;
3058             bSuccess = TRUE;
3059         }
3060         else
3061             FTP_SetResponseError(nResCode);
3062     }
3063
3064 lend:
3065     return bSuccess;
3066 }
3067
3068
3069 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3070 {
3071     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3072     {
3073         if (!FTP_DoPassive(lpwfs))
3074             return FALSE;
3075     }
3076     else
3077     {
3078         if (!FTP_SendPort(lpwfs))
3079             return FALSE;
3080     }
3081     return TRUE;
3082 }
3083
3084
3085 /***********************************************************************
3086  *           FTP_GetDataSocket (internal)
3087  *
3088  * Either accepts an incoming data socket connection from the server
3089  * or just returns the already opened socket after a PASV command
3090  * in case of passive FTP.
3091  *
3092  *
3093  * RETURNS
3094  *   TRUE on success
3095  *   FALSE on failure
3096  *
3097  */
3098 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3099 {
3100     struct sockaddr_in saddr;
3101     socklen_t addrlen = sizeof(struct sockaddr);
3102
3103     TRACE("\n");
3104     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3105     {
3106         *nDataSocket = lpwfs->pasvSocket;
3107     }
3108     else
3109     {
3110         *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3111         closesocket(lpwfs->lstnSocket);
3112         lpwfs->lstnSocket = -1;
3113     }
3114     return *nDataSocket != -1;
3115 }
3116
3117
3118 /***********************************************************************
3119  *           FTP_SendData (internal)
3120  *
3121  * Send data to the server
3122  *
3123  * RETURNS
3124  *   TRUE on success
3125  *   FALSE on failure
3126  *
3127  */
3128 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3129 {
3130     BY_HANDLE_FILE_INFORMATION fi;
3131     DWORD nBytesRead = 0;
3132     DWORD nBytesSent = 0;
3133     DWORD nTotalSent = 0;
3134     DWORD nBytesToSend, nLen;
3135     int nRC = 1;
3136     time_t s_long_time, e_long_time;
3137     LONG nSeconds;
3138     CHAR *lpszBuffer;
3139
3140     TRACE("\n");
3141     lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3142
3143     /* Get the size of the file. */
3144     GetFileInformationByHandle(hFile, &fi);
3145     time(&s_long_time);
3146
3147     do
3148     {
3149         nBytesToSend = nBytesRead - nBytesSent;
3150
3151         if (nBytesToSend <= 0)
3152         {
3153             /* Read data from file. */
3154             nBytesSent = 0;
3155             if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3156             ERR("Failed reading from file\n");
3157
3158             if (nBytesRead > 0)
3159                 nBytesToSend = nBytesRead;
3160             else
3161                 break;
3162         }
3163
3164         nLen = DATA_PACKET_SIZE < nBytesToSend ?
3165             DATA_PACKET_SIZE : nBytesToSend;
3166         nRC  = send(nDataSocket, lpszBuffer, nLen, 0);
3167
3168         if (nRC != -1)
3169         {
3170             nBytesSent += nRC;
3171             nTotalSent += nRC;
3172         }
3173
3174         /* Do some computation to display the status. */
3175         time(&e_long_time);
3176         nSeconds = e_long_time - s_long_time;
3177         if( nSeconds / 60 > 0 )
3178         {
3179             TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3180             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3181             nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3182         }
3183         else
3184         {
3185             TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3186             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3187             (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3188         }
3189     } while (nRC != -1);
3190
3191     TRACE("file transfer complete!\n");
3192
3193     HeapFree(GetProcessHeap(), 0, lpszBuffer);
3194
3195     return nTotalSent;
3196 }
3197
3198
3199 /***********************************************************************
3200  *           FTP_SendRetrieve (internal)
3201  *
3202  * Send request to retrieve a file
3203  *
3204  * RETURNS
3205  *   Number of bytes to be received on success
3206  *   0 on failure
3207  *
3208  */
3209 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3210 {
3211     INT nResCode;
3212     BOOL ret;
3213
3214     TRACE("\n");
3215     if (!(ret = FTP_InitListenSocket(lpwfs)))
3216         goto lend;
3217
3218     if (!(ret = FTP_SendType(lpwfs, dwType)))
3219         goto lend;
3220
3221     if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3222         goto lend;
3223
3224     if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3225         goto lend;
3226
3227     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3228     if ((nResCode != 125) && (nResCode != 150)) {
3229         /* That means that we got an error getting the file. */
3230         FTP_SetResponseError(nResCode);
3231         ret = FALSE;
3232     }
3233
3234 lend:
3235     if (!ret && lpwfs->lstnSocket != -1)
3236     {
3237         closesocket(lpwfs->lstnSocket);
3238         lpwfs->lstnSocket = -1;
3239     }
3240
3241     return ret;
3242 }
3243
3244
3245 /***********************************************************************
3246  *           FTP_RetrieveData  (internal)
3247  *
3248  * Retrieve data from server
3249  *
3250  * RETURNS
3251  *   TRUE on success
3252  *   FALSE on failure
3253  *
3254  */
3255 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3256 {
3257     DWORD nBytesWritten;
3258     INT nRC = 0;
3259     CHAR *lpszBuffer;
3260
3261     TRACE("\n");
3262
3263     lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3264     if (NULL == lpszBuffer)
3265     {
3266         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3267         return FALSE;
3268     }
3269
3270     while (nRC != -1)
3271     {
3272         nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3273         if (nRC != -1)
3274         {
3275             /* other side closed socket. */
3276             if (nRC == 0)
3277                 goto recv_end;
3278             WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3279         }
3280     }
3281
3282     TRACE("Data transfer complete\n");
3283
3284 recv_end:
3285     HeapFree(GetProcessHeap(), 0, lpszBuffer);
3286
3287     return  (nRC != -1);
3288 }
3289
3290 /***********************************************************************
3291  *           FTPFINDNEXT_Destroy (internal)
3292  *
3293  * Deallocate session handle
3294  */
3295 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3296 {
3297     LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3298     DWORD i;
3299
3300     TRACE("\n");
3301
3302     WININET_Release(&lpwfn->lpFtpSession->hdr);
3303
3304     for (i = 0; i < lpwfn->size; i++)
3305     {
3306         HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3307     }
3308
3309     HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3310     HeapFree(GetProcessHeap(), 0, lpwfn);
3311 }
3312
3313 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3314 {
3315     WIN32_FIND_DATAW *find_data = data;
3316     DWORD res = ERROR_SUCCESS;
3317
3318     TRACE("index(%d) size(%d)\n", find->index, find->size);
3319
3320     ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3321
3322     if (find->index < find->size) {
3323         FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3324         find->index++;
3325
3326         TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3327     }else {
3328         res = ERROR_NO_MORE_FILES;
3329     }
3330
3331     if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3332     {
3333         INTERNET_ASYNC_RESULT iar;
3334
3335         iar.dwResult = (res == ERROR_SUCCESS);
3336         iar.dwError = res;
3337
3338         INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3339                               INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3340                               sizeof(INTERNET_ASYNC_RESULT));
3341     }
3342
3343     return res;
3344 }
3345
3346 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3347 {
3348     struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3349
3350     FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3351 }
3352
3353 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3354 {
3355     switch(option) {
3356     case INTERNET_OPTION_HANDLE_TYPE:
3357         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3358
3359         if (*size < sizeof(ULONG))
3360             return ERROR_INSUFFICIENT_BUFFER;
3361
3362         *size = sizeof(DWORD);
3363         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3364         return ERROR_SUCCESS;
3365     }
3366
3367     return INET_QueryOption(option, buffer, size, unicode);
3368 }
3369
3370 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3371 {
3372     WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3373
3374     if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3375     {
3376         WORKREQUEST workRequest;
3377         struct WORKREQ_FTPFINDNEXTW *req;
3378
3379         workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3380         workRequest.hdr = WININET_AddRef( &find->hdr );
3381         req = &workRequest.u.FtpFindNextW;
3382         req->lpFindFileData = data;
3383
3384         INTERNET_AsyncCall(&workRequest);
3385
3386         return ERROR_SUCCESS;
3387     }
3388
3389     return FTPFINDNEXT_FindNextFileProc(find, data);
3390 }
3391
3392 static const object_vtbl_t FTPFINDNEXTVtbl = {
3393     FTPFINDNEXT_Destroy,
3394     NULL,
3395     FTPFINDNEXT_QueryOption,
3396     NULL,
3397     NULL,
3398     NULL,
3399     NULL,
3400     NULL,
3401     NULL,
3402     FTPFINDNEXT_FindNextFileW
3403 };
3404
3405 /***********************************************************************
3406  *           FTP_ReceiveFileList (internal)
3407  *
3408  * Read file list from server
3409  *
3410  * RETURNS
3411  *   Handle to file list on success
3412  *   NULL on failure
3413  *
3414  */
3415 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3416         LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3417 {
3418     DWORD dwSize = 0;
3419     LPFILEPROPERTIESW lpafp = NULL;
3420     LPWININETFTPFINDNEXTW lpwfn = NULL;
3421     HINTERNET handle = 0;
3422
3423     TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3424
3425     if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3426     {
3427         if(lpFindFileData)
3428             FTP_ConvertFileProp(lpafp, lpFindFileData);
3429
3430         lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3431         if (lpwfn)
3432         {
3433             lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3434             lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3435             lpwfn->hdr.dwContext = dwContext;
3436             lpwfn->hdr.refs = 1;
3437             lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3438             lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3439             lpwfn->size = dwSize;
3440             lpwfn->lpafp = lpafp;
3441
3442             WININET_AddRef( &lpwfs->hdr );
3443             lpwfn->lpFtpSession = lpwfs;
3444             list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3445
3446             handle = WININET_AllocHandle( &lpwfn->hdr );
3447         }
3448     }
3449
3450     if( lpwfn )
3451         WININET_Release( &lpwfn->hdr );
3452
3453     TRACE("Matched %d files\n", dwSize);
3454     return handle;
3455 }
3456
3457
3458 /***********************************************************************
3459  *           FTP_ConvertFileProp (internal)
3460  *
3461  * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3462  *
3463  * RETURNS
3464  *   TRUE on success
3465  *   FALSE on failure
3466  *
3467  */
3468 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3469 {
3470     BOOL bSuccess = FALSE;
3471
3472     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3473
3474     if (lpafp)
3475     {
3476         SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3477         lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3478         lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3479         
3480         /* Not all fields are filled in */
3481         lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3482         lpFindFileData->nFileSizeLow = lpafp->nSize;
3483
3484         if (lpafp->bIsDirectory)
3485             lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3486
3487         if (lpafp->lpszName)
3488             lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3489
3490         bSuccess = TRUE;
3491     }
3492
3493     return bSuccess;
3494 }
3495
3496 /***********************************************************************
3497  *           FTP_ParseNextFile (internal)
3498  *
3499  * Parse the next line in file listing
3500  *
3501  * RETURNS
3502  *   TRUE on success
3503  *   FALSE on failure
3504  */
3505 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3506 {
3507     static const char szSpace[] = " \t";
3508     DWORD nBufLen;
3509     char *pszLine;
3510     char *pszToken;
3511     char *pszTmp;
3512     BOOL found = FALSE;
3513     int i;
3514     
3515     lpfp->lpszName = NULL;
3516     do {
3517         if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3518             return FALSE;
3519     
3520         pszToken = strtok(pszLine, szSpace);
3521         /* ls format
3522          * <Permissions> <NoLinks> <owner>   <group> <size> <date>  <time or year> <filename>
3523          *
3524          * For instance:
3525          * drwx--s---     2         pcarrier  ens     512    Sep 28  1995           pcarrier
3526          */
3527         if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3528             if(!FTP_ParsePermission(pszToken, lpfp))
3529                 lpfp->bIsDirectory = FALSE;
3530             for(i=0; i<=3; i++) {
3531               if(!(pszToken = strtok(NULL, szSpace)))
3532                   break;
3533             }
3534             if(!pszToken) continue;
3535             if(lpfp->bIsDirectory) {
3536                 TRACE("Is directory\n");
3537                 lpfp->nSize = 0;
3538             }
3539             else {
3540                 TRACE("Size: %s\n", pszToken);
3541                 lpfp->nSize = atol(pszToken);
3542             }
3543             
3544             lpfp->tmLastModified.wSecond = 0;
3545             lpfp->tmLastModified.wMinute = 0;
3546             lpfp->tmLastModified.wHour   = 0;
3547             lpfp->tmLastModified.wDay    = 0;
3548             lpfp->tmLastModified.wMonth  = 0;
3549             lpfp->tmLastModified.wYear   = 0;
3550             
3551             /* Determine month */
3552             pszToken = strtok(NULL, szSpace);
3553             if(!pszToken) continue;
3554             if(strlen(pszToken) >= 3) {
3555                 pszToken[3] = 0;
3556                 if((pszTmp = StrStrIA(szMonths, pszToken)))
3557                     lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3558             }
3559             /* Determine day */
3560             pszToken = strtok(NULL, szSpace);
3561             if(!pszToken) continue;
3562             lpfp->tmLastModified.wDay = atoi(pszToken);
3563             /* Determine time or year */
3564             pszToken = strtok(NULL, szSpace);
3565             if(!pszToken) continue;
3566             if((pszTmp = strchr(pszToken, ':'))) {
3567                 SYSTEMTIME curr_time;
3568                 *pszTmp = 0;
3569                 pszTmp++;
3570                 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3571                 lpfp->tmLastModified.wHour = atoi(pszToken);
3572                 GetLocalTime( &curr_time );
3573                 lpfp->tmLastModified.wYear = curr_time.wYear;
3574             }
3575             else {
3576                 lpfp->tmLastModified.wYear = atoi(pszToken);
3577                 lpfp->tmLastModified.wHour = 12;
3578             }
3579             TRACE("Mod time: %02d:%02d:%02d  %04d/%02d/%02d\n",
3580                   lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3581                   lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3582
3583             pszToken = strtok(NULL, szSpace);
3584             if(!pszToken) continue;
3585             lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3586             TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3587         }
3588         /* NT way of parsing ... :
3589             
3590                 07-13-03  08:55PM       <DIR>          sakpatch
3591                 05-09-03  06:02PM             12656686 2003-04-21bgm_cmd_e.rgz
3592         */
3593         else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3594             int mon, mday, year, hour, min;
3595             lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3596             
3597             sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3598             lpfp->tmLastModified.wDay   = mday;
3599             lpfp->tmLastModified.wMonth = mon;
3600             lpfp->tmLastModified.wYear  = year;
3601
3602             /* Hacky and bad Y2K protection :-) */
3603             if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3604
3605             pszToken = strtok(NULL, szSpace);
3606             if(!pszToken) continue;
3607             sscanf(pszToken, "%d:%d", &hour, &min);
3608             lpfp->tmLastModified.wHour   = hour;
3609             lpfp->tmLastModified.wMinute = min;
3610             if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3611                 lpfp->tmLastModified.wHour += 12;
3612             }
3613             lpfp->tmLastModified.wSecond = 0;
3614
3615             TRACE("Mod time: %02d:%02d:%02d  %04d/%02d/%02d\n",
3616                   lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3617                   lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3618
3619             pszToken = strtok(NULL, szSpace);
3620             if(!pszToken) continue;
3621             if(!strcasecmp(pszToken, "<DIR>")) {
3622                 lpfp->bIsDirectory = TRUE;
3623                 lpfp->nSize = 0;
3624                 TRACE("Is directory\n");
3625             }
3626             else {
3627                 lpfp->bIsDirectory = FALSE;
3628                 lpfp->nSize = atol(pszToken);
3629                 TRACE("Size: %d\n", lpfp->nSize);
3630             }
3631             
3632             pszToken = strtok(NULL, szSpace);
3633             if(!pszToken) continue;
3634             lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3635             TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3636         }
3637         /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3638         else if(pszToken[0] == '+') {
3639             FIXME("EPLF Format not implemented\n");
3640         }
3641         
3642         if(lpfp->lpszName) {
3643             if((lpszSearchFile == NULL) ||
3644                (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3645                 found = TRUE;
3646                 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3647             }
3648             else {
3649                 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3650                 lpfp->lpszName = NULL;
3651             }
3652         }
3653     } while(!found);
3654     return TRUE;
3655 }
3656
3657 /***********************************************************************
3658  *           FTP_ParseDirectory (internal)
3659  *
3660  * Parse string of directory information
3661  *
3662  * RETURNS
3663  *   TRUE on success
3664  *   FALSE on failure
3665  */
3666 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3667     LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3668 {
3669     BOOL bSuccess = TRUE;
3670     INT sizeFilePropArray = 500;/*20; */
3671     INT indexFilePropArray = -1;
3672
3673     TRACE("\n");
3674
3675     /* Allocate initial file properties array */
3676     *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3677     if (!*lpafp)
3678         return FALSE;
3679
3680     do {
3681         if (indexFilePropArray+1 >= sizeFilePropArray)
3682         {
3683             LPFILEPROPERTIESW tmpafp;
3684             
3685             sizeFilePropArray *= 2;
3686             tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3687                 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3688             if (NULL == tmpafp)
3689             {
3690                 bSuccess = FALSE;
3691                 break;
3692             }
3693
3694             *lpafp = tmpafp;
3695         }
3696         indexFilePropArray++;
3697     } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3698
3699     if (bSuccess && indexFilePropArray)
3700     {
3701         if (indexFilePropArray < sizeFilePropArray - 1)
3702         {
3703             LPFILEPROPERTIESW tmpafp;
3704
3705             tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3706                 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3707             if (NULL != tmpafp)
3708                 *lpafp = tmpafp;
3709         }
3710         *dwfp = indexFilePropArray;
3711     }
3712     else
3713     {
3714         HeapFree(GetProcessHeap(), 0, *lpafp);
3715         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3716         bSuccess = FALSE;
3717     }
3718
3719     return bSuccess;
3720 }
3721
3722
3723 /***********************************************************************
3724  *           FTP_ParsePermission (internal)
3725  *
3726  * Parse permission string of directory information
3727  *
3728  * RETURNS
3729  *   TRUE on success
3730  *   FALSE on failure
3731  *
3732  */
3733 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3734 {
3735     BOOL bSuccess = TRUE;
3736     unsigned short nPermission = 0;
3737     INT nPos = 1;
3738     INT nLast  = 9;
3739
3740     TRACE("\n");
3741     if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3742     {
3743         bSuccess = FALSE;
3744         return bSuccess;
3745     }
3746
3747     lpfp->bIsDirectory = (*lpszPermission == 'd');
3748     do
3749     {
3750         switch (nPos)
3751         {
3752             case 1:
3753                 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3754                 break;
3755             case 2:
3756                 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3757                 break;
3758             case 3:
3759                 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3760                 break;
3761             case 4:
3762                 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3763                 break;
3764             case 5:
3765                 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3766                 break;
3767             case 6:
3768                 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3769                 break;
3770             case 7:
3771                 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3772                 break;
3773             case 8:
3774                 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3775                 break;
3776             case 9:
3777                 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3778                 break;
3779         }
3780         nPos++;
3781     }while (nPos <= nLast);
3782
3783     lpfp->permissions = nPermission;
3784     return bSuccess;
3785 }
3786
3787
3788 /***********************************************************************
3789  *           FTP_SetResponseError (internal)
3790  *
3791  * Set the appropriate error code for a given response from the server
3792  *
3793  * RETURNS
3794  *
3795  */
3796 static DWORD FTP_SetResponseError(DWORD dwResponse)
3797 {
3798     DWORD dwCode = 0;
3799
3800     switch(dwResponse)
3801     {
3802     case 425: /* Cannot open data connection. */
3803         dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3804         break;
3805
3806     case 426: /* Connection closed, transer aborted. */
3807         dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3808         break;
3809
3810     case 530: /* Not logged in. Login incorrect. */
3811         dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3812         break;
3813
3814     case 421: /* Service not available - Server may be shutting down. */
3815     case 450: /* File action not taken. File may be busy. */
3816     case 451: /* Action aborted. Server error. */
3817     case 452: /* Action not taken. Insufficient storage space on server. */
3818     case 500: /* Syntax error. Command unrecognized. */
3819     case 501: /* Syntax error. Error in parameters or arguments. */
3820     case 502: /* Command not implemented. */
3821     case 503: /* Bad sequence of commands. */
3822     case 504: /* Command not implemented for that parameter. */
3823     case 532: /* Need account for storing files */
3824     case 550: /* File action not taken. File not found or no access. */
3825     case 551: /* Requested action aborted. Page type unknown */
3826     case 552: /* Action aborted. Exceeded storage allocation */
3827     case 553: /* Action not taken. File name not allowed. */
3828
3829     default:
3830         dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3831         break;
3832     }
3833
3834     INTERNET_SetLastError(dwCode);
3835     return dwCode;
3836 }