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