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