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