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