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