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