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