Add '\n' at the end of traces.
[wine] / dlls / wininet / ftp.c
1 /*
2  * WININET - Ftp implementation
3  *
4  * Copyright 1999 Corel Corporation
5  *
6  * Ulrich Czekalla
7  * Noureddine Jemmali
8  *
9  * Copyright 2000 Andreas Mohr
10  */
11
12 #include "config.h"
13
14 #include <errno.h>
15 #ifdef HAVE_NETDB_H
16 # include <netdb.h>
17 #endif
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/types.h>
22 #ifdef HAVE_SYS_SOCKET_H
23 # include <sys/socket.h>
24 #endif
25 #include <sys/stat.h>
26 #include <unistd.h>
27 #include <time.h>
28 #ifdef HAVE_NETINET_IN_SYSTM_H
29 # include <netinet/in_systm.h>
30 #endif
31 #ifdef HAVE_NETINET_IN_H
32 # include <netinet/in.h>
33 #endif
34 #ifdef HAVE_NETINET_IP_H
35 # include <netinet/ip.h>
36 #endif
37
38 #include "winbase.h"
39 #include "wingdi.h"
40 #include "winuser.h"
41 #include "wininet.h"
42 #include "winerror.h"
43 #include "winsock.h"
44 #include "heap.h"
45
46 #include "debugtools.h"
47 #include "internet.h"
48
49 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.43)
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 != INVALID_SOCKET)
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.49)
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.31)
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.35)
457  *
458  * Search the specified directory
459  *
460  * RETURNS
461  *    HINTERNET on success
462  *    NULL on failure
463  *
464  */
465 INTERNETAPI 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 INTERNETAPI 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 != INVALID_SOCKET)
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.37)
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.41)
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 INTERNETAPI 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 != INVALID_SOCKET)
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.39)
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 != INVALID_SOCKET)
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.33)
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.45)
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.47)
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 = INVALID_SOCKET, 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     if (INVALID_SOCKET == (nsocket = socket(AF_INET,SOCK_STREAM,0)))
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 && INVALID_SOCKET != nsocket)
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 != SOCKET_ERROR))
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 != SOCKET_ERROR);
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 && INVALID_SOCKET != lpwfs->lstnSocket)
1685     {
1686         close(lpwfs->lstnSocket);
1687         lpwfs->lstnSocket = INVALID_SOCKET;
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 (INVALID_SOCKET == lpwfs->lstnSocket)
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 (SOCKET_ERROR == bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)))
1725     {
1726         TRACE("Unable to bind socket\n");
1727         goto lend;
1728     }
1729
1730     if (SOCKET_ERROR == listen(lpwfs->lstnSocket, MAX_BACKLOG))
1731     {
1732         TRACE("listen failed\n");
1733         goto lend;
1734     }
1735
1736     if (SOCKET_ERROR != getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen))
1737         bSuccess = TRUE;
1738
1739 lend:
1740     if (!bSuccess && INVALID_SOCKET == lpwfs->lstnSocket)
1741     {
1742         close(lpwfs->lstnSocket);
1743         lpwfs->lstnSocket = INVALID_SOCKET;
1744     }
1745
1746     return bSuccess;
1747 }
1748
1749
1750 /***********************************************************************
1751  *           FTP_SendType (internal)
1752  *
1753  * Tell server type of data being transfered
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 = INVALID_SOCKET;
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             if (INVALID_SOCKET == (nsocket = socket(AF_INET,SOCK_STREAM,0)))
1904                 goto lend;
1905
1906             if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
1907             {
1908                 ERR("can't connect passive FTP data port.\n");
1909                 goto lend;
1910             }
1911             lpwfs->pasvSocket = nsocket;
1912             bSuccess = TRUE;
1913         }
1914         else
1915             FTP_SetResponseError(nResCode);
1916     }
1917
1918 lend:
1919     return bSuccess;
1920 }
1921
1922
1923 BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONA lpwfs)
1924 {
1925     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
1926     {
1927         if (!FTP_DoPassive(lpwfs))
1928             return FALSE;
1929     }
1930     else
1931     {
1932         if (!FTP_SendPort(lpwfs))
1933             return FALSE;
1934     }
1935     return TRUE;
1936 }
1937
1938
1939 /***********************************************************************
1940  *           FTP_GetDataSocket (internal)
1941  *
1942  * Either accepts an incoming data socket connection from the server
1943  * or just returns the already opened socket after a PASV command
1944  * in case of passive FTP.
1945  * 
1946  *
1947  * RETURNS
1948  *   TRUE on success
1949  *   FALSE on failure
1950  *
1951  */
1952 BOOL FTP_GetDataSocket(LPWININETFTPSESSIONA lpwfs, LPINT nDataSocket)
1953 {
1954     struct sockaddr_in saddr;
1955     size_t addrlen = sizeof(struct sockaddr);
1956
1957     TRACE("\n");
1958     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
1959     {
1960         *nDataSocket = lpwfs->pasvSocket;
1961     }
1962     else
1963     {
1964         *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
1965         close(lpwfs->lstnSocket);
1966         lpwfs->lstnSocket = INVALID_SOCKET;
1967     }
1968     return *nDataSocket != INVALID_SOCKET;
1969 }
1970
1971
1972 /***********************************************************************
1973  *           FTP_SendData (internal)
1974  *
1975  * Send data to the server
1976  *
1977  * RETURNS
1978  *   TRUE on success
1979  *   FALSE on failure
1980  *
1981  */
1982 BOOL FTP_SendData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, HANDLE hFile)
1983 {
1984     BY_HANDLE_FILE_INFORMATION fi;
1985     DWORD nBytesRead = 0;
1986     DWORD nBytesSent = 0;
1987     DWORD nTotalSent = 0;
1988     DWORD nBytesToSend, nLen, nRC = 1;
1989     time_t s_long_time, e_long_time;
1990     LONG nSeconds;
1991     CHAR *lpszBuffer;
1992
1993     TRACE("\n");
1994     lpszBuffer = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR)*DATA_PACKET_SIZE);
1995     memset(lpszBuffer, 0, sizeof(CHAR)*DATA_PACKET_SIZE);
1996
1997     /* Get the size of the file. */
1998     GetFileInformationByHandle(hFile, &fi);
1999     time(&s_long_time);
2000
2001     do
2002     {
2003         nBytesToSend = nBytesRead - nBytesSent;
2004
2005         if (nBytesToSend <= 0)
2006         {
2007             /* Read data from file. */
2008             nBytesSent = 0;
2009             if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
2010             ERR("Failed reading from file\n");
2011
2012             if (nBytesRead > 0)
2013                 nBytesToSend = nBytesRead;
2014             else
2015                 break;
2016         }
2017
2018         nLen = DATA_PACKET_SIZE < nBytesToSend ? 
2019             DATA_PACKET_SIZE : nBytesToSend;
2020         nRC  = send(nDataSocket, lpszBuffer, nLen, 0);
2021
2022         if (nRC != SOCKET_ERROR)
2023         {
2024             nBytesSent += nRC;
2025             nTotalSent += nRC;
2026         }
2027
2028         /* Do some computation to display the status. */
2029         time(&e_long_time);
2030         nSeconds = e_long_time - s_long_time;
2031         if( nSeconds / 60 > 0 )
2032         {
2033             TRACE( "%ld bytes of %d bytes (%ld%%) in %ld min %ld sec estimated remainig time %ld sec\n",
2034             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60, 
2035             nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
2036         }
2037         else
2038         {
2039             TRACE( "%ld bytes of %d bytes (%ld%%) in %ld sec estimated remainig time %ld sec\n",
2040             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
2041             (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
2042         }
2043     } while (nRC != SOCKET_ERROR);
2044
2045     TRACE("file transfer complete!\n");
2046
2047     if(lpszBuffer != NULL)
2048         HeapFree(GetProcessHeap(), 0, lpszBuffer);
2049
2050     return nTotalSent;
2051 }
2052
2053
2054 /***********************************************************************
2055  *           FTP_SendRetrieve (internal)
2056  *
2057  * Send request to retrieve a file
2058  *
2059  * RETURNS
2060  *   Number of bytes to be received on success
2061  *   0 on failure
2062  *
2063  */
2064 DWORD FTP_SendRetrieve(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType)
2065 {
2066     INT nResCode;
2067     DWORD nResult = 0;
2068
2069     TRACE("\n");
2070     if (!FTP_InitListenSocket(lpwfs))
2071         goto lend;
2072
2073     if (!FTP_SendType(lpwfs, dwType))
2074         goto lend;
2075
2076     if (!FTP_SendPortOrPasv(lpwfs))
2077         goto lend;
2078
2079     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0))
2080         goto lend;
2081
2082     nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
2083         MAX_REPLY_LEN, 0, 0, 0);
2084     if (nResCode)
2085     {
2086         if (nResCode == 125 || nResCode == 150)
2087         {
2088             /* Parse size of data to be retrieved */
2089             INT i, sizepos = -1;
2090             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2091             for (i = strlen(lpszResponseBuffer) - 1; i >= 0; i--)
2092             {
2093                 if ('(' == lpszResponseBuffer[i])
2094                 {
2095                     sizepos = i;
2096                     break;
2097                 }
2098             }
2099
2100             if (sizepos >= 0)
2101             {
2102                 nResult = atol(&lpszResponseBuffer[sizepos+1]);
2103                 TRACE("Waiting to receive %ld bytes\n", nResult);
2104             }
2105         }
2106     }
2107
2108 lend:
2109     if (0 == nResult && INVALID_SOCKET != lpwfs->lstnSocket)
2110     {
2111         close(lpwfs->lstnSocket);
2112         lpwfs->lstnSocket = INVALID_SOCKET;
2113     }
2114
2115     return nResult;
2116 }
2117
2118
2119 /***********************************************************************
2120  *           FTP_RetrieveData  (internal)
2121  *
2122  * Retrieve data from server
2123  *
2124  * RETURNS
2125  *   TRUE on success
2126  *   FALSE on failure
2127  *
2128  */
2129 BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile)
2130 {
2131     DWORD nBytesWritten;
2132     DWORD nBytesReceived = 0;
2133     INT nRC = 0;
2134     CHAR *lpszBuffer;
2135
2136     TRACE("\n");
2137
2138     if (INVALID_HANDLE_VALUE == hFile)
2139         return FALSE;
2140
2141     lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2142     if (NULL == lpszBuffer)
2143     {
2144         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2145         return FALSE;
2146     }
2147
2148     while (nBytesReceived < nBytes && nRC != SOCKET_ERROR)
2149     {
2150         nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
2151         if (nRC != SOCKET_ERROR)
2152         {
2153             /* other side closed socket. */
2154             if (nRC == 0)
2155                 goto recv_end;
2156             WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL); 
2157             nBytesReceived += nRC;
2158         }
2159
2160         TRACE("%ld bytes of %ld (%ld%%)\r", nBytesReceived, nBytes, 
2161            nBytesReceived * 100 / nBytes);
2162     }
2163
2164     TRACE("Data transfer complete\n");
2165     if (NULL != lpszBuffer)
2166         HeapFree(GetProcessHeap(), 0, lpszBuffer);
2167
2168 recv_end:
2169     return  (nRC != SOCKET_ERROR);
2170 }
2171
2172
2173 /***********************************************************************
2174  *           FTP_CloseSessionHandle (internal)
2175  *
2176  * Deallocate session handle
2177  *
2178  * RETURNS
2179  *   TRUE on success
2180  *   FALSE on failure
2181  *
2182  */
2183 BOOL FTP_CloseSessionHandle(LPWININETFTPSESSIONA lpwfs)
2184 {
2185     if (INVALID_SOCKET != lpwfs->sndSocket)
2186         close(lpwfs->sndSocket);
2187
2188     if (INVALID_SOCKET != lpwfs->lstnSocket)
2189         close(lpwfs->lstnSocket);
2190
2191     if (lpwfs->lpszPassword)
2192         HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2193
2194     if (lpwfs->lpszUserName)
2195         HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2196
2197     HeapFree(GetProcessHeap(), 0, lpwfs);
2198
2199     return TRUE;
2200 }
2201
2202
2203 /***********************************************************************
2204  *           FTP_CloseSessionHandle (internal)
2205  *
2206  * Deallocate session handle
2207  *
2208  * RETURNS
2209  *   TRUE on success
2210  *   FALSE on failure
2211  *
2212  */
2213 BOOL FTP_CloseFindNextHandle(LPWININETFINDNEXTA lpwfn)
2214 {
2215     INT i; 
2216
2217     TRACE("\n");
2218
2219     for (i = 0; i < lpwfn->size; i++)
2220     {
2221         if (NULL != lpwfn->lpafp[i].lpszName)
2222             HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
2223     }
2224
2225     HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
2226     HeapFree(GetProcessHeap(), 0, lpwfn);
2227
2228     return TRUE;
2229 }
2230
2231
2232 /***********************************************************************
2233  *           FTP_ReceiveFileList (internal)
2234  *
2235  * Read file list from server
2236  *
2237  * RETURNS
2238  *   Handle to file list on success
2239  *   NULL on failure
2240  *
2241  */
2242 HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONA lpwfs, INT nSocket, 
2243         LPWIN32_FIND_DATAA lpFindFileData, DWORD dwContext)
2244 {
2245     DWORD dwSize = 0;
2246     LPFILEPROPERTIESA lpafp = NULL;
2247     LPWININETFINDNEXTA lpwfn = NULL;
2248
2249     TRACE("\n");
2250
2251     if (FTP_ParseDirectory(lpwfs, nSocket, &lpafp, &dwSize))
2252     {
2253         FTP_ConvertFileProp(lpafp, lpFindFileData);
2254
2255         lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFINDNEXTA));
2256         if (NULL != lpwfn)
2257         {
2258             lpwfn->hdr.htype = WH_HFINDNEXT;
2259             lpwfn->hdr.lpwhparent = (LPWININETHANDLEHEADER)lpwfs;
2260             lpwfn->hdr.dwContext = dwContext; 
2261             lpwfn->index = 1; /* Next index is 1 since we return index 0 */
2262             lpwfn->size = dwSize;
2263             lpwfn->lpafp = lpafp;
2264         }
2265     } 
2266
2267     TRACE("Matched %ld files\n", dwSize);
2268     return (HINTERNET)lpwfn;
2269 }
2270
2271
2272 /***********************************************************************
2273  *           FTP_ConvertFileProp (internal)
2274  *
2275  * Converts FILEPROPERTIESA struct to WIN32_FIND_DATAA
2276  *
2277  * RETURNS
2278  *   TRUE on success
2279  *   FALSE on failure
2280  *
2281  */
2282 BOOL FTP_ConvertFileProp(LPFILEPROPERTIESA lpafp, LPWIN32_FIND_DATAA lpFindFileData)
2283 {
2284     BOOL bSuccess = FALSE;
2285
2286     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
2287
2288     if (lpafp)
2289     {
2290         DWORD access = mktime(&lpafp->tmLastModified);
2291                 
2292         /* Not all fields are filled in */
2293         lpFindFileData->ftLastAccessTime.dwHighDateTime = HIWORD(access);
2294         lpFindFileData->ftLastAccessTime.dwLowDateTime  = LOWORD(access);
2295         lpFindFileData->nFileSizeHigh = HIWORD(lpafp->nSize);
2296         lpFindFileData->nFileSizeLow = LOWORD(lpafp->nSize);
2297
2298         if (lpafp->bIsDirectory)
2299             lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
2300
2301         if (lpafp->lpszName)
2302             strncpy(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
2303
2304         bSuccess = TRUE;
2305     }
2306
2307     return bSuccess;
2308 }
2309
2310
2311 /***********************************************************************
2312  *           FTP_ParseDirectory (internal)
2313  *
2314  * Parse string of directory information
2315  *
2316  * RETURNS
2317  *   TRUE on success
2318  *   FALSE on failure
2319  *
2320  * FIXME: - This function needs serious clea-up
2321  *        - We should consider both UNIX and NT list formats
2322  */
2323 #define MAX_MONTH_LEN 10
2324 #define MIN_LEN_DIR_ENTRY 15
2325
2326 BOOL FTP_ParseDirectory(LPWININETFTPSESSIONA lpwfs, INT nSocket, LPFILEPROPERTIESA *lpafp, LPDWORD dwfp)
2327 {
2328   /*
2329    * <Permissions> <NoLinks> <owner>   <group> <size> <date>  <time or year> <filename>
2330    *
2331    * For instance:
2332    * drwx--s---     2         pcarrier  ens     512    Sep 28  1995           pcarrier
2333    */
2334     CHAR* pszMinutes;
2335     CHAR* pszHour;
2336     time_t aTime;
2337     struct tm* apTM;
2338     CHAR pszMonth[MAX_MONTH_LEN];
2339     CHAR* pszMatch;
2340     BOOL bSuccess = TRUE;
2341     DWORD nBufLen = MAX_REPLY_LEN;
2342     LPFILEPROPERTIESA curFileProp = NULL;
2343     CHAR* pszLine  = NULL;
2344     CHAR* pszToken = NULL;
2345     INT nTokenToSkip = 3;
2346     INT nCount = 0;
2347     INT nSeconds = 0;
2348     INT nMinutes = 0;
2349     INT nHour    = 0;
2350     INT nDay     = 0;
2351     INT nMonth   = 0;
2352     INT nYear    = 0;
2353     INT sizeFilePropArray = 20;
2354     INT indexFilePropArray = 0;
2355
2356     TRACE("\n");
2357
2358     /* Allocate intial file properties array */
2359     *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESA)*(sizeFilePropArray));
2360     if (NULL == lpafp)
2361     {
2362         bSuccess = FALSE;
2363         goto lend;
2364     }
2365
2366     while ((pszLine = INTERNET_GetNextLine(nSocket, INTERNET_GetResponseBuffer(), &nBufLen)) != NULL)
2367     {
2368         if (sizeFilePropArray <= indexFilePropArray)
2369         {
2370             LPFILEPROPERTIESA tmpafp;
2371
2372             sizeFilePropArray *= 2;
2373             tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp, 
2374                 sizeof(FILEPROPERTIESA)*sizeFilePropArray);
2375             if (NULL == tmpafp)
2376             {
2377                 bSuccess = FALSE;
2378                 goto lend;
2379             }
2380
2381             *lpafp = tmpafp;
2382         }
2383
2384         curFileProp = &((*lpafp)[indexFilePropArray]);
2385
2386         /* First Parse the permissions. */
2387         pszToken = strtok(pszLine, " \t" );
2388
2389         /* HACK! If this is not a file listing skip the line */
2390         if (!pszToken || 10 != strlen(pszToken) || nBufLen <= MIN_LEN_DIR_ENTRY)
2391         {
2392             nBufLen = MAX_REPLY_LEN;
2393             continue;
2394         }
2395
2396         FTP_ParsePermission(pszToken, curFileProp);
2397
2398         nTokenToSkip = 3;
2399         nCount = 0;
2400         do
2401         {
2402             pszToken = strtok( NULL, " \t" );
2403             nCount++;
2404         } while( nCount <= nTokenToSkip );
2405
2406         /* Store the size of the file in the param list. */
2407         TRACE("nSize-> %s\n", pszToken);
2408         if (pszToken != NULL)
2409             curFileProp->nSize = atol(pszToken);
2410
2411         /* Parse last modified time. */
2412         nSeconds = 0;
2413         nMinutes = 0;
2414         nHour    = 0;
2415         nDay     = 0;
2416         nMonth   = 0;
2417         nYear    = 0;
2418
2419         pszToken = strtok( NULL, " \t" );
2420         strncpy(pszMonth, pszToken, MAX_MONTH_LEN);
2421         CharUpperA(pszMonth);
2422         pszMatch = strstr(szMonths, pszMonth);
2423         if( pszMatch != NULL )
2424             nMonth = (pszMatch - szMonths) / 3;
2425
2426         pszToken = strtok(NULL, " \t");
2427         TRACE("nDay -> %s\n", pszToken);
2428         if (pszToken != NULL)
2429             nDay = atoi(pszToken);
2430
2431         pszToken = strtok(NULL, " \t");
2432         pszMinutes = strchr(pszToken, ':');
2433         if( pszMinutes != NULL )
2434         {
2435             pszMinutes++;
2436             nMinutes = atoi(pszMinutes);
2437             pszHour = pszMinutes - 3;
2438             if (pszHour != NULL)
2439                 nHour = atoi(pszHour);
2440             time(&aTime);
2441             apTM = localtime( &aTime );
2442             nYear = apTM->tm_year;
2443         }
2444         else
2445         {
2446             nYear  = atoi(pszToken);
2447             nYear -= 1900;
2448             nHour  = 12;
2449         }
2450
2451         curFileProp->tmLastModified.tm_sec  = nSeconds;
2452         curFileProp->tmLastModified.tm_min  = nMinutes;
2453         curFileProp->tmLastModified.tm_hour = nHour;
2454         curFileProp->tmLastModified.tm_mday = nDay;
2455         curFileProp->tmLastModified.tm_mon  = nMonth;
2456         curFileProp->tmLastModified.tm_year = nYear;
2457
2458         pszToken = strtok(NULL, " \t");
2459         if(pszToken != NULL)
2460         {
2461             curFileProp->lpszName = FTP_strdup(pszToken);
2462             TRACE(": %s\n", curFileProp->lpszName);
2463         }
2464
2465         nBufLen = MAX_REPLY_LEN;
2466         indexFilePropArray++;
2467     }
2468
2469     if (bSuccess && indexFilePropArray)
2470     {
2471         if (indexFilePropArray < sizeFilePropArray - 1)
2472         {
2473             LPFILEPROPERTIESA tmpafp;
2474
2475             tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp, 
2476                 sizeof(FILEPROPERTIESA)*indexFilePropArray);
2477             if (NULL == tmpafp)
2478                 *lpafp = tmpafp;
2479         }
2480         *dwfp = indexFilePropArray;
2481     }
2482     else
2483     {
2484         HeapFree(GetProcessHeap(), 0, *lpafp);
2485         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
2486         bSuccess = FALSE;
2487     }
2488
2489 lend:   
2490     return bSuccess;
2491 }
2492
2493
2494 /***********************************************************************
2495  *           FTP_ParsePermission (internal)
2496  *
2497  * Parse permission string of directory information
2498  *
2499  * RETURNS
2500  *   TRUE on success
2501  *   FALSE on failure
2502  *
2503  */
2504 BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESA lpfp)
2505 {
2506     BOOL bSuccess = TRUE;
2507     unsigned short nPermission = 0;
2508     INT nPos = 1;
2509     INT nLast  = 9;
2510
2511     TRACE("\n");
2512     if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
2513     {
2514         bSuccess = FALSE;
2515         return bSuccess;
2516     }
2517
2518     lpfp->bIsDirectory = (*lpszPermission == 'd');
2519     do
2520     {
2521         switch (nPos)
2522         {
2523             case 1:
2524                 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
2525                 break;
2526             case 2:
2527                 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
2528                 break;
2529             case 3:
2530                 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
2531                 break;
2532             case 4:
2533                 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
2534                 break;
2535             case 5:
2536                 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
2537                 break;
2538             case 6:
2539                 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
2540                 break;
2541             case 7:
2542                 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
2543                 break;
2544             case 8:
2545                 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
2546                 break;
2547             case 9:
2548                 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
2549                 break;
2550         }
2551         nPos++;
2552     }while (nPos <= nLast);
2553
2554     lpfp->permissions = nPermission;
2555     return bSuccess;
2556 }
2557
2558
2559 /***********************************************************************
2560  *           FTP_SetResponseError (internal)
2561  *
2562  * Set the appropriate error code for a given response from the server
2563  *
2564  * RETURNS
2565  *
2566  */
2567 DWORD FTP_SetResponseError(DWORD dwResponse)
2568 {
2569     DWORD dwCode = 0;
2570
2571     switch(dwResponse)
2572     {
2573         case 421: /* Service not available - Server may be shutting down. */
2574             dwCode = ERROR_INTERNET_TIMEOUT;
2575             break;
2576
2577         case 425: /* Cannot open data connection. */
2578             dwCode = ERROR_INTERNET_CANNOT_CONNECT;
2579             break;
2580
2581         case 426: /* Connection closed, transer aborted. */
2582             dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
2583             break;
2584
2585         case 500: /* Syntax error. Command unrecognized. */
2586         case 501: /* Syntax error. Error in parameters or arguments. */
2587             dwCode = ERROR_INTERNET_INCORRECT_FORMAT;
2588             break;
2589
2590         case 530: /* Not logged in. Login incorrect. */
2591             dwCode = ERROR_INTERNET_LOGIN_FAILURE;
2592             break;
2593
2594         case 550: /* File action not taken. File not found or no access. */
2595             dwCode = ERROR_INTERNET_ITEM_NOT_FOUND;
2596             break;
2597
2598         case 450: /* File action not taken. File may be busy. */
2599         case 451: /* Action aborted. Server error. */
2600         case 452: /* Action not taken. Insufficient storage space on server. */
2601         case 502: /* Command not implemented. */
2602         case 503: /* Bad sequence of command. */
2603         case 504: /* Command not implemented for that parameter. */
2604         case 532: /* Need account for storing files */
2605         case 551: /* Requested action aborted. Page type unknown */
2606         case 552: /* Action aborted. Exceeded storage allocation */
2607         case 553: /* Action not taken. File name not allowed. */
2608
2609         default:
2610             dwCode = ERROR_INTERNET_INTERNAL_ERROR;
2611             break;
2612     }
2613     
2614     INTERNET_SetLastError(dwCode);
2615     return dwCode;
2616 }