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