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