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