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