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