Faster serial speed cases for non Linux systems.
[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  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25
26 #include "config.h"
27
28 #include <errno.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/types.h>
33 #ifdef HAVE_SYS_SOCKET_H
34 # include <sys/socket.h>
35 #endif
36 #include <unistd.h>
37 #include <time.h>
38
39 #include "winbase.h"
40 #include "wingdi.h"
41 #include "winuser.h"
42 #include "wininet.h"
43 #include "winerror.h"
44
45 #include "wine/debug.h"
46 #include "internet.h"
47
48 WINE_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.@)
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 != -1)
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.@)
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.@)
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.@)
456  *
457  * Search the specified directory
458  *
459  * RETURNS
460  *    HINTERNET on success
461  *    NULL on failure
462  *
463  */
464 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 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 != -1)
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.@)
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.@)
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 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 != -1)
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.@)
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 != -1)
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.@)
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.@)
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.@)
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 = -1, 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     nsocket = socket(AF_INET,SOCK_STREAM,0);
1318     if (nsocket == -1)
1319     {
1320         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1321         goto lerror;
1322     }
1323
1324     if (hIC->lpfnStatusCB)
1325         hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
1326              &socketAddr, sizeof(struct sockaddr_in));
1327
1328     if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
1329     {
1330         ERR("Unable to connect (%s)\n", strerror(errno));
1331         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1332     }
1333     else
1334     {
1335         TRACE("Connected to server\n"); 
1336         if (hIC->lpfnStatusCB)
1337             hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
1338                 &socketAddr, sizeof(struct sockaddr_in));
1339
1340         lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONA));
1341         if (NULL == lpwfs)
1342         {
1343             INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1344             goto lerror;
1345         }
1346
1347         lpwfs->hdr.htype = WH_HFTPSESSION;
1348         lpwfs->hdr.dwFlags = dwFlags;
1349         lpwfs->hdr.dwContext = dwContext;
1350         lpwfs->hdr.lpwhparent = (LPWININETHANDLEHEADER)hInternet;
1351         lpwfs->sndSocket = nsocket;
1352         sock_namelen = sizeof(lpwfs->socketAddress);
1353         getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
1354         lpwfs->phostent = phe;
1355
1356         if (NULL == lpszUserName)
1357         {
1358             lpwfs->lpszUserName = FTP_strdup("anonymous");
1359             lpwfs->lpszPassword = FTP_strdup("user@server");
1360         }
1361         else
1362         {
1363             lpwfs->lpszUserName = FTP_strdup(lpszUserName);
1364             lpwfs->lpszPassword = FTP_strdup(lpszPassword);
1365         }
1366
1367         if (FTP_ConnectToHost(lpwfs))
1368         {
1369             if (hIC->lpfnStatusCB)
1370             {
1371                 INTERNET_ASYNC_RESULT iar;
1372
1373                 iar.dwResult = (DWORD)lpwfs;
1374                 iar.dwError = ERROR_SUCCESS;
1375
1376                 hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_HANDLE_CREATED,
1377                     &iar, sizeof(INTERNET_ASYNC_RESULT));
1378             }
1379             TRACE("Successfully logged into server\n");
1380             bSuccess = TRUE;
1381         }
1382     }
1383
1384 lerror:
1385     if (!bSuccess && nsocket == -1)
1386         close(nsocket);
1387
1388     if (!bSuccess && lpwfs)
1389     {
1390         HeapFree(GetProcessHeap(), 0, lpwfs);
1391         lpwfs = NULL;
1392     }
1393
1394     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
1395     {
1396         INTERNET_ASYNC_RESULT iar;
1397                
1398         iar.dwResult = (DWORD)lpwfs;
1399         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1400         hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1401             &iar, sizeof(INTERNET_ASYNC_RESULT));
1402     }
1403
1404     return (HINTERNET) lpwfs;
1405 }
1406
1407
1408 /***********************************************************************
1409  *           FTP_ConnectToHost (internal)
1410  *
1411  * Connect to a ftp server
1412  *
1413  * RETURNS
1414  *   TRUE on success
1415  *   NULL on failure
1416  *
1417  */
1418 BOOL FTP_ConnectToHost(LPWININETFTPSESSIONA lpwfs)
1419 {
1420     INT nResCode;
1421     BOOL bSuccess = FALSE;
1422
1423     TRACE("\n");
1424     FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0);
1425
1426     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
1427         goto lend;
1428
1429     nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1430         MAX_REPLY_LEN, 0, 0, 0);
1431     if (nResCode)
1432     {
1433         /* Login successful... */
1434         if (nResCode == 230)
1435             bSuccess = TRUE;
1436         /* User name okay, need password... */
1437         else if (nResCode == 331)
1438             bSuccess = FTP_SendPassword(lpwfs);
1439         /* Need account for login... */
1440         else if (nResCode == 332)
1441             bSuccess = FTP_SendAccount(lpwfs);
1442         else
1443             FTP_SetResponseError(nResCode);
1444     }
1445
1446     TRACE("Returning %d\n", bSuccess);
1447 lend:
1448     return bSuccess;
1449 }
1450
1451
1452 /***********************************************************************
1453  *           FTP_SendCommand (internal)
1454  *
1455  * Send command to server
1456  *
1457  * RETURNS
1458  *   TRUE on success
1459  *   NULL on failure
1460  *
1461  */
1462 BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
1463         INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext)
1464 {
1465         DWORD len;
1466         CHAR *buf;
1467         DWORD nBytesSent = 0;
1468         DWORD nRC = 0;
1469         BOOL bParamHasLen;
1470
1471         TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
1472
1473         if (lpfnStatusCB)
1474                 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
1475
1476         bParamHasLen = lpszParam && strlen(lpszParam) > 0;
1477         len = (bParamHasLen ? strlen(lpszParam) : -1) + strlen(szFtpCommands[ftpCmd]) +
1478             strlen(szCRLF)+ 1;
1479         if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
1480         {
1481             INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1482             return FALSE;
1483         }
1484         sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], bParamHasLen ? " " : "", 
1485                 bParamHasLen ? lpszParam : "", szCRLF);
1486
1487         TRACE("Sending (%s) len(%ld)\n", buf, len);
1488         while((nBytesSent < len) && (nRC != -1))
1489         {
1490                 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
1491                 nBytesSent += nRC;
1492         }
1493
1494         HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
1495
1496         if (lpfnStatusCB)
1497                 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_REQUEST_SENT, 
1498                         &nBytesSent, sizeof(DWORD));
1499
1500         TRACE("Sent %ld bytes\n", nBytesSent);
1501         return (nRC != -1);
1502 }
1503
1504
1505 /***********************************************************************
1506  *           FTP_ReceiveResponse (internal)
1507  *
1508  * Receive response from server
1509  *
1510  * RETURNS
1511  *   Reply code on success
1512  *   0 on failure
1513  *
1514  */
1515
1516 INT FTP_ReceiveResponse(INT nSocket, LPSTR lpszResponse, DWORD dwResponse,
1517         INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext)
1518 {
1519     DWORD nRecv;
1520     INT rc = 0;
1521     char firstprefix[5];
1522     BOOL multiline = FALSE;
1523
1524
1525     TRACE("socket(%d) \n", nSocket);
1526
1527     if (lpfnStatusCB)
1528         lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
1529
1530     while(1)
1531     {
1532         nRecv = dwResponse;
1533         if (!INTERNET_GetNextLine(nSocket, lpszResponse, &nRecv))
1534             goto lerror;
1535
1536         if (nRecv >= 3)
1537         {
1538             if(!multiline)
1539             {
1540                 if(lpszResponse[3] != '-')
1541                     break;
1542                 else
1543                 {  /* Start of multiline repsonse.  Loop until we get "nnn " */
1544                     multiline = TRUE;
1545                     memcpy(firstprefix, lpszResponse, 3);
1546                     firstprefix[3] = ' ';
1547                     firstprefix[4] = '\0';
1548                 }
1549             }
1550             else
1551             {
1552                 if(!memcmp(firstprefix, lpszResponse, 4))
1553                     break;
1554             }
1555         }
1556     }
1557                    
1558     if (nRecv >= 3)
1559     {
1560         lpszResponse[nRecv] = '\0';
1561         rc = atoi(lpszResponse);
1562
1563         if (lpfnStatusCB)
1564             lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED, 
1565                     &nRecv, sizeof(DWORD));
1566     }
1567
1568 lerror:
1569     TRACE("return %d\n", rc);
1570     return rc;
1571 }
1572
1573
1574 /***********************************************************************
1575  *           FTP_SendPassword (internal)
1576  *
1577  * Send password to ftp server
1578  *
1579  * RETURNS
1580  *   TRUE on success
1581  *   NULL on failure
1582  *
1583  */
1584 BOOL FTP_SendPassword(LPWININETFTPSESSIONA lpwfs)
1585 {
1586     INT nResCode;
1587     BOOL bSuccess = FALSE;
1588
1589     TRACE("\n");
1590     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
1591         goto lend;
1592            
1593     nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1594         MAX_REPLY_LEN, 0, 0, 0);
1595     if (nResCode)
1596     {
1597         TRACE("Received reply code %d\n", nResCode);
1598         /* Login successful... */
1599         if (nResCode == 230)
1600             bSuccess = TRUE;
1601         /* Command not implemented, superfluous at the server site... */
1602         /* Need account for login... */
1603         else if (nResCode == 332)
1604             bSuccess = FTP_SendAccount(lpwfs);
1605         else
1606             FTP_SetResponseError(nResCode);
1607     }
1608
1609 lend:
1610     TRACE("Returning %d\n", bSuccess);
1611     return bSuccess;
1612 }
1613
1614
1615 /***********************************************************************
1616  *           FTP_SendAccount (internal)
1617  *
1618  * 
1619  *
1620  * RETURNS
1621  *   TRUE on success
1622  *   FALSE on failure
1623  *
1624  */
1625 BOOL FTP_SendAccount(LPWININETFTPSESSIONA lpwfs)
1626 {
1627     INT nResCode;
1628     BOOL bSuccess = FALSE;
1629
1630     TRACE("\n");
1631     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, NOACCOUNT, 0, 0, 0))
1632         goto lend;
1633
1634     nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1635         MAX_REPLY_LEN, 0,  0, 0);
1636     if (nResCode)
1637         bSuccess = TRUE;
1638     else
1639         FTP_SetResponseError(nResCode);
1640
1641 lend:
1642     return bSuccess;
1643 }
1644
1645
1646 /***********************************************************************
1647  *           FTP_SendStore (internal)
1648  *
1649  * Send request to upload file to ftp server
1650  *
1651  * RETURNS
1652  *   TRUE on success
1653  *   FALSE on failure
1654  *
1655  */
1656 BOOL FTP_SendStore(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType)
1657 {
1658     INT nResCode;
1659     BOOL bSuccess = FALSE;
1660
1661     TRACE("\n");
1662     if (!FTP_InitListenSocket(lpwfs))
1663         goto lend;
1664
1665     if (!FTP_SendType(lpwfs, dwType))
1666         goto lend;
1667
1668     if (!FTP_SendPortOrPasv(lpwfs))
1669         goto lend;
1670
1671     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
1672             goto lend;
1673     nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1674        MAX_REPLY_LEN, 0, 0, 0);
1675     if (nResCode)
1676     {
1677         if (nResCode == 150)
1678             bSuccess = TRUE;
1679         else
1680             FTP_SetResponseError(nResCode);
1681     }
1682
1683 lend:
1684     if (!bSuccess && lpwfs->lstnSocket != -1)
1685     {
1686         close(lpwfs->lstnSocket);
1687         lpwfs->lstnSocket = -1;
1688     }
1689
1690     return bSuccess;
1691 }
1692
1693
1694 /***********************************************************************
1695  *           FTP_InitListenSocket (internal)
1696  *
1697  * Create a socket to listen for server response
1698  *
1699  * RETURNS
1700  *   TRUE on success
1701  *   FALSE on failure
1702  *
1703  */
1704 BOOL FTP_InitListenSocket(LPWININETFTPSESSIONA lpwfs)
1705 {
1706     BOOL bSuccess = FALSE;
1707     size_t namelen = sizeof(struct sockaddr_in);
1708
1709     TRACE("\n");
1710
1711     lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
1712     if (lpwfs->lstnSocket == -1)
1713     {
1714         TRACE("Unable to create listening socket\n");
1715             goto lend;
1716     }
1717
1718     /* We obtain our ip addr from the name of the command channel socket */
1719     lpwfs->lstnSocketAddress = lpwfs->socketAddress;
1720
1721     /* and get the system to assign us a port */
1722     lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
1723
1724     if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
1725     {
1726         TRACE("Unable to bind socket\n");
1727         goto lend;
1728     }
1729
1730     if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
1731     {
1732         TRACE("listen failed\n");
1733         goto lend;
1734     }
1735
1736     if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
1737         bSuccess = TRUE;
1738
1739 lend:
1740     if (!bSuccess && lpwfs->lstnSocket == -1)
1741     {
1742         close(lpwfs->lstnSocket);
1743         lpwfs->lstnSocket = -1;
1744     }
1745
1746     return bSuccess;
1747 }
1748
1749
1750 /***********************************************************************
1751  *           FTP_SendType (internal)
1752  *
1753  * Tell server type of data being transferred
1754  *
1755  * RETURNS
1756  *   TRUE on success
1757  *   FALSE on failure
1758  *
1759  * W98SE doesn't cache the type that's currently set
1760  * (i.e. it sends it always),
1761  * so we probably don't want to do that either.
1762  */
1763 BOOL FTP_SendType(LPWININETFTPSESSIONA lpwfs, DWORD dwType)
1764 {
1765     INT nResCode;
1766     CHAR type[2] = { "I\0" };
1767     BOOL bSuccess = FALSE;
1768
1769     TRACE("\n");
1770     if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
1771         *type = 'A';
1772
1773     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
1774         goto lend;
1775
1776     nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1777         MAX_REPLY_LEN, 0, 0, 0)/100;
1778     if (nResCode)
1779     {
1780         if (nResCode == 2)
1781             bSuccess = TRUE;
1782         else
1783             FTP_SetResponseError(nResCode);
1784     }
1785
1786 lend:
1787     return bSuccess;
1788 }
1789
1790
1791 /***********************************************************************
1792  *           FTP_SendPort (internal)
1793  *
1794  * Tell server which port to use
1795  *
1796  * RETURNS
1797  *   TRUE on success
1798  *   FALSE on failure
1799  *
1800  */
1801 BOOL FTP_SendPort(LPWININETFTPSESSIONA lpwfs)
1802 {
1803     INT nResCode;
1804     CHAR szIPAddress[64];
1805     BOOL bSuccess = FALSE;
1806     TRACE("\n");
1807
1808     sprintf(szIPAddress, "%d,%d,%d,%d,%d,%d",
1809          lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
1810         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
1811         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
1812         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
1813         lpwfs->lstnSocketAddress.sin_port & 0xFF,
1814         (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
1815
1816     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
1817         goto lend;
1818
1819     nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1820         MAX_REPLY_LEN,0, 0, 0);
1821     if (nResCode)
1822     {
1823         if (nResCode == 200)
1824             bSuccess = TRUE;
1825         else
1826             FTP_SetResponseError(nResCode);
1827     }
1828
1829 lend:
1830     return bSuccess;
1831 }
1832
1833
1834 /***********************************************************************
1835  *           FTP_DoPassive (internal)
1836  *
1837  * Tell server that we want to do passive transfers
1838  * and connect data socket
1839  *
1840  * RETURNS
1841  *   TRUE on success
1842  *   FALSE on failure
1843  *
1844  */
1845 BOOL FTP_DoPassive(LPWININETFTPSESSIONA lpwfs)
1846 {
1847     INT nResCode;
1848     BOOL bSuccess = FALSE;
1849
1850     TRACE("\n");
1851     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
1852         goto lend;
1853
1854     nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1855         MAX_REPLY_LEN,0, 0, 0);
1856     if (nResCode)
1857     {
1858         if (nResCode == 227)
1859         {
1860             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
1861             LPSTR p;
1862             int f[6];
1863             int i;
1864             char *pAddr, *pPort;
1865             INT nsocket = -1;
1866             struct sockaddr_in dataSocketAddress;
1867
1868             p = lpszResponseBuffer+4; /* skip status code */
1869
1870             /* do a very strict check; we can improve that later. */
1871
1872             if (strncmp(p, "Entering Passive Mode", 21))
1873             {
1874                 ERR("unknown response '%.*s', aborting\n", 21, p);
1875                 goto lend;
1876             }
1877             p += 21; /* skip string */
1878             if ((*p++ != ' ') || (*p++ != '('))
1879             {
1880                 ERR("unknown response format, aborting\n");
1881                 goto lend;
1882             }
1883
1884             if (sscanf(p, "%d,%d,%d,%d,%d,%d",  &f[0], &f[1], &f[2], &f[3],
1885                                                 &f[4], &f[5]) != 6)
1886             {
1887                 ERR("unknown response address format '%s', aborting\n", p);
1888                 goto lend;
1889             }
1890             for (i=0; i < 6; i++)
1891                 f[i] = f[i] & 0xff;
1892
1893             dataSocketAddress = lpwfs->socketAddress;
1894             pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
1895             pPort = (char *)&(dataSocketAddress.sin_port);
1896             pAddr[0] = f[0];
1897             pAddr[1] = f[1];
1898             pAddr[2] = f[2];
1899             pAddr[3] = f[3];
1900             pPort[0] = f[4];
1901             pPort[1] = f[5];
1902
1903             nsocket = socket(AF_INET,SOCK_STREAM,0);
1904             if (nsocket == -1)
1905                 goto lend;
1906
1907             if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
1908             {
1909                 ERR("can't connect passive FTP data port.\n");
1910                 goto lend;
1911             }
1912             lpwfs->pasvSocket = nsocket;
1913             bSuccess = TRUE;
1914         }
1915         else
1916             FTP_SetResponseError(nResCode);
1917     }
1918
1919 lend:
1920     return bSuccess;
1921 }
1922
1923
1924 BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONA lpwfs)
1925 {
1926     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
1927     {
1928         if (!FTP_DoPassive(lpwfs))
1929             return FALSE;
1930     }
1931     else
1932     {
1933         if (!FTP_SendPort(lpwfs))
1934             return FALSE;
1935     }
1936     return TRUE;
1937 }
1938
1939
1940 /***********************************************************************
1941  *           FTP_GetDataSocket (internal)
1942  *
1943  * Either accepts an incoming data socket connection from the server
1944  * or just returns the already opened socket after a PASV command
1945  * in case of passive FTP.
1946  * 
1947  *
1948  * RETURNS
1949  *   TRUE on success
1950  *   FALSE on failure
1951  *
1952  */
1953 BOOL FTP_GetDataSocket(LPWININETFTPSESSIONA lpwfs, LPINT nDataSocket)
1954 {
1955     struct sockaddr_in saddr;
1956     size_t addrlen = sizeof(struct sockaddr);
1957
1958     TRACE("\n");
1959     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
1960     {
1961         *nDataSocket = lpwfs->pasvSocket;
1962     }
1963     else
1964     {
1965         *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
1966         close(lpwfs->lstnSocket);
1967         lpwfs->lstnSocket = -1;
1968     }
1969     return *nDataSocket != -1;
1970 }
1971
1972
1973 /***********************************************************************
1974  *           FTP_SendData (internal)
1975  *
1976  * Send data to the server
1977  *
1978  * RETURNS
1979  *   TRUE on success
1980  *   FALSE on failure
1981  *
1982  */
1983 BOOL FTP_SendData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, HANDLE hFile)
1984 {
1985     BY_HANDLE_FILE_INFORMATION fi;
1986     DWORD nBytesRead = 0;
1987     DWORD nBytesSent = 0;
1988     DWORD nTotalSent = 0;
1989     DWORD nBytesToSend, nLen, nRC = 1;
1990     time_t s_long_time, e_long_time;
1991     LONG nSeconds;
1992     CHAR *lpszBuffer;
1993
1994     TRACE("\n");
1995     lpszBuffer = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR)*DATA_PACKET_SIZE);
1996     memset(lpszBuffer, 0, sizeof(CHAR)*DATA_PACKET_SIZE);
1997
1998     /* Get the size of the file. */
1999     GetFileInformationByHandle(hFile, &fi);
2000     time(&s_long_time);
2001
2002     do
2003     {
2004         nBytesToSend = nBytesRead - nBytesSent;
2005
2006         if (nBytesToSend <= 0)
2007         {
2008             /* Read data from file. */
2009             nBytesSent = 0;
2010             if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
2011             ERR("Failed reading from file\n");
2012
2013             if (nBytesRead > 0)
2014                 nBytesToSend = nBytesRead;
2015             else
2016                 break;
2017         }
2018
2019         nLen = DATA_PACKET_SIZE < nBytesToSend ? 
2020             DATA_PACKET_SIZE : nBytesToSend;
2021         nRC  = send(nDataSocket, lpszBuffer, nLen, 0);
2022
2023         if (nRC != -1)
2024         {
2025             nBytesSent += nRC;
2026             nTotalSent += nRC;
2027         }
2028
2029         /* Do some computation to display the status. */
2030         time(&e_long_time);
2031         nSeconds = e_long_time - s_long_time;
2032         if( nSeconds / 60 > 0 )
2033         {
2034             TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld min %ld sec estimated remainig time %ld sec\n",
2035             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60, 
2036             nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
2037         }
2038         else
2039         {
2040             TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld sec estimated remainig time %ld sec\n",
2041             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
2042             (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
2043         }
2044     } while (nRC != -1);
2045
2046     TRACE("file transfer complete!\n");
2047
2048     if(lpszBuffer != NULL)
2049         HeapFree(GetProcessHeap(), 0, lpszBuffer);
2050
2051     return nTotalSent;
2052 }
2053
2054
2055 /***********************************************************************
2056  *           FTP_SendRetrieve (internal)
2057  *
2058  * Send request to retrieve a file
2059  *
2060  * RETURNS
2061  *   Number of bytes to be received on success
2062  *   0 on failure
2063  *
2064  */
2065 DWORD FTP_SendRetrieve(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType)
2066 {
2067     INT nResCode;
2068     DWORD nResult = 0;
2069
2070     TRACE("\n");
2071     if (!FTP_InitListenSocket(lpwfs))
2072         goto lend;
2073
2074     if (!FTP_SendType(lpwfs, dwType))
2075         goto lend;
2076
2077     if (!FTP_SendPortOrPasv(lpwfs))
2078         goto lend;
2079
2080     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0))
2081         goto lend;
2082
2083     nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
2084         MAX_REPLY_LEN, 0, 0, 0);
2085     if (nResCode)
2086     {
2087         if (nResCode == 125 || nResCode == 150)
2088         {
2089             /* Parse size of data to be retrieved */
2090             INT i, sizepos = -1;
2091             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2092             for (i = strlen(lpszResponseBuffer) - 1; i >= 0; i--)
2093             {
2094                 if ('(' == lpszResponseBuffer[i])
2095                 {
2096                     sizepos = i;
2097                     break;
2098                 }
2099             }
2100
2101             if (sizepos >= 0)
2102             {
2103                 nResult = atol(&lpszResponseBuffer[sizepos+1]);
2104                 TRACE("Waiting to receive %ld bytes\n", nResult);
2105             }
2106         }
2107     }
2108
2109 lend:
2110     if (0 == nResult && lpwfs->lstnSocket != -1)
2111     {
2112         close(lpwfs->lstnSocket);
2113         lpwfs->lstnSocket = -1;
2114     }
2115
2116     return nResult;
2117 }
2118
2119
2120 /***********************************************************************
2121  *           FTP_RetrieveData  (internal)
2122  *
2123  * Retrieve data from server
2124  *
2125  * RETURNS
2126  *   TRUE on success
2127  *   FALSE on failure
2128  *
2129  */
2130 BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile)
2131 {
2132     DWORD nBytesWritten;
2133     DWORD nBytesReceived = 0;
2134     INT nRC = 0;
2135     CHAR *lpszBuffer;
2136
2137     TRACE("\n");
2138
2139     if (INVALID_HANDLE_VALUE == hFile)
2140         return FALSE;
2141
2142     lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2143     if (NULL == lpszBuffer)
2144     {
2145         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2146         return FALSE;
2147     }
2148
2149     while (nBytesReceived < nBytes && nRC != -1)
2150     {
2151         nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
2152         if (nRC != -1)
2153         {
2154             /* other side closed socket. */
2155             if (nRC == 0)
2156                 goto recv_end;
2157             WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL); 
2158             nBytesReceived += nRC;
2159         }
2160
2161         TRACE("%ld bytes of %ld (%ld%%)\r", nBytesReceived, nBytes, 
2162            nBytesReceived * 100 / nBytes);
2163     }
2164
2165     TRACE("Data transfer complete\n");
2166     if (NULL != lpszBuffer)
2167         HeapFree(GetProcessHeap(), 0, lpszBuffer);
2168
2169 recv_end:
2170     return  (nRC != -1);
2171 }
2172
2173
2174 /***********************************************************************
2175  *           FTP_CloseSessionHandle (internal)
2176  *
2177  * Deallocate session handle
2178  *
2179  * RETURNS
2180  *   TRUE on success
2181  *   FALSE on failure
2182  *
2183  */
2184 BOOL FTP_CloseSessionHandle(LPWININETFTPSESSIONA lpwfs)
2185 {
2186     if (lpwfs->sndSocket != -1)
2187         close(lpwfs->sndSocket);
2188
2189     if (lpwfs->lstnSocket != -1)
2190         close(lpwfs->lstnSocket);
2191
2192     if (lpwfs->lpszPassword)
2193         HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2194
2195     if (lpwfs->lpszUserName)
2196         HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2197
2198     HeapFree(GetProcessHeap(), 0, lpwfs);
2199
2200     return TRUE;
2201 }
2202
2203
2204 /***********************************************************************
2205  *           FTP_CloseSessionHandle (internal)
2206  *
2207  * Deallocate session handle
2208  *
2209  * RETURNS
2210  *   TRUE on success
2211  *   FALSE on failure
2212  *
2213  */
2214 BOOL FTP_CloseFindNextHandle(LPWININETFINDNEXTA lpwfn)
2215 {
2216     INT i; 
2217
2218     TRACE("\n");
2219
2220     for (i = 0; i < lpwfn->size; i++)
2221     {
2222         if (NULL != lpwfn->lpafp[i].lpszName)
2223             HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
2224     }
2225
2226     HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
2227     HeapFree(GetProcessHeap(), 0, lpwfn);
2228
2229     return TRUE;
2230 }
2231
2232
2233 /***********************************************************************
2234  *           FTP_ReceiveFileList (internal)
2235  *
2236  * Read file list from server
2237  *
2238  * RETURNS
2239  *   Handle to file list on success
2240  *   NULL on failure
2241  *
2242  */
2243 HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONA lpwfs, INT nSocket, 
2244         LPWIN32_FIND_DATAA lpFindFileData, DWORD dwContext)
2245 {
2246     DWORD dwSize = 0;
2247     LPFILEPROPERTIESA lpafp = NULL;
2248     LPWININETFINDNEXTA lpwfn = NULL;
2249
2250     TRACE("\n");
2251
2252     if (FTP_ParseDirectory(lpwfs, nSocket, &lpafp, &dwSize))
2253     {
2254         FTP_ConvertFileProp(lpafp, lpFindFileData);
2255
2256         lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFINDNEXTA));
2257         if (NULL != lpwfn)
2258         {
2259             lpwfn->hdr.htype = WH_HFINDNEXT;
2260             lpwfn->hdr.lpwhparent = (LPWININETHANDLEHEADER)lpwfs;
2261             lpwfn->hdr.dwContext = dwContext; 
2262             lpwfn->index = 1; /* Next index is 1 since we return index 0 */
2263             lpwfn->size = dwSize;
2264             lpwfn->lpafp = lpafp;
2265         }
2266     } 
2267
2268     TRACE("Matched %ld files\n", dwSize);
2269     return (HINTERNET)lpwfn;
2270 }
2271
2272
2273 /***********************************************************************
2274  *           FTP_ConvertFileProp (internal)
2275  *
2276  * Converts FILEPROPERTIESA struct to WIN32_FIND_DATAA
2277  *
2278  * RETURNS
2279  *   TRUE on success
2280  *   FALSE on failure
2281  *
2282  */
2283 BOOL FTP_ConvertFileProp(LPFILEPROPERTIESA lpafp, LPWIN32_FIND_DATAA lpFindFileData)
2284 {
2285     BOOL bSuccess = FALSE;
2286
2287     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
2288
2289     if (lpafp)
2290     {
2291         DWORD access = mktime(&lpafp->tmLastModified);
2292                 
2293         /* Not all fields are filled in */
2294         lpFindFileData->ftLastAccessTime.dwHighDateTime = HIWORD(access);
2295         lpFindFileData->ftLastAccessTime.dwLowDateTime  = LOWORD(access);
2296         lpFindFileData->nFileSizeHigh = HIWORD(lpafp->nSize);
2297         lpFindFileData->nFileSizeLow = LOWORD(lpafp->nSize);
2298
2299         if (lpafp->bIsDirectory)
2300             lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
2301
2302         if (lpafp->lpszName)
2303             strncpy(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
2304
2305         bSuccess = TRUE;
2306     }
2307
2308     return bSuccess;
2309 }
2310
2311
2312 /***********************************************************************
2313  *           FTP_ParseDirectory (internal)
2314  *
2315  * Parse string of directory information
2316  *
2317  * RETURNS
2318  *   TRUE on success
2319  *   FALSE on failure
2320  *
2321  * FIXME: - This function needs serious clea-up
2322  *        - We should consider both UNIX and NT list formats
2323  */
2324 #define MAX_MONTH_LEN 10
2325 #define MIN_LEN_DIR_ENTRY 15
2326
2327 BOOL FTP_ParseDirectory(LPWININETFTPSESSIONA lpwfs, INT nSocket, LPFILEPROPERTIESA *lpafp, LPDWORD dwfp)
2328 {
2329   /*
2330    * <Permissions> <NoLinks> <owner>   <group> <size> <date>  <time or year> <filename>
2331    *
2332    * For instance:
2333    * drwx--s---     2         pcarrier  ens     512    Sep 28  1995           pcarrier
2334    */
2335     CHAR* pszMinutes;
2336     CHAR* pszHour;
2337     time_t aTime;
2338     struct tm* apTM;
2339     CHAR pszMonth[MAX_MONTH_LEN];
2340     CHAR* pszMatch;
2341     BOOL bSuccess = TRUE;
2342     DWORD nBufLen = MAX_REPLY_LEN;
2343     LPFILEPROPERTIESA curFileProp = NULL;
2344     CHAR* pszLine  = NULL;
2345     CHAR* pszToken = NULL;
2346     INT nTokenToSkip = 3;
2347     INT nCount = 0;
2348     INT nSeconds = 0;
2349     INT nMinutes = 0;
2350     INT nHour    = 0;
2351     INT nDay     = 0;
2352     INT nMonth   = 0;
2353     INT nYear    = 0;
2354     INT sizeFilePropArray = 20;
2355     INT indexFilePropArray = 0;
2356
2357     TRACE("\n");
2358
2359     /* Allocate intial file properties array */
2360     *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESA)*(sizeFilePropArray));
2361     if (NULL == lpafp)
2362     {
2363         bSuccess = FALSE;
2364         goto lend;
2365     }
2366
2367     while ((pszLine = INTERNET_GetNextLine(nSocket, INTERNET_GetResponseBuffer(), &nBufLen)) != NULL)
2368     {
2369         if (sizeFilePropArray <= indexFilePropArray)
2370         {
2371             LPFILEPROPERTIESA tmpafp;
2372
2373             sizeFilePropArray *= 2;
2374             tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp, 
2375                 sizeof(FILEPROPERTIESA)*sizeFilePropArray);
2376             if (NULL == tmpafp)
2377             {
2378                 bSuccess = FALSE;
2379                 goto lend;
2380             }
2381
2382             *lpafp = tmpafp;
2383         }
2384
2385         curFileProp = &((*lpafp)[indexFilePropArray]);
2386
2387         /* First Parse the permissions. */
2388         pszToken = strtok(pszLine, " \t" );
2389
2390         /* HACK! If this is not a file listing skip the line */
2391         if (!pszToken || 10 != strlen(pszToken) || nBufLen <= MIN_LEN_DIR_ENTRY)
2392         {
2393             nBufLen = MAX_REPLY_LEN;
2394             continue;
2395         }
2396
2397         FTP_ParsePermission(pszToken, curFileProp);
2398
2399         nTokenToSkip = 3;
2400         nCount = 0;
2401         do
2402         {
2403             pszToken = strtok( NULL, " \t" );
2404             nCount++;
2405         } while( nCount <= nTokenToSkip );
2406
2407         /* Store the size of the file in the param list. */
2408         TRACE("nSize-> %s\n", pszToken);
2409         if (pszToken != NULL)
2410             curFileProp->nSize = atol(pszToken);
2411
2412         /* Parse last modified time. */
2413         nSeconds = 0;
2414         nMinutes = 0;
2415         nHour    = 0;
2416         nDay     = 0;
2417         nMonth   = 0;
2418         nYear    = 0;
2419
2420         pszToken = strtok( NULL, " \t" );
2421         strncpy(pszMonth, pszToken, MAX_MONTH_LEN);
2422         CharUpperA(pszMonth);
2423         pszMatch = strstr(szMonths, pszMonth);
2424         if( pszMatch != NULL )
2425             nMonth = (pszMatch - szMonths) / 3;
2426
2427         pszToken = strtok(NULL, " \t");
2428         TRACE("nDay -> %s\n", pszToken);
2429         if (pszToken != NULL)
2430             nDay = atoi(pszToken);
2431
2432         pszToken = strtok(NULL, " \t");
2433         pszMinutes = strchr(pszToken, ':');
2434         if( pszMinutes != NULL )
2435         {
2436             pszMinutes++;
2437             nMinutes = atoi(pszMinutes);
2438             pszHour = pszMinutes - 3;
2439             if (pszHour != NULL)
2440                 nHour = atoi(pszHour);
2441             time(&aTime);
2442             apTM = localtime( &aTime );
2443             nYear = apTM->tm_year;
2444         }
2445         else
2446         {
2447             nYear  = atoi(pszToken);
2448             nYear -= 1900;
2449             nHour  = 12;
2450         }
2451
2452         curFileProp->tmLastModified.tm_sec  = nSeconds;
2453         curFileProp->tmLastModified.tm_min  = nMinutes;
2454         curFileProp->tmLastModified.tm_hour = nHour;
2455         curFileProp->tmLastModified.tm_mday = nDay;
2456         curFileProp->tmLastModified.tm_mon  = nMonth;
2457         curFileProp->tmLastModified.tm_year = nYear;
2458
2459         pszToken = strtok(NULL, " \t");
2460         if(pszToken != NULL)
2461         {
2462             curFileProp->lpszName = FTP_strdup(pszToken);
2463             TRACE(": %s\n", curFileProp->lpszName);
2464         }
2465
2466         nBufLen = MAX_REPLY_LEN;
2467         indexFilePropArray++;
2468     }
2469
2470     if (bSuccess && indexFilePropArray)
2471     {
2472         if (indexFilePropArray < sizeFilePropArray - 1)
2473         {
2474             LPFILEPROPERTIESA tmpafp;
2475
2476             tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp, 
2477                 sizeof(FILEPROPERTIESA)*indexFilePropArray);
2478             if (NULL == tmpafp)
2479                 *lpafp = tmpafp;
2480         }
2481         *dwfp = indexFilePropArray;
2482     }
2483     else
2484     {
2485         HeapFree(GetProcessHeap(), 0, *lpafp);
2486         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
2487         bSuccess = FALSE;
2488     }
2489
2490 lend:   
2491     return bSuccess;
2492 }
2493
2494
2495 /***********************************************************************
2496  *           FTP_ParsePermission (internal)
2497  *
2498  * Parse permission string of directory information
2499  *
2500  * RETURNS
2501  *   TRUE on success
2502  *   FALSE on failure
2503  *
2504  */
2505 BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESA lpfp)
2506 {
2507     BOOL bSuccess = TRUE;
2508     unsigned short nPermission = 0;
2509     INT nPos = 1;
2510     INT nLast  = 9;
2511
2512     TRACE("\n");
2513     if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
2514     {
2515         bSuccess = FALSE;
2516         return bSuccess;
2517     }
2518
2519     lpfp->bIsDirectory = (*lpszPermission == 'd');
2520     do
2521     {
2522         switch (nPos)
2523         {
2524             case 1:
2525                 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
2526                 break;
2527             case 2:
2528                 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
2529                 break;
2530             case 3:
2531                 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
2532                 break;
2533             case 4:
2534                 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
2535                 break;
2536             case 5:
2537                 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
2538                 break;
2539             case 6:
2540                 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
2541                 break;
2542             case 7:
2543                 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
2544                 break;
2545             case 8:
2546                 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
2547                 break;
2548             case 9:
2549                 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
2550                 break;
2551         }
2552         nPos++;
2553     }while (nPos <= nLast);
2554
2555     lpfp->permissions = nPermission;
2556     return bSuccess;
2557 }
2558
2559
2560 /***********************************************************************
2561  *           FTP_SetResponseError (internal)
2562  *
2563  * Set the appropriate error code for a given response from the server
2564  *
2565  * RETURNS
2566  *
2567  */
2568 DWORD FTP_SetResponseError(DWORD dwResponse)
2569 {
2570     DWORD dwCode = 0;
2571
2572     switch(dwResponse)
2573     {
2574         case 421: /* Service not available - Server may be shutting down. */
2575             dwCode = ERROR_INTERNET_TIMEOUT;
2576             break;
2577
2578         case 425: /* Cannot open data connection. */
2579             dwCode = ERROR_INTERNET_CANNOT_CONNECT;
2580             break;
2581
2582         case 426: /* Connection closed, transer aborted. */
2583             dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
2584             break;
2585
2586         case 500: /* Syntax error. Command unrecognized. */
2587         case 501: /* Syntax error. Error in parameters or arguments. */
2588             dwCode = ERROR_INTERNET_INCORRECT_FORMAT;
2589             break;
2590
2591         case 530: /* Not logged in. Login incorrect. */
2592             dwCode = ERROR_INTERNET_LOGIN_FAILURE;
2593             break;
2594
2595         case 550: /* File action not taken. File not found or no access. */
2596             dwCode = ERROR_INTERNET_ITEM_NOT_FOUND;
2597             break;
2598
2599         case 450: /* File action not taken. File may be busy. */
2600         case 451: /* Action aborted. Server error. */
2601         case 452: /* Action not taken. Insufficient storage space on server. */
2602         case 502: /* Command not implemented. */
2603         case 503: /* Bad sequence of command. */
2604         case 504: /* Command not implemented for that parameter. */
2605         case 532: /* Need account for storing files */
2606         case 551: /* Requested action aborted. Page type unknown */
2607         case 552: /* Action aborted. Exceeded storage allocation */
2608         case 553: /* Action not taken. File name not allowed. */
2609
2610         default:
2611             dwCode = ERROR_INTERNET_INTERNAL_ERROR;
2612             break;
2613     }
2614     
2615     INTERNET_SetLastError(dwCode);
2616     return dwCode;
2617 }