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