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