wintrust: Use a helper function to get a signer's cert info from a message.
[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.close_connection = NULL;
1125         lpwh->hdr.destroy = FTP_CloseFileTransferHandle;
1126         lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1127         lpwh->nDataSocket = nDataSocket;
1128         lpwh->session_deleted = FALSE;
1129
1130         WININET_AddRef( &lpwfs->hdr );
1131         lpwh->lpFtpSession = lpwfs;
1132         
1133         handle = WININET_AllocHandle( &lpwh->hdr );
1134         if( !handle )
1135             goto lend;
1136
1137         /* Indicate that a download is currently in progress */
1138         lpwfs->download_in_progress = lpwh;
1139     }
1140
1141     if (lpwfs->lstnSocket != -1)
1142         closesocket(lpwfs->lstnSocket);
1143
1144     hIC = lpwfs->lpAppInfo;
1145     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1146     {
1147         INTERNET_ASYNC_RESULT iar;
1148
1149         if (lpwh)
1150         {
1151             iar.dwResult = (DWORD)handle;
1152             iar.dwError = ERROR_SUCCESS;
1153             SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1154                 &iar, sizeof(INTERNET_ASYNC_RESULT));
1155         }
1156
1157         iar.dwResult = (DWORD)bSuccess;
1158         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1159         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1160             &iar, sizeof(INTERNET_ASYNC_RESULT));
1161     }
1162
1163 lend:
1164     if( lpwh )
1165         WININET_Release( &lpwh->hdr );
1166
1167     return handle;
1168 }
1169
1170
1171 /***********************************************************************
1172  *           FtpGetFileA (WININET.@)
1173  *
1174  * Retrieve file from the FTP server
1175  *
1176  * RETURNS
1177  *    TRUE on success
1178  *    FALSE on failure
1179  *
1180  */
1181 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1182     BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1183     DWORD dwContext)
1184 {
1185     LPWSTR lpwzRemoteFile;
1186     LPWSTR lpwzNewFile;
1187     BOOL ret;
1188     
1189     lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
1190     lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
1191     ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1192         dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1193     HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1194     HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1195     return ret;
1196 }
1197
1198
1199 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1200 {
1201     struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1202     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1203
1204     TRACE("%p\n", lpwfs);
1205
1206     FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1207              req->lpszNewFile, req->fFailIfExists,
1208              req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1209     HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1210     HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1211 }
1212
1213
1214 /***********************************************************************
1215  *           FtpGetFileW (WININET.@)
1216  *
1217  * Retrieve file from the FTP server
1218  *
1219  * RETURNS
1220  *    TRUE on success
1221  *    FALSE on failure
1222  *
1223  */
1224 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1225     BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1226     DWORD dwContext)
1227 {
1228     LPWININETFTPSESSIONW lpwfs;
1229     LPWININETAPPINFOW hIC = NULL;
1230     BOOL r = FALSE;
1231
1232     if (!lpszRemoteFile || !lpszNewFile)
1233     {
1234         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1235         return FALSE;
1236     }
1237
1238     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
1239     if (!lpwfs)
1240     {
1241         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1242         return FALSE;
1243     }
1244
1245     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1246     {
1247         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1248         goto lend;
1249     }
1250
1251     if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1252     {
1253         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1254         goto lend;
1255     }
1256
1257     if (lpwfs->download_in_progress != NULL) {
1258         INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1259         goto lend;
1260     }
1261     
1262     hIC = lpwfs->lpAppInfo;
1263     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1264     {
1265         WORKREQUEST workRequest;
1266         struct WORKREQ_FTPGETFILEW *req;
1267
1268         workRequest.asyncproc = AsyncFtpGetFileProc;
1269         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1270         req = &workRequest.u.FtpGetFileW;
1271         req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
1272         req->lpszNewFile = WININET_strdupW(lpszNewFile);
1273         req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1274         req->fFailIfExists = fFailIfExists;
1275         req->dwFlags = dwInternetFlags;
1276         req->dwContext = dwContext;
1277
1278         r = INTERNET_AsyncCall(&workRequest);
1279     }
1280     else
1281     {
1282         r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1283            fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1284     }
1285
1286 lend:
1287     WININET_Release( &lpwfs->hdr );
1288
1289     return r;
1290 }
1291
1292
1293 /***********************************************************************
1294  *           FTP_FtpGetFileW (Internal)
1295  *
1296  * Retrieve file from the FTP server
1297  *
1298  * RETURNS
1299  *    TRUE on success
1300  *    FALSE on failure
1301  *
1302  */
1303 BOOL WINAPI FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1304         BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1305         DWORD dwContext)
1306 {
1307     DWORD nBytes;
1308     BOOL bSuccess = FALSE;
1309     HANDLE hFile;
1310     LPWININETAPPINFOW hIC = NULL;
1311
1312     TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1313
1314     /* Clear any error information */
1315     INTERNET_SetLastError(0);
1316
1317     /* Ensure we can write to lpszNewfile by opening it */
1318     hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1319         CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1320     if (INVALID_HANDLE_VALUE == hFile)
1321         return FALSE;
1322
1323     /* Set up socket to retrieve data */
1324     nBytes = FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags);
1325
1326     if (nBytes > 0)
1327     {
1328         INT nDataSocket;
1329
1330         /* Get data socket to server */
1331         if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1332         {
1333             INT nResCode;
1334
1335             /* Receive data */
1336             FTP_RetrieveFileData(lpwfs, nDataSocket, nBytes, hFile);
1337             nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1338             if (nResCode)
1339             {
1340                 if (nResCode == 226)
1341                     bSuccess = TRUE;
1342                 else
1343                     FTP_SetResponseError(nResCode);
1344             }
1345             closesocket(nDataSocket);
1346         }
1347     }
1348
1349     if (lpwfs->lstnSocket != -1)
1350         closesocket(lpwfs->lstnSocket);
1351
1352     CloseHandle(hFile);
1353
1354     hIC = lpwfs->lpAppInfo;
1355     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1356     {
1357         INTERNET_ASYNC_RESULT iar;
1358
1359         iar.dwResult = (DWORD)bSuccess;
1360         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1361         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1362             &iar, sizeof(INTERNET_ASYNC_RESULT));
1363     }
1364
1365     return bSuccess;
1366 }
1367
1368 /***********************************************************************
1369  *           FtpGetFileSize  (WININET.@)
1370  */
1371 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1372 {
1373     FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1374
1375     if (lpdwFileSizeHigh)
1376         *lpdwFileSizeHigh = 0;
1377
1378     return 0;
1379 }
1380
1381 /***********************************************************************
1382  *           FtpDeleteFileA  (WININET.@)
1383  *
1384  * Delete a file on the ftp server
1385  *
1386  * RETURNS
1387  *    TRUE on success
1388  *    FALSE on failure
1389  *
1390  */
1391 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1392 {
1393     LPWSTR lpwzFileName;
1394     BOOL ret;
1395     
1396     lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1397     ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1398     HeapFree(GetProcessHeap(), 0, lpwzFileName);
1399     return ret;
1400 }
1401
1402 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1403 {
1404     struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1405     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1406
1407     TRACE("%p\n", lpwfs);
1408
1409     FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1410     HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1411 }
1412
1413 /***********************************************************************
1414  *           FtpDeleteFileW  (WININET.@)
1415  *
1416  * Delete a file on the ftp server
1417  *
1418  * RETURNS
1419  *    TRUE on success
1420  *    FALSE on failure
1421  *
1422  */
1423 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1424 {
1425     LPWININETFTPSESSIONW lpwfs;
1426     LPWININETAPPINFOW hIC = NULL;
1427     BOOL r = FALSE;
1428
1429     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1430     if (!lpwfs)
1431     {
1432         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1433         return FALSE;
1434     }
1435
1436     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1437     {
1438         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1439         goto lend;
1440     }
1441
1442     if (!lpszFileName)
1443     {
1444         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1445         goto lend;
1446     }
1447
1448     hIC = lpwfs->lpAppInfo;
1449     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1450     {
1451         WORKREQUEST workRequest;
1452         struct WORKREQ_FTPDELETEFILEW *req;
1453
1454         workRequest.asyncproc = AsyncFtpDeleteFileProc;
1455         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1456         req = &workRequest.u.FtpDeleteFileW;
1457         req->lpszFilename = WININET_strdupW(lpszFileName);
1458
1459         r = INTERNET_AsyncCall(&workRequest);
1460     }
1461     else
1462     {
1463         r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1464     }
1465
1466 lend:
1467     WININET_Release( &lpwfs->hdr );
1468
1469     return r;
1470 }
1471
1472 /***********************************************************************
1473  *           FTP_FtpDeleteFileW  (Internal)
1474  *
1475  * Delete a file on the ftp server
1476  *
1477  * RETURNS
1478  *    TRUE on success
1479  *    FALSE on failure
1480  *
1481  */
1482 BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName)
1483 {
1484     INT nResCode;
1485     BOOL bSuccess = FALSE;
1486     LPWININETAPPINFOW hIC = NULL;
1487
1488     TRACE("%p\n", lpwfs);
1489
1490     /* Clear any error information */
1491     INTERNET_SetLastError(0);
1492
1493     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1494         goto lend;
1495
1496     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1497     if (nResCode)
1498     {
1499         if (nResCode == 250)
1500             bSuccess = TRUE;
1501         else
1502             FTP_SetResponseError(nResCode);
1503     }
1504 lend:
1505     hIC = lpwfs->lpAppInfo;
1506     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1507     {
1508         INTERNET_ASYNC_RESULT iar;
1509
1510         iar.dwResult = (DWORD)bSuccess;
1511         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1512         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1513             &iar, sizeof(INTERNET_ASYNC_RESULT));
1514     }
1515
1516     return bSuccess;
1517 }
1518
1519
1520 /***********************************************************************
1521  *           FtpRemoveDirectoryA  (WININET.@)
1522  *
1523  * Remove a directory on the ftp server
1524  *
1525  * RETURNS
1526  *    TRUE on success
1527  *    FALSE on failure
1528  *
1529  */
1530 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1531 {
1532     LPWSTR lpwzDirectory;
1533     BOOL ret;
1534     
1535     lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
1536     ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1537     HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1538     return ret;
1539 }
1540
1541 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1542 {
1543     struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1544     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1545
1546     TRACE("%p\n", lpwfs);
1547
1548     FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1549     HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1550 }
1551
1552 /***********************************************************************
1553  *           FtpRemoveDirectoryW  (WININET.@)
1554  *
1555  * Remove a directory on the ftp server
1556  *
1557  * RETURNS
1558  *    TRUE on success
1559  *    FALSE on failure
1560  *
1561  */
1562 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1563 {
1564     LPWININETFTPSESSIONW lpwfs;
1565     LPWININETAPPINFOW hIC = NULL;
1566     BOOL r = FALSE;
1567
1568     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1569     if (!lpwfs)
1570     {
1571         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1572         return FALSE;
1573     }
1574
1575     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1576     {
1577         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1578         goto lend;
1579     }
1580
1581     if (!lpszDirectory)
1582     {
1583         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1584         goto lend;
1585     }
1586
1587     hIC = lpwfs->lpAppInfo;
1588     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1589     {
1590         WORKREQUEST workRequest;
1591         struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1592
1593         workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1594         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1595         req = &workRequest.u.FtpRemoveDirectoryW;
1596         req->lpszDirectory = WININET_strdupW(lpszDirectory);
1597
1598         r = INTERNET_AsyncCall(&workRequest);
1599     }
1600     else
1601     {
1602         r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1603     }
1604
1605 lend:
1606     WININET_Release( &lpwfs->hdr );
1607
1608     return r;
1609 }
1610
1611 /***********************************************************************
1612  *           FTP_FtpRemoveDirectoryW  (Internal)
1613  *
1614  * Remove a directory on the ftp server
1615  *
1616  * RETURNS
1617  *    TRUE on success
1618  *    FALSE on failure
1619  *
1620  */
1621 BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
1622 {
1623     INT nResCode;
1624     BOOL bSuccess = FALSE;
1625     LPWININETAPPINFOW hIC = NULL;
1626
1627     TRACE("\n");
1628
1629     /* Clear any error information */
1630     INTERNET_SetLastError(0);
1631
1632     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1633         goto lend;
1634
1635     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1636     if (nResCode)
1637     {
1638         if (nResCode == 250)
1639             bSuccess = TRUE;
1640         else
1641             FTP_SetResponseError(nResCode);
1642     }
1643
1644 lend:
1645     hIC = lpwfs->lpAppInfo;
1646     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1647     {
1648         INTERNET_ASYNC_RESULT iar;
1649
1650         iar.dwResult = (DWORD)bSuccess;
1651         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1652         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1653             &iar, sizeof(INTERNET_ASYNC_RESULT));
1654     }
1655
1656     return bSuccess;
1657 }
1658
1659
1660 /***********************************************************************
1661  *           FtpRenameFileA  (WININET.@)
1662  *
1663  * Rename a file on the ftp server
1664  *
1665  * RETURNS
1666  *    TRUE on success
1667  *    FALSE on failure
1668  *
1669  */
1670 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1671 {
1672     LPWSTR lpwzSrc;
1673     LPWSTR lpwzDest;
1674     BOOL ret;
1675     
1676     lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL;
1677     lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL;
1678     ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1679     HeapFree(GetProcessHeap(), 0, lpwzSrc);
1680     HeapFree(GetProcessHeap(), 0, lpwzDest);
1681     return ret;
1682 }
1683
1684 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
1685 {
1686     struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
1687     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1688
1689     TRACE("%p\n", lpwfs);
1690
1691     FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
1692     HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
1693     HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
1694 }
1695
1696 /***********************************************************************
1697  *           FtpRenameFileW  (WININET.@)
1698  *
1699  * Rename a file on the ftp server
1700  *
1701  * RETURNS
1702  *    TRUE on success
1703  *    FALSE on failure
1704  *
1705  */
1706 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
1707 {
1708     LPWININETFTPSESSIONW lpwfs;
1709     LPWININETAPPINFOW hIC = NULL;
1710     BOOL r = FALSE;
1711
1712     lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1713     if (!lpwfs)
1714     {
1715         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1716         return FALSE;
1717     }
1718
1719     if (WH_HFTPSESSION != lpwfs->hdr.htype)
1720     {
1721         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1722         goto lend;
1723     }
1724
1725     if (!lpszSrc || !lpszDest)
1726     {
1727         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1728         goto lend;
1729     }
1730
1731     hIC = lpwfs->lpAppInfo;
1732     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1733     {
1734         WORKREQUEST workRequest;
1735         struct WORKREQ_FTPRENAMEFILEW *req;
1736
1737         workRequest.asyncproc = AsyncFtpRenameFileProc;
1738         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1739         req = &workRequest.u.FtpRenameFileW;
1740         req->lpszSrcFile = WININET_strdupW(lpszSrc);
1741         req->lpszDestFile = WININET_strdupW(lpszDest);
1742
1743         r = INTERNET_AsyncCall(&workRequest);
1744     }
1745     else
1746     {
1747         r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
1748     }
1749
1750 lend:
1751     WININET_Release( &lpwfs->hdr );
1752
1753     return r;
1754 }
1755
1756 /***********************************************************************
1757  *           FTP_FtpRenameFileW  (Internal)
1758  *
1759  * Rename a file on the ftp server
1760  *
1761  * RETURNS
1762  *    TRUE on success
1763  *    FALSE on failure
1764  *
1765  */
1766 BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs,
1767                          LPCWSTR lpszSrc, LPCWSTR lpszDest)
1768 {
1769     INT nResCode;
1770     BOOL bSuccess = FALSE;
1771     LPWININETAPPINFOW hIC = NULL;
1772
1773     TRACE("\n");
1774
1775     /* Clear any error information */
1776     INTERNET_SetLastError(0);
1777
1778     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
1779         goto lend;
1780
1781     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1782     if (nResCode == 350)
1783     {
1784         if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
1785             goto lend;
1786
1787         nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1788     }
1789
1790     if (nResCode == 250)
1791         bSuccess = TRUE;
1792     else
1793         FTP_SetResponseError(nResCode);
1794
1795 lend:
1796     hIC = lpwfs->lpAppInfo;
1797     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1798     {
1799         INTERNET_ASYNC_RESULT iar;
1800
1801         iar.dwResult = (DWORD)bSuccess;
1802         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1803         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1804             &iar, sizeof(INTERNET_ASYNC_RESULT));
1805     }
1806
1807     return bSuccess;
1808 }
1809
1810 /***********************************************************************
1811  *           FtpCommandA  (WININET.@)
1812  */
1813 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1814                          LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1815 {
1816     FIXME("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1817           debugstr_a(lpszCommand), dwContext, phFtpCommand);
1818
1819     return TRUE;
1820 }
1821
1822 /***********************************************************************
1823  *           FtpCommandW  (WININET.@)
1824  */
1825 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1826                          LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1827 {
1828     FIXME("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1829           debugstr_w(lpszCommand), dwContext, phFtpCommand);
1830
1831     return TRUE;
1832 }
1833
1834 /***********************************************************************
1835  *           FTP_Connect (internal)
1836  *
1837  * Connect to a ftp server
1838  *
1839  * RETURNS
1840  *   HINTERNET a session handle on success
1841  *   NULL on failure
1842  *
1843  * NOTES:
1844  *
1845  * Windows uses 'anonymous' as the username, when given a NULL username
1846  * and a NULL password. The password is first looked up in:
1847  *
1848  * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
1849  *
1850  * If this entry is not present it uses the current username as the password.
1851  *
1852  */
1853
1854 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
1855         INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
1856         LPCWSTR lpszPassword, DWORD dwFlags, DWORD dwContext,
1857         DWORD dwInternalFlags)
1858 {
1859     static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
1860                                    'M','i','c','r','o','s','o','f','t','\\',
1861                                    'W','i','n','d','o','w','s','\\',
1862                                    'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1863                                    'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
1864     static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
1865     static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
1866     static const WCHAR szEmpty[] = {'\0'};
1867     struct sockaddr_in socketAddr;
1868     INT nsocket = -1;
1869     UINT sock_namelen;
1870     BOOL bSuccess = FALSE;
1871     LPWININETFTPSESSIONW lpwfs = NULL;
1872     HINTERNET handle = NULL;
1873
1874     TRACE("%p  Server(%s) Port(%d) User(%s) Paswd(%s)\n",
1875             hIC, debugstr_w(lpszServerName),
1876             nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
1877
1878     assert( hIC->hdr.htype == WH_HINIT );
1879
1880     if (NULL == lpszUserName && NULL != lpszPassword)
1881     {
1882         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1883         goto lerror;
1884     }
1885     
1886     lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
1887     if (NULL == lpwfs)
1888     {
1889         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1890         goto lerror;
1891     }
1892
1893     if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
1894         nServerPort = INTERNET_DEFAULT_FTP_PORT;
1895
1896     lpwfs->hdr.htype = WH_HFTPSESSION;
1897     lpwfs->hdr.dwFlags = dwFlags;
1898     lpwfs->hdr.dwContext = dwContext;
1899     lpwfs->hdr.dwInternalFlags = dwInternalFlags;
1900     lpwfs->hdr.dwRefCount = 1;
1901     /* FIXME: Native sends INTERNET_STATUS_CLOSING_CONNECTION and
1902      * INTERNET_STATUS_CONNECTION_CLOSED, need an equivalent FTP_CloseConnection
1903      * function */
1904     lpwfs->hdr.close_connection = NULL;
1905     lpwfs->hdr.destroy = FTP_CloseSessionHandle;
1906     lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
1907     lpwfs->download_in_progress = NULL;
1908
1909     WININET_AddRef( &hIC->hdr );
1910     lpwfs->lpAppInfo = hIC;
1911
1912     handle = WININET_AllocHandle( &lpwfs->hdr );
1913     if( !handle )
1914     {
1915         ERR("Failed to alloc handle\n");
1916         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1917         goto lerror;
1918     }
1919
1920     if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
1921         if(strchrW(hIC->lpszProxy, ' '))
1922             FIXME("Several proxies not implemented.\n");
1923         if(hIC->lpszProxyBypass)
1924             FIXME("Proxy bypass is ignored.\n");
1925     }
1926     if ( !lpszUserName) {
1927         HKEY key;
1928         WCHAR szPassword[MAX_PATH];
1929         DWORD len = sizeof(szPassword);
1930
1931         lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
1932
1933         RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
1934         if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
1935             /* Nothing in the registry, get the username and use that as the password */
1936             if (!GetUserNameW(szPassword, &len)) {
1937                 /* Should never get here, but use an empty password as failsafe */
1938                 strcpyW(szPassword, szEmpty);
1939             }
1940         }
1941         RegCloseKey(key);
1942
1943         TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
1944         lpwfs->lpszPassword = WININET_strdupW(szPassword);
1945     }
1946     else {
1947         lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
1948
1949         if (lpszPassword)
1950             lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
1951         else
1952             lpwfs->lpszPassword = WININET_strdupW(szEmpty);
1953     }
1954     
1955     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
1956     if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
1957     {
1958         INTERNET_ASYNC_RESULT iar;
1959
1960         iar.dwResult = (DWORD)handle;
1961         iar.dwError = ERROR_SUCCESS;
1962
1963         SendAsyncCallback(&hIC->hdr, dwContext,
1964                       INTERNET_STATUS_HANDLE_CREATED, &iar,
1965                       sizeof(INTERNET_ASYNC_RESULT));
1966     }
1967         
1968     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
1969         (LPWSTR) lpszServerName, strlenW(lpszServerName));
1970
1971     if (!GetAddress(lpszServerName, nServerPort, &socketAddr))
1972     {
1973         INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1974         goto lerror;
1975     }
1976
1977     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
1978         (LPWSTR) lpszServerName, strlenW(lpszServerName));
1979
1980     nsocket = socket(AF_INET,SOCK_STREAM,0);
1981     if (nsocket == -1)
1982     {
1983         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1984         goto lerror;
1985     }
1986
1987     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
1988         &socketAddr, sizeof(struct sockaddr_in));
1989
1990     if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
1991     {
1992         ERR("Unable to connect (%s)\n", strerror(errno));
1993         INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1994     }
1995     else
1996     {
1997         TRACE("Connected to server\n");
1998         lpwfs->sndSocket = nsocket;
1999         SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2000             &socketAddr, sizeof(struct sockaddr_in));
2001
2002         sock_namelen = sizeof(lpwfs->socketAddress);
2003         getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2004
2005         if (FTP_ConnectToHost(lpwfs))
2006         {
2007             TRACE("Successfully logged into server\n");
2008             bSuccess = TRUE;
2009         }
2010     }
2011
2012 lerror:
2013     if (!bSuccess && nsocket != -1)
2014         closesocket(nsocket);
2015
2016     if (!bSuccess && lpwfs)
2017     {
2018         HeapFree(GetProcessHeap(), 0, lpwfs);
2019         WININET_FreeHandle( handle );
2020         handle = NULL;
2021         lpwfs = NULL;
2022     }
2023
2024     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2025     {
2026         INTERNET_ASYNC_RESULT iar;
2027
2028         iar.dwResult = (DWORD)lpwfs;
2029         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2030         SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2031             &iar, sizeof(INTERNET_ASYNC_RESULT));
2032     }
2033
2034     return handle;
2035 }
2036
2037
2038 /***********************************************************************
2039  *           FTP_ConnectToHost (internal)
2040  *
2041  * Connect to a ftp server
2042  *
2043  * RETURNS
2044  *   TRUE on success
2045  *   NULL on failure
2046  *
2047  */
2048 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
2049 {
2050     INT nResCode;
2051     BOOL bSuccess = FALSE;
2052
2053     TRACE("\n");
2054     FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2055
2056     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2057         goto lend;
2058
2059     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2060     if (nResCode)
2061     {
2062         /* Login successful... */
2063         if (nResCode == 230)
2064             bSuccess = TRUE;
2065         /* User name okay, need password... */
2066         else if (nResCode == 331)
2067             bSuccess = FTP_SendPassword(lpwfs);
2068         /* Need account for login... */
2069         else if (nResCode == 332)
2070             bSuccess = FTP_SendAccount(lpwfs);
2071         else
2072             FTP_SetResponseError(nResCode);
2073     }
2074
2075     TRACE("Returning %d\n", bSuccess);
2076 lend:
2077     return bSuccess;
2078 }
2079
2080
2081 /***********************************************************************
2082  *           FTP_SendCommandA (internal)
2083  *
2084  * Send command to server
2085  *
2086  * RETURNS
2087  *   TRUE on success
2088  *   NULL on failure
2089  *
2090  */
2091 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2092         INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext)
2093 {
2094         DWORD len;
2095         CHAR *buf;
2096         DWORD nBytesSent = 0;
2097         int nRC = 0;
2098         DWORD dwParamLen;
2099
2100         TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2101
2102         if (lpfnStatusCB)
2103         {
2104             lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2105         }
2106
2107         dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2108         len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2109         if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2110         {
2111             INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2112             return FALSE;
2113         }
2114         sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2115                 dwParamLen ? lpszParam : "", szCRLF);
2116
2117         TRACE("Sending (%s) len(%d)\n", buf, len);
2118         while((nBytesSent < len) && (nRC != -1))
2119         {
2120                 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2121                 nBytesSent += nRC;
2122         }
2123
2124         HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2125
2126         if (lpfnStatusCB)
2127         {
2128             lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2129                          &nBytesSent, sizeof(DWORD));
2130         }
2131
2132         TRACE("Sent %d bytes\n", nBytesSent);
2133         return (nRC != -1);
2134 }
2135
2136 /***********************************************************************
2137  *           FTP_SendCommand (internal)
2138  *
2139  * Send command to server
2140  *
2141  * RETURNS
2142  *   TRUE on success
2143  *   NULL on failure
2144  *
2145  */
2146 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2147         INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext)
2148 {
2149     BOOL ret;
2150     LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
2151     ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2152     HeapFree(GetProcessHeap(), 0, lpszParamA);
2153     return ret;
2154 }
2155
2156 /***********************************************************************
2157  *           FTP_ReceiveResponse (internal)
2158  *
2159  * Receive response from server
2160  *
2161  * RETURNS
2162  *   Reply code on success
2163  *   0 on failure
2164  *
2165  */
2166 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD dwContext)
2167 {
2168     LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2169     DWORD nRecv;
2170     INT rc = 0;
2171     char firstprefix[5];
2172     BOOL multiline = FALSE;
2173     LPWININETAPPINFOW hIC = NULL;
2174
2175     TRACE("socket(%d)\n", lpwfs->sndSocket);
2176
2177     hIC = lpwfs->lpAppInfo;
2178     SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2179
2180     while(1)
2181     {
2182         if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2183             goto lerror;
2184
2185         if (nRecv >= 3)
2186         {
2187             if(!multiline)
2188             {
2189                 if(lpszResponse[3] != '-')
2190                     break;
2191                 else
2192                 {  /* Start of multiline repsonse.  Loop until we get "nnn " */
2193                     multiline = TRUE;
2194                     memcpy(firstprefix, lpszResponse, 3);
2195                     firstprefix[3] = ' ';
2196                     firstprefix[4] = '\0';
2197                 }
2198             }
2199             else
2200             {
2201                 if(!memcmp(firstprefix, lpszResponse, 4))
2202                     break;
2203             }
2204         }
2205     }
2206
2207     if (nRecv >= 3)
2208     {
2209         rc = atoi(lpszResponse);
2210
2211         SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2212                     &nRecv, sizeof(DWORD));
2213     }
2214
2215 lerror:
2216     TRACE("return %d\n", rc);
2217     return rc;
2218 }
2219
2220
2221 /***********************************************************************
2222  *           FTP_SendPassword (internal)
2223  *
2224  * Send password to ftp server
2225  *
2226  * RETURNS
2227  *   TRUE on success
2228  *   NULL on failure
2229  *
2230  */
2231 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2232 {
2233     INT nResCode;
2234     BOOL bSuccess = FALSE;
2235
2236     TRACE("\n");
2237     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2238         goto lend;
2239
2240     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2241     if (nResCode)
2242     {
2243         TRACE("Received reply code %d\n", nResCode);
2244         /* Login successful... */
2245         if (nResCode == 230)
2246             bSuccess = TRUE;
2247         /* Command not implemented, superfluous at the server site... */
2248         /* Need account for login... */
2249         else if (nResCode == 332)
2250             bSuccess = FTP_SendAccount(lpwfs);
2251         else
2252             FTP_SetResponseError(nResCode);
2253     }
2254
2255 lend:
2256     TRACE("Returning %d\n", bSuccess);
2257     return bSuccess;
2258 }
2259
2260
2261 /***********************************************************************
2262  *           FTP_SendAccount (internal)
2263  *
2264  *
2265  *
2266  * RETURNS
2267  *   TRUE on success
2268  *   FALSE on failure
2269  *
2270  */
2271 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2272 {
2273     INT nResCode;
2274     BOOL bSuccess = FALSE;
2275
2276     TRACE("\n");
2277     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2278         goto lend;
2279
2280     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2281     if (nResCode)
2282         bSuccess = TRUE;
2283     else
2284         FTP_SetResponseError(nResCode);
2285
2286 lend:
2287     return bSuccess;
2288 }
2289
2290
2291 /***********************************************************************
2292  *           FTP_SendStore (internal)
2293  *
2294  * Send request to upload file to ftp server
2295  *
2296  * RETURNS
2297  *   TRUE on success
2298  *   FALSE on failure
2299  *
2300  */
2301 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2302 {
2303     INT nResCode;
2304     BOOL bSuccess = FALSE;
2305
2306     TRACE("\n");
2307     if (!FTP_InitListenSocket(lpwfs))
2308         goto lend;
2309
2310     if (!FTP_SendType(lpwfs, dwType))
2311         goto lend;
2312
2313     if (!FTP_SendPortOrPasv(lpwfs))
2314         goto lend;
2315
2316     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2317             goto lend;
2318     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2319     if (nResCode)
2320     {
2321         if (nResCode == 150 || nResCode == 125)
2322             bSuccess = TRUE;
2323         else
2324             FTP_SetResponseError(nResCode);
2325     }
2326
2327 lend:
2328     if (!bSuccess && lpwfs->lstnSocket != -1)
2329     {
2330         closesocket(lpwfs->lstnSocket);
2331         lpwfs->lstnSocket = -1;
2332     }
2333
2334     return bSuccess;
2335 }
2336
2337
2338 /***********************************************************************
2339  *           FTP_InitListenSocket (internal)
2340  *
2341  * Create a socket to listen for server response
2342  *
2343  * RETURNS
2344  *   TRUE on success
2345  *   FALSE on failure
2346  *
2347  */
2348 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2349 {
2350     BOOL bSuccess = FALSE;
2351     socklen_t namelen = sizeof(struct sockaddr_in);
2352
2353     TRACE("\n");
2354
2355     lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2356     if (lpwfs->lstnSocket == -1)
2357     {
2358         TRACE("Unable to create listening socket\n");
2359             goto lend;
2360     }
2361
2362     /* We obtain our ip addr from the name of the command channel socket */
2363     lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2364
2365     /* and get the system to assign us a port */
2366     lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
2367
2368     if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2369     {
2370         TRACE("Unable to bind socket\n");
2371         goto lend;
2372     }
2373
2374     if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2375     {
2376         TRACE("listen failed\n");
2377         goto lend;
2378     }
2379
2380     if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2381         bSuccess = TRUE;
2382
2383 lend:
2384     if (!bSuccess && lpwfs->lstnSocket != -1)
2385     {
2386         closesocket(lpwfs->lstnSocket);
2387         lpwfs->lstnSocket = -1;
2388     }
2389
2390     return bSuccess;
2391 }
2392
2393
2394 /***********************************************************************
2395  *           FTP_SendType (internal)
2396  *
2397  * Tell server type of data being transferred
2398  *
2399  * RETURNS
2400  *   TRUE on success
2401  *   FALSE on failure
2402  *
2403  * W98SE doesn't cache the type that's currently set
2404  * (i.e. it sends it always),
2405  * so we probably don't want to do that either.
2406  */
2407 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2408 {
2409     INT nResCode;
2410     WCHAR type[] = { 'I','\0' };
2411     BOOL bSuccess = FALSE;
2412
2413     TRACE("\n");
2414     if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2415         type[0] = 'A';
2416
2417     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2418         goto lend;
2419
2420     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2421     if (nResCode)
2422     {
2423         if (nResCode == 2)
2424             bSuccess = TRUE;
2425         else
2426             FTP_SetResponseError(nResCode);
2427     }
2428
2429 lend:
2430     return bSuccess;
2431 }
2432
2433 /***********************************************************************
2434  *           FTP_GetFileSize (internal)
2435  *
2436  * Retrieves from the server the size of the given file
2437  *
2438  * RETURNS
2439  *   TRUE on success
2440  *   FALSE on failure
2441  *
2442  */
2443 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2444 {
2445     INT nResCode;
2446     BOOL bSuccess = FALSE;
2447
2448     TRACE("\n");
2449
2450     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2451         goto lend;
2452
2453     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2454     if (nResCode)
2455     {
2456         if (nResCode == 213) {
2457             /* Now parses the output to get the actual file size */
2458             int i;
2459             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2460
2461             for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2462             if (lpszResponseBuffer[i] == '\0') return FALSE;
2463             *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2464             
2465             bSuccess = TRUE;
2466         } else {
2467             FTP_SetResponseError(nResCode);
2468         }
2469     }
2470
2471 lend:
2472     return bSuccess;
2473 }
2474
2475
2476 /***********************************************************************
2477  *           FTP_SendPort (internal)
2478  *
2479  * Tell server which port to use
2480  *
2481  * RETURNS
2482  *   TRUE on success
2483  *   FALSE on failure
2484  *
2485  */
2486 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2487 {
2488     static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2489     INT nResCode;
2490     WCHAR szIPAddress[64];
2491     BOOL bSuccess = FALSE;
2492     TRACE("\n");
2493
2494     sprintfW(szIPAddress, szIPFormat,
2495          lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2496         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2497         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2498         (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2499         lpwfs->lstnSocketAddress.sin_port & 0xFF,
2500         (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2501
2502     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2503         goto lend;
2504
2505     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2506     if (nResCode)
2507     {
2508         if (nResCode == 200)
2509             bSuccess = TRUE;
2510         else
2511             FTP_SetResponseError(nResCode);
2512     }
2513
2514 lend:
2515     return bSuccess;
2516 }
2517
2518
2519 /***********************************************************************
2520  *           FTP_DoPassive (internal)
2521  *
2522  * Tell server that we want to do passive transfers
2523  * and connect data socket
2524  *
2525  * RETURNS
2526  *   TRUE on success
2527  *   FALSE on failure
2528  *
2529  */
2530 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2531 {
2532     INT nResCode;
2533     BOOL bSuccess = FALSE;
2534
2535     TRACE("\n");
2536     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2537         goto lend;
2538
2539     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2540     if (nResCode)
2541     {
2542         if (nResCode == 227)
2543         {
2544             LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2545             LPSTR p;
2546             int f[6];
2547             int i;
2548             char *pAddr, *pPort;
2549             INT nsocket = -1;
2550             struct sockaddr_in dataSocketAddress;
2551
2552             p = lpszResponseBuffer+4; /* skip status code */
2553             while (*p != '\0' && (*p < '0' || *p > '9')) p++;
2554
2555             if (*p == '\0')
2556             {
2557                 ERR("no address found in response, aborting\n");
2558                 goto lend;
2559             }
2560
2561             if (sscanf(p, "%d,%d,%d,%d,%d,%d",  &f[0], &f[1], &f[2], &f[3],
2562                                                 &f[4], &f[5]) != 6)
2563             {
2564                 ERR("unknown response address format '%s', aborting\n", p);
2565                 goto lend;
2566             }
2567             for (i=0; i < 6; i++)
2568                 f[i] = f[i] & 0xff;
2569
2570             dataSocketAddress = lpwfs->socketAddress;
2571             pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2572             pPort = (char *)&(dataSocketAddress.sin_port);
2573             pAddr[0] = f[0];
2574             pAddr[1] = f[1];
2575             pAddr[2] = f[2];
2576             pAddr[3] = f[3];
2577             pPort[0] = f[4];
2578             pPort[1] = f[5];
2579
2580             nsocket = socket(AF_INET,SOCK_STREAM,0);
2581             if (nsocket == -1)
2582                 goto lend;
2583
2584             if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2585             {
2586                 ERR("can't connect passive FTP data port.\n");
2587                 closesocket(nsocket);
2588                 goto lend;
2589             }
2590             lpwfs->pasvSocket = nsocket;
2591             bSuccess = TRUE;
2592         }
2593         else
2594             FTP_SetResponseError(nResCode);
2595     }
2596
2597 lend:
2598     return bSuccess;
2599 }
2600
2601
2602 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2603 {
2604     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2605     {
2606         if (!FTP_DoPassive(lpwfs))
2607             return FALSE;
2608     }
2609     else
2610     {
2611         if (!FTP_SendPort(lpwfs))
2612             return FALSE;
2613     }
2614     return TRUE;
2615 }
2616
2617
2618 /***********************************************************************
2619  *           FTP_GetDataSocket (internal)
2620  *
2621  * Either accepts an incoming data socket connection from the server
2622  * or just returns the already opened socket after a PASV command
2623  * in case of passive FTP.
2624  *
2625  *
2626  * RETURNS
2627  *   TRUE on success
2628  *   FALSE on failure
2629  *
2630  */
2631 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
2632 {
2633     struct sockaddr_in saddr;
2634     socklen_t addrlen = sizeof(struct sockaddr);
2635
2636     TRACE("\n");
2637     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2638     {
2639         *nDataSocket = lpwfs->pasvSocket;
2640     }
2641     else
2642     {
2643         *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
2644         closesocket(lpwfs->lstnSocket);
2645         lpwfs->lstnSocket = -1;
2646     }
2647     return *nDataSocket != -1;
2648 }
2649
2650
2651 /***********************************************************************
2652  *           FTP_SendData (internal)
2653  *
2654  * Send data to the server
2655  *
2656  * RETURNS
2657  *   TRUE on success
2658  *   FALSE on failure
2659  *
2660  */
2661 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
2662 {
2663     BY_HANDLE_FILE_INFORMATION fi;
2664     DWORD nBytesRead = 0;
2665     DWORD nBytesSent = 0;
2666     DWORD nTotalSent = 0;
2667     DWORD nBytesToSend, nLen;
2668     int nRC = 1;
2669     time_t s_long_time, e_long_time;
2670     LONG nSeconds;
2671     CHAR *lpszBuffer;
2672
2673     TRACE("\n");
2674     lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2675
2676     /* Get the size of the file. */
2677     GetFileInformationByHandle(hFile, &fi);
2678     time(&s_long_time);
2679
2680     do
2681     {
2682         nBytesToSend = nBytesRead - nBytesSent;
2683
2684         if (nBytesToSend <= 0)
2685         {
2686             /* Read data from file. */
2687             nBytesSent = 0;
2688             if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
2689             ERR("Failed reading from file\n");
2690
2691             if (nBytesRead > 0)
2692                 nBytesToSend = nBytesRead;
2693             else
2694                 break;
2695         }
2696
2697         nLen = DATA_PACKET_SIZE < nBytesToSend ?
2698             DATA_PACKET_SIZE : nBytesToSend;
2699         nRC  = send(nDataSocket, lpszBuffer, nLen, 0);
2700
2701         if (nRC != -1)
2702         {
2703             nBytesSent += nRC;
2704             nTotalSent += nRC;
2705         }
2706
2707         /* Do some computation to display the status. */
2708         time(&e_long_time);
2709         nSeconds = e_long_time - s_long_time;
2710         if( nSeconds / 60 > 0 )
2711         {
2712             TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
2713             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
2714             nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
2715         }
2716         else
2717         {
2718             TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
2719             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
2720             (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
2721         }
2722     } while (nRC != -1);
2723
2724     TRACE("file transfer complete!\n");
2725
2726     HeapFree(GetProcessHeap(), 0, lpszBuffer);
2727
2728     return nTotalSent;
2729 }
2730
2731
2732 /***********************************************************************
2733  *           FTP_SendRetrieve (internal)
2734  *
2735  * Send request to retrieve a file
2736  *
2737  * RETURNS
2738  *   Number of bytes to be received on success
2739  *   0 on failure
2740  *
2741  */
2742 static DWORD FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2743 {
2744     INT nResCode;
2745     DWORD nResult = 0;
2746
2747     TRACE("\n");
2748     if (!FTP_InitListenSocket(lpwfs))
2749         goto lend;
2750
2751     if (!FTP_SendType(lpwfs, dwType))
2752         goto lend;
2753
2754     if (!FTP_SendPortOrPasv(lpwfs))
2755         goto lend;
2756
2757     if (!FTP_GetFileSize(lpwfs, lpszRemoteFile, &nResult))
2758         goto lend;
2759
2760     TRACE("Waiting to receive %d bytes\n", nResult);
2761     
2762     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0))
2763         goto lend;
2764
2765     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2766     if ((nResCode != 125) && (nResCode != 150)) {
2767         /* That means that we got an error getting the file. */
2768         nResult = 0;
2769     }
2770
2771 lend:
2772     if (0 == nResult && lpwfs->lstnSocket != -1)
2773     {
2774         closesocket(lpwfs->lstnSocket);
2775         lpwfs->lstnSocket = -1;
2776     }
2777
2778     return nResult;
2779 }
2780
2781
2782 /***********************************************************************
2783  *           FTP_RetrieveData  (internal)
2784  *
2785  * Retrieve data from server
2786  *
2787  * RETURNS
2788  *   TRUE on success
2789  *   FALSE on failure
2790  *
2791  */
2792 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile)
2793 {
2794     DWORD nBytesWritten;
2795     DWORD nBytesReceived = 0;
2796     INT nRC = 0;
2797     CHAR *lpszBuffer;
2798
2799     TRACE("\n");
2800
2801     lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2802     if (NULL == lpszBuffer)
2803     {
2804         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2805         return FALSE;
2806     }
2807
2808     while (nBytesReceived < nBytes && nRC != -1)
2809     {
2810         nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
2811         if (nRC != -1)
2812         {
2813             /* other side closed socket. */
2814             if (nRC == 0)
2815                 goto recv_end;
2816             WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
2817             nBytesReceived += nRC;
2818         }
2819
2820         TRACE("%d bytes of %d (%d%%)\r", nBytesReceived, nBytes,
2821            nBytesReceived * 100 / nBytes);
2822     }
2823
2824     TRACE("Data transfer complete\n");
2825
2826 recv_end:
2827     HeapFree(GetProcessHeap(), 0, lpszBuffer);
2828
2829     return  (nRC != -1);
2830 }
2831
2832
2833 /***********************************************************************
2834  *           FTP_CloseSessionHandle (internal)
2835  *
2836  * Deallocate session handle
2837  *
2838  * RETURNS
2839  *   TRUE on success
2840  *   FALSE on failure
2841  *
2842  */
2843 static void FTP_CloseSessionHandle(LPWININETHANDLEHEADER hdr)
2844 {
2845     LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2846
2847     TRACE("\n");
2848
2849     WININET_Release(&lpwfs->lpAppInfo->hdr);
2850
2851     if (lpwfs->download_in_progress != NULL)
2852         lpwfs->download_in_progress->session_deleted = TRUE;
2853     
2854     if (lpwfs->sndSocket != -1)
2855         closesocket(lpwfs->sndSocket);
2856
2857     if (lpwfs->lstnSocket != -1)
2858         closesocket(lpwfs->lstnSocket);
2859
2860     HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2861     HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2862     HeapFree(GetProcessHeap(), 0, lpwfs);
2863 }
2864
2865
2866 /***********************************************************************
2867  *           FTP_FindNextFileW (Internal)
2868  *
2869  * Continues a file search from a previous call to FindFirstFile
2870  *
2871  * RETURNS
2872  *    TRUE on success
2873  *    FALSE on failure
2874  *
2875  */
2876 BOOL WINAPI FTP_FindNextFileW(LPWININETFTPFINDNEXTW lpwh, LPVOID lpvFindData)
2877 {
2878     BOOL bSuccess = TRUE;
2879     LPWIN32_FIND_DATAW lpFindFileData;
2880
2881     TRACE("index(%d) size(%d)\n", lpwh->index, lpwh->size);
2882
2883     assert (lpwh->hdr.htype == WH_HFTPFINDNEXT);
2884
2885     /* Clear any error information */
2886     INTERNET_SetLastError(0);
2887
2888     lpFindFileData = (LPWIN32_FIND_DATAW) lpvFindData;
2889     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
2890
2891     if (lpwh->index >= lpwh->size)
2892     {
2893         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
2894         bSuccess = FALSE;
2895         goto lend;
2896     }
2897
2898     FTP_ConvertFileProp(&lpwh->lpafp[lpwh->index], lpFindFileData);
2899     lpwh->index++;
2900
2901     TRACE("\nName: %s\nSize: %d\n", debugstr_w(lpFindFileData->cFileName), lpFindFileData->nFileSizeLow);
2902
2903 lend:
2904
2905     if (lpwh->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2906     {
2907         INTERNET_ASYNC_RESULT iar;
2908
2909         iar.dwResult = (DWORD)bSuccess;
2910         iar.dwError = iar.dwError = bSuccess ? ERROR_SUCCESS :
2911                                                INTERNET_GetLastError();
2912
2913         INTERNET_SendCallback(&lpwh->hdr, lpwh->hdr.dwContext,
2914                               INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2915                               sizeof(INTERNET_ASYNC_RESULT));
2916     }
2917
2918     return bSuccess;
2919 }
2920
2921
2922 /***********************************************************************
2923  *           FTP_CloseFindNextHandle (internal)
2924  *
2925  * Deallocate session handle
2926  *
2927  * RETURNS
2928  *   TRUE on success
2929  *   FALSE on failure
2930  *
2931  */
2932 static void FTP_CloseFindNextHandle(LPWININETHANDLEHEADER hdr)
2933 {
2934     LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
2935     DWORD i;
2936
2937     TRACE("\n");
2938
2939     WININET_Release(&lpwfn->lpFtpSession->hdr);
2940
2941     for (i = 0; i < lpwfn->size; i++)
2942     {
2943         HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
2944     }
2945
2946     HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
2947     HeapFree(GetProcessHeap(), 0, lpwfn);
2948 }
2949
2950 /***********************************************************************
2951  *           FTP_CloseFileTransferHandle (internal)
2952  *
2953  * Closes the file transfer handle. This also 'cleans' the data queue of
2954  * the 'transfer conplete' message (this is a bit of a hack though :-/ )
2955  *
2956  */
2957 static void FTP_CloseFileTransferHandle(LPWININETHANDLEHEADER hdr)
2958 {
2959     LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
2960     LPWININETFTPSESSIONW lpwfs = lpwh->lpFtpSession;
2961     INT nResCode;
2962
2963     TRACE("\n");
2964
2965     WININET_Release(&lpwh->lpFtpSession->hdr);
2966
2967     if (!lpwh->session_deleted)
2968         lpwfs->download_in_progress = NULL;
2969
2970     /* This just serves to flush the control socket of any spurrious lines written
2971        to it (like '226 Transfer complete.').
2972
2973        Wonder what to do if the server sends us an error code though...
2974     */
2975     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2976     
2977     if (lpwh->nDataSocket != -1)
2978         closesocket(lpwh->nDataSocket);
2979
2980     HeapFree(GetProcessHeap(), 0, lpwh);
2981 }
2982
2983 /***********************************************************************
2984  *           FTP_ReceiveFileList (internal)
2985  *
2986  * Read file list from server
2987  *
2988  * RETURNS
2989  *   Handle to file list on success
2990  *   NULL on failure
2991  *
2992  */
2993 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
2994         LPWIN32_FIND_DATAW lpFindFileData, DWORD dwContext)
2995 {
2996     DWORD dwSize = 0;
2997     LPFILEPROPERTIESW lpafp = NULL;
2998     LPWININETFTPFINDNEXTW lpwfn = NULL;
2999     HINTERNET handle = 0;
3000
3001     TRACE("(%p,%d,%s,%p,%d)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3002
3003     if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3004     {
3005         if(lpFindFileData)
3006             FTP_ConvertFileProp(lpafp, lpFindFileData);
3007
3008         lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3009         if (lpwfn)
3010         {
3011             lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3012             lpwfn->hdr.dwContext = dwContext;
3013             lpwfn->hdr.dwRefCount = 1;
3014             lpwfn->hdr.close_connection = NULL;
3015             lpwfn->hdr.destroy = FTP_CloseFindNextHandle;
3016             lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3017             lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3018             lpwfn->size = dwSize;
3019             lpwfn->lpafp = lpafp;
3020
3021             WININET_AddRef( &lpwfs->hdr );
3022             lpwfn->lpFtpSession = lpwfs;
3023
3024             handle = WININET_AllocHandle( &lpwfn->hdr );
3025         }
3026     }
3027
3028     if( lpwfn )
3029         WININET_Release( &lpwfn->hdr );
3030
3031     TRACE("Matched %d files\n", dwSize);
3032     return handle;
3033 }
3034
3035
3036 /***********************************************************************
3037  *           FTP_ConvertFileProp (internal)
3038  *
3039  * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3040  *
3041  * RETURNS
3042  *   TRUE on success
3043  *   FALSE on failure
3044  *
3045  */
3046 BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3047 {
3048     BOOL bSuccess = FALSE;
3049
3050     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3051
3052     if (lpafp)
3053     {
3054         /* Convert 'Unix' time to Windows time */
3055         RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
3056                                   (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
3057         lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3058         lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3059         
3060         /* Not all fields are filled in */
3061         lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3062         lpFindFileData->nFileSizeLow = lpafp->nSize;
3063
3064         if (lpafp->bIsDirectory)
3065             lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3066
3067         if (lpafp->lpszName)
3068             lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3069
3070         bSuccess = TRUE;
3071     }
3072
3073     return bSuccess;
3074 }
3075
3076 /***********************************************************************
3077  *           FTP_ParseNextFile (internal)
3078  *
3079  * Parse the next line in file listing
3080  *
3081  * RETURNS
3082  *   TRUE on success
3083  *   FALSE on failure
3084  */
3085 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3086 {
3087     static const char szSpace[] = " \t";
3088     DWORD nBufLen;
3089     char *pszLine;
3090     char *pszToken;
3091     char *pszTmp;
3092     BOOL found = FALSE;
3093     int i;
3094     
3095     lpfp->lpszName = NULL;
3096     do {
3097         if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3098             return FALSE;
3099     
3100         pszToken = strtok(pszLine, szSpace);
3101         /* ls format
3102          * <Permissions> <NoLinks> <owner>   <group> <size> <date>  <time or year> <filename>
3103          *
3104          * For instance:
3105          * drwx--s---     2         pcarrier  ens     512    Sep 28  1995           pcarrier
3106          */
3107         if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3108             if(!FTP_ParsePermission(pszToken, lpfp))
3109                 lpfp->bIsDirectory = FALSE;
3110             for(i=0; i<=3; i++) {
3111               if(!(pszToken = strtok(NULL, szSpace)))
3112                   break;
3113             }
3114             if(!pszToken) continue;
3115             if(lpfp->bIsDirectory) {
3116                 TRACE("Is directory\n");
3117                 lpfp->nSize = 0;
3118             }
3119             else {
3120                 TRACE("Size: %s\n", pszToken);
3121                 lpfp->nSize = atol(pszToken);
3122             }
3123             
3124             lpfp->tmLastModified.tm_sec  = 0;
3125             lpfp->tmLastModified.tm_min  = 0;
3126             lpfp->tmLastModified.tm_hour = 0;
3127             lpfp->tmLastModified.tm_mday = 0;
3128             lpfp->tmLastModified.tm_mon  = 0;
3129             lpfp->tmLastModified.tm_year = 0;
3130             
3131             /* Determine month */
3132             pszToken = strtok(NULL, szSpace);
3133             if(!pszToken) continue;
3134             if(strlen(pszToken) >= 3) {
3135                 pszToken[3] = 0;
3136                 if((pszTmp = StrStrIA(szMonths, pszToken)))
3137                     lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
3138             }
3139             /* Determine day */
3140             pszToken = strtok(NULL, szSpace);
3141             if(!pszToken) continue;
3142             lpfp->tmLastModified.tm_mday = atoi(pszToken);
3143             /* Determine time or year */
3144             pszToken = strtok(NULL, szSpace);
3145             if(!pszToken) continue;
3146             if((pszTmp = strchr(pszToken, ':'))) {
3147                 struct tm* apTM;
3148                 time_t aTime;
3149                 *pszTmp = 0;
3150                 pszTmp++;
3151                 lpfp->tmLastModified.tm_min = atoi(pszTmp);
3152                 lpfp->tmLastModified.tm_hour = atoi(pszToken);
3153                 time(&aTime);
3154                 apTM = localtime(&aTime);
3155                 lpfp->tmLastModified.tm_year = apTM->tm_year;
3156             }
3157             else {
3158                 lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
3159                 lpfp->tmLastModified.tm_hour = 12;
3160             }
3161             TRACE("Mod time: %02d:%02d:%02d  %02d/%02d/%02d\n",
3162                 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3163                 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3164                 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3165
3166             pszToken = strtok(NULL, szSpace);
3167             if(!pszToken) continue;
3168             lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3169             TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3170         }
3171         /* NT way of parsing ... :
3172             
3173                 07-13-03  08:55PM       <DIR>          sakpatch
3174                 05-09-03  06:02PM             12656686 2003-04-21bgm_cmd_e.rgz
3175         */
3176         else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3177             lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3178             
3179             sscanf(pszToken, "%d-%d-%d",
3180                 &lpfp->tmLastModified.tm_mon,
3181                 &lpfp->tmLastModified.tm_mday,
3182                 &lpfp->tmLastModified.tm_year);
3183
3184             /* Hacky and bad Y2K protection :-) */
3185             if (lpfp->tmLastModified.tm_year < 70)
3186                 lpfp->tmLastModified.tm_year += 100;
3187             
3188             pszToken = strtok(NULL, szSpace);
3189             if(!pszToken) continue;
3190             sscanf(pszToken, "%d:%d",
3191                 &lpfp->tmLastModified.tm_hour,
3192                 &lpfp->tmLastModified.tm_min);
3193             if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3194                 lpfp->tmLastModified.tm_hour += 12;
3195             }
3196             lpfp->tmLastModified.tm_sec = 0;
3197
3198             TRACE("Mod time: %02d:%02d:%02d  %02d/%02d/%02d\n",
3199                 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3200                 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3201                 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3202             
3203             pszToken = strtok(NULL, szSpace);
3204             if(!pszToken) continue;
3205             if(!strcasecmp(pszToken, "<DIR>")) {
3206                 lpfp->bIsDirectory = TRUE;
3207                 lpfp->nSize = 0;
3208                 TRACE("Is directory\n");
3209             }
3210             else {
3211                 lpfp->bIsDirectory = FALSE;
3212                 lpfp->nSize = atol(pszToken);
3213                 TRACE("Size: %d\n", lpfp->nSize);
3214             }
3215             
3216             pszToken = strtok(NULL, szSpace);
3217             if(!pszToken) continue;
3218             lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3219             TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3220         }
3221         /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3222         else if(pszToken[0] == '+') {
3223             FIXME("EPLF Format not implemented\n");
3224         }
3225         
3226         if(lpfp->lpszName) {
3227             if((lpszSearchFile == NULL) ||
3228                (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3229                 found = TRUE;
3230                 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3231             }
3232             else {
3233                 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3234                 lpfp->lpszName = NULL;
3235             }
3236         }
3237     } while(!found);
3238     return TRUE;
3239 }
3240
3241 /***********************************************************************
3242  *           FTP_ParseDirectory (internal)
3243  *
3244  * Parse string of directory information
3245  *
3246  * RETURNS
3247  *   TRUE on success
3248  *   FALSE on failure
3249  */
3250 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3251     LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3252 {
3253     BOOL bSuccess = TRUE;
3254     INT sizeFilePropArray = 500;/*20; */
3255     INT indexFilePropArray = -1;
3256
3257     TRACE("\n");
3258
3259     /* Allocate intial file properties array */
3260     *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3261     if (!*lpafp)
3262         return FALSE;
3263
3264     do {
3265         if (indexFilePropArray+1 >= sizeFilePropArray)
3266         {
3267             LPFILEPROPERTIESW tmpafp;
3268             
3269             sizeFilePropArray *= 2;
3270             tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3271                 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3272             if (NULL == tmpafp)
3273             {
3274                 bSuccess = FALSE;
3275                 break;
3276             }
3277
3278             *lpafp = tmpafp;
3279         }
3280         indexFilePropArray++;
3281     } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3282
3283     if (bSuccess && indexFilePropArray)
3284     {
3285         if (indexFilePropArray < sizeFilePropArray - 1)
3286         {
3287             LPFILEPROPERTIESW tmpafp;
3288
3289             tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3290                 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3291             if (NULL == tmpafp)
3292                 *lpafp = tmpafp;
3293         }
3294         *dwfp = indexFilePropArray;
3295     }
3296     else
3297     {
3298         HeapFree(GetProcessHeap(), 0, *lpafp);
3299         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3300         bSuccess = FALSE;
3301     }
3302
3303     return bSuccess;
3304 }
3305
3306
3307 /***********************************************************************
3308  *           FTP_ParsePermission (internal)
3309  *
3310  * Parse permission string of directory information
3311  *
3312  * RETURNS
3313  *   TRUE on success
3314  *   FALSE on failure
3315  *
3316  */
3317 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3318 {
3319     BOOL bSuccess = TRUE;
3320     unsigned short nPermission = 0;
3321     INT nPos = 1;
3322     INT nLast  = 9;
3323
3324     TRACE("\n");
3325     if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3326     {
3327         bSuccess = FALSE;
3328         return bSuccess;
3329     }
3330
3331     lpfp->bIsDirectory = (*lpszPermission == 'd');
3332     do
3333     {
3334         switch (nPos)
3335         {
3336             case 1:
3337                 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3338                 break;
3339             case 2:
3340                 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3341                 break;
3342             case 3:
3343                 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3344                 break;
3345             case 4:
3346                 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3347                 break;
3348             case 5:
3349                 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3350                 break;
3351             case 6:
3352                 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3353                 break;
3354             case 7:
3355                 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3356                 break;
3357             case 8:
3358                 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3359                 break;
3360             case 9:
3361                 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3362                 break;
3363         }
3364         nPos++;
3365     }while (nPos <= nLast);
3366
3367     lpfp->permissions = nPermission;
3368     return bSuccess;
3369 }
3370
3371
3372 /***********************************************************************
3373  *           FTP_SetResponseError (internal)
3374  *
3375  * Set the appropriate error code for a given response from the server
3376  *
3377  * RETURNS
3378  *
3379  */
3380 static DWORD FTP_SetResponseError(DWORD dwResponse)
3381 {
3382     DWORD dwCode = 0;
3383
3384     switch(dwResponse)
3385     {
3386         case 421: /* Service not available - Server may be shutting down. */
3387             dwCode = ERROR_INTERNET_TIMEOUT;
3388             break;
3389
3390         case 425: /* Cannot open data connection. */
3391             dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3392             break;
3393
3394         case 426: /* Connection closed, transer aborted. */
3395             dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3396             break;
3397
3398         case 500: /* Syntax error. Command unrecognized. */
3399         case 501: /* Syntax error. Error in parameters or arguments. */
3400             dwCode = ERROR_INTERNET_INCORRECT_FORMAT;
3401             break;
3402
3403         case 530: /* Not logged in. Login incorrect. */
3404             dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3405             break;
3406
3407         case 550: /* File action not taken. File not found or no access. */
3408             dwCode = ERROR_INTERNET_ITEM_NOT_FOUND;
3409             break;
3410
3411         case 450: /* File action not taken. File may be busy. */
3412         case 451: /* Action aborted. Server error. */
3413         case 452: /* Action not taken. Insufficient storage space on server. */
3414         case 502: /* Command not implemented. */
3415         case 503: /* Bad sequence of commands. */
3416         case 504: /* Command not implemented for that parameter. */
3417         case 532: /* Need account for storing files */
3418         case 551: /* Requested action aborted. Page type unknown */
3419         case 552: /* Action aborted. Exceeded storage allocation */
3420         case 553: /* Action not taken. File name not allowed. */
3421
3422         default:
3423             dwCode = ERROR_INTERNET_INTERNAL_ERROR;
3424             break;
3425     }
3426
3427     INTERNET_SetLastError(dwCode);
3428     return dwCode;
3429 }