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