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