xref: /reactos/dll/win32/wininet/ftp.c (revision 8a978a17)
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         WININET_Release(&lpwfs->hdr);
2585         return NULL;
2586     }
2587 
2588     return lpwfs->hdr.hInternet;
2589 }
2590 
2591 
2592 /***********************************************************************
2593  *           FTP_ConnectToHost (internal)
2594  *
2595  * Connect to a ftp server
2596  *
2597  * RETURNS
2598  *   TRUE on success
2599  *   NULL on failure
2600  *
2601  */
2602 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2603 {
2604     INT nResCode;
2605     BOOL bSuccess = FALSE;
2606 
2607     TRACE("\n");
2608     FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2609 
2610     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2611         goto lend;
2612 
2613     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2614     if (nResCode)
2615     {
2616         /* Login successful... */
2617         if (nResCode == 230)
2618             bSuccess = TRUE;
2619         /* User name okay, need password... */
2620         else if (nResCode == 331)
2621             bSuccess = FTP_SendPassword(lpwfs);
2622         /* Need account for login... */
2623         else if (nResCode == 332)
2624             bSuccess = FTP_SendAccount(lpwfs);
2625         else
2626             FTP_SetResponseError(nResCode);
2627     }
2628 
2629     TRACE("Returning %d\n", bSuccess);
2630 lend:
2631     return bSuccess;
2632 }
2633 
2634 /***********************************************************************
2635  *           FTP_GetNextLine  (internal)
2636  *
2637  * Parse next line in directory string listing
2638  *
2639  * RETURNS
2640  *   Pointer to beginning of next line
2641  *   NULL on failure
2642  *
2643  */
2644 
2645 static LPSTR FTP_GetNextLine(INT nSocket, LPDWORD dwLen)
2646 {
2647     struct timeval tv = {RESPONSE_TIMEOUT,0};
2648     FD_SET set;
2649     INT nRecv = 0;
2650     LPSTR lpszBuffer = INTERNET_GetResponseBuffer();
2651 
2652     TRACE("\n");
2653 
2654     FD_ZERO(&set);
2655     FD_SET(nSocket, &set);
2656 
2657     while (nRecv < MAX_REPLY_LEN)
2658     {
2659         if (select(nSocket+1, &set, NULL, NULL, &tv) > 0)
2660         {
2661             if (sock_recv(nSocket, &lpszBuffer[nRecv], 1, 0) <= 0)
2662             {
2663                 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2664                 return NULL;
2665             }
2666 
2667             if (lpszBuffer[nRecv] == '\n')
2668             {
2669                 lpszBuffer[nRecv] = '\0';
2670                 *dwLen = nRecv - 1;
2671                 TRACE(":%d %s\n", nRecv, lpszBuffer);
2672                 return lpszBuffer;
2673             }
2674             if (lpszBuffer[nRecv] != '\r')
2675                 nRecv++;
2676         }
2677 	else
2678 	{
2679             INTERNET_SetLastError(ERROR_INTERNET_TIMEOUT);
2680             return NULL;
2681         }
2682     }
2683 
2684     return NULL;
2685 }
2686 
2687 /***********************************************************************
2688  *           FTP_SendCommandA (internal)
2689  *
2690  * Send command to server
2691  *
2692  * RETURNS
2693  *   TRUE on success
2694  *   NULL on failure
2695  *
2696  */
2697 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2698 	INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2699 {
2700     	DWORD len;
2701 	CHAR *buf;
2702 	DWORD nBytesSent = 0;
2703 	int nRC = 0;
2704 	DWORD dwParamLen;
2705 
2706 	TRACE("%d: (%s) %d\n", ftpCmd, debugstr_a(lpszParam), nSocket);
2707 
2708 	if (lpfnStatusCB)
2709         {
2710             lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2711         }
2712 
2713 	dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2714 	len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2715 	if (NULL == (buf = heap_alloc(len+1)))
2716 	{
2717 	    INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2718 	    return FALSE;
2719 	}
2720 	sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2721 		dwParamLen ? lpszParam : "", szCRLF);
2722 
2723 	TRACE("Sending (%s) len(%d)\n", debugstr_a(buf), len);
2724 	while((nBytesSent < len) && (nRC != -1))
2725 	{
2726 		nRC = sock_send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2727 		nBytesSent += nRC;
2728 	}
2729     heap_free(buf);
2730 
2731 	if (lpfnStatusCB)
2732         {
2733             lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2734                          &nBytesSent, sizeof(DWORD));
2735         }
2736 
2737 	TRACE("Sent %d bytes\n", nBytesSent);
2738 	return (nRC != -1);
2739 }
2740 
2741 /***********************************************************************
2742  *           FTP_SendCommand (internal)
2743  *
2744  * Send command to server
2745  *
2746  * RETURNS
2747  *   TRUE on success
2748  *   NULL on failure
2749  *
2750  */
2751 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2752 	INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2753 {
2754     BOOL ret;
2755     LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2756     ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2757     heap_free(lpszParamA);
2758     return ret;
2759 }
2760 
2761 /***********************************************************************
2762  *           FTP_ReceiveResponse (internal)
2763  *
2764  * Receive response from server
2765  *
2766  * RETURNS
2767  *   Reply code on success
2768  *   0 on failure
2769  *
2770  */
2771 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2772 {
2773     LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2774     DWORD nRecv;
2775     INT rc = 0;
2776     char firstprefix[5];
2777     BOOL multiline = FALSE;
2778 
2779     TRACE("socket(%d)\n", lpwfs->sndSocket);
2780 
2781     INTERNET_SendCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2782 
2783     while(1)
2784     {
2785 	if (!FTP_GetNextLine(lpwfs->sndSocket, &nRecv))
2786 	    goto lerror;
2787 
2788         if (nRecv >= 3)
2789 	{
2790 	    if(!multiline)
2791 	    {
2792 	        if(lpszResponse[3] != '-')
2793 		    break;
2794 		else
2795 		{  /* Start of multiline response.  Loop until we get "nnn " */
2796 		    multiline = TRUE;
2797 		    memcpy(firstprefix, lpszResponse, 3);
2798 		    firstprefix[3] = ' ';
2799 		    firstprefix[4] = '\0';
2800 		}
2801 	    }
2802 	    else
2803 	    {
2804 	        if(!memcmp(firstprefix, lpszResponse, 4))
2805 		    break;
2806 	    }
2807 	}
2808     }
2809 
2810     if (nRecv >= 3)
2811     {
2812         rc = atoi(lpszResponse);
2813 
2814         INTERNET_SendCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2815 		    &nRecv, sizeof(DWORD));
2816     }
2817 
2818 lerror:
2819     TRACE("return %d\n", rc);
2820     return rc;
2821 }
2822 
2823 
2824 /***********************************************************************
2825  *           FTP_SendPassword (internal)
2826  *
2827  * Send password to ftp server
2828  *
2829  * RETURNS
2830  *   TRUE on success
2831  *   NULL on failure
2832  *
2833  */
2834 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2835 {
2836     INT nResCode;
2837     BOOL bSuccess = FALSE;
2838 
2839     TRACE("\n");
2840     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2841         goto lend;
2842 
2843     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2844     if (nResCode)
2845     {
2846         TRACE("Received reply code %d\n", nResCode);
2847         /* Login successful... */
2848         if (nResCode == 230)
2849             bSuccess = TRUE;
2850         /* Command not implemented, superfluous at the server site... */
2851         /* Need account for login... */
2852         else if (nResCode == 332)
2853             bSuccess = FTP_SendAccount(lpwfs);
2854         else
2855             FTP_SetResponseError(nResCode);
2856     }
2857 
2858 lend:
2859     TRACE("Returning %d\n", bSuccess);
2860     return bSuccess;
2861 }
2862 
2863 
2864 /***********************************************************************
2865  *           FTP_SendAccount (internal)
2866  *
2867  *
2868  *
2869  * RETURNS
2870  *   TRUE on success
2871  *   FALSE on failure
2872  *
2873  */
2874 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2875 {
2876     INT nResCode;
2877     BOOL bSuccess = FALSE;
2878 
2879     TRACE("\n");
2880     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2881         goto lend;
2882 
2883     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2884     if (nResCode)
2885         bSuccess = TRUE;
2886     else
2887         FTP_SetResponseError(nResCode);
2888 
2889 lend:
2890     return bSuccess;
2891 }
2892 
2893 
2894 /***********************************************************************
2895  *           FTP_SendStore (internal)
2896  *
2897  * Send request to upload file to ftp server
2898  *
2899  * RETURNS
2900  *   TRUE on success
2901  *   FALSE on failure
2902  *
2903  */
2904 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2905 {
2906     INT nResCode;
2907     BOOL bSuccess = FALSE;
2908 
2909     TRACE("\n");
2910     if (!FTP_InitListenSocket(lpwfs))
2911         goto lend;
2912 
2913     if (!FTP_SendType(lpwfs, dwType))
2914         goto lend;
2915 
2916     if (!FTP_SendPortOrPasv(lpwfs))
2917         goto lend;
2918 
2919     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2920 	    goto lend;
2921     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2922     if (nResCode)
2923     {
2924         if (nResCode == 150 || nResCode == 125)
2925             bSuccess = TRUE;
2926 	else
2927             FTP_SetResponseError(nResCode);
2928     }
2929 
2930 lend:
2931     if (!bSuccess && lpwfs->lstnSocket != -1)
2932     {
2933         closesocket(lpwfs->lstnSocket);
2934         lpwfs->lstnSocket = -1;
2935     }
2936 
2937     return bSuccess;
2938 }
2939 
2940 
2941 /***********************************************************************
2942  *           FTP_InitListenSocket (internal)
2943  *
2944  * Create a socket to listen for server response
2945  *
2946  * RETURNS
2947  *   TRUE on success
2948  *   FALSE on failure
2949  *
2950  */
2951 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2952 {
2953     BOOL bSuccess = FALSE;
2954     socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2955 
2956     TRACE("\n");
2957 
2958     init_winsock();
2959     lpwfs->lstnSocket = socket(AF_INET, SOCK_STREAM, 0);
2960     if (lpwfs->lstnSocket == -1)
2961     {
2962         TRACE("Unable to create listening socket\n");
2963             goto lend;
2964     }
2965 
2966     /* We obtain our ip addr from the name of the command channel socket */
2967     lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2968 
2969     /* and get the system to assign us a port */
2970     lpwfs->lstnSocketAddress.sin_port = htons(0);
2971 
2972     if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2973     {
2974         TRACE("Unable to bind socket\n");
2975         goto lend;
2976     }
2977 
2978     if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2979     {
2980         TRACE("listen failed\n");
2981         goto lend;
2982     }
2983 
2984     if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2985         bSuccess = TRUE;
2986 
2987 lend:
2988     if (!bSuccess && lpwfs->lstnSocket != -1)
2989     {
2990         closesocket(lpwfs->lstnSocket);
2991         lpwfs->lstnSocket = -1;
2992     }
2993 
2994     return bSuccess;
2995 }
2996 
2997 
2998 /***********************************************************************
2999  *           FTP_SendType (internal)
3000  *
3001  * Tell server type of data being transferred
3002  *
3003  * RETURNS
3004  *   TRUE on success
3005  *   FALSE on failure
3006  *
3007  * W98SE doesn't cache the type that's currently set
3008  * (i.e. it sends it always),
3009  * so we probably don't want to do that either.
3010  */
3011 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
3012 {
3013     INT nResCode;
3014     WCHAR type[] = { 'I','\0' };
3015     BOOL bSuccess = FALSE;
3016 
3017     TRACE("\n");
3018     if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
3019         type[0] = 'A';
3020 
3021     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
3022         goto lend;
3023 
3024     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
3025     if (nResCode)
3026     {
3027         if (nResCode == 2)
3028             bSuccess = TRUE;
3029 	else
3030             FTP_SetResponseError(nResCode);
3031     }
3032 
3033 lend:
3034     return bSuccess;
3035 }
3036 
3037 
3038 #if 0  /* FIXME: should probably be used for FtpGetFileSize */
3039 /***********************************************************************
3040  *           FTP_GetFileSize (internal)
3041  *
3042  * Retrieves from the server the size of the given file
3043  *
3044  * RETURNS
3045  *   TRUE on success
3046  *   FALSE on failure
3047  *
3048  */
3049 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
3050 {
3051     INT nResCode;
3052     BOOL bSuccess = FALSE;
3053 
3054     TRACE("\n");
3055 
3056     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
3057         goto lend;
3058 
3059     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3060     if (nResCode)
3061     {
3062         if (nResCode == 213) {
3063 	    /* Now parses the output to get the actual file size */
3064 	    int i;
3065 	    LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3066 
3067 	    for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3068 	    if (lpszResponseBuffer[i] == '\0') return FALSE;
3069 	    *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3070 
3071             bSuccess = TRUE;
3072 	} else {
3073             FTP_SetResponseError(nResCode);
3074 	}
3075     }
3076 
3077 lend:
3078     return bSuccess;
3079 }
3080 #endif
3081 
3082 
3083 /***********************************************************************
3084  *           FTP_SendPort (internal)
3085  *
3086  * Tell server which port to use
3087  *
3088  * RETURNS
3089  *   TRUE on success
3090  *   FALSE on failure
3091  *
3092  */
3093 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3094 {
3095     static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3096     INT nResCode;
3097     WCHAR szIPAddress[64];
3098     BOOL bSuccess = FALSE;
3099     TRACE("\n");
3100 
3101     sprintfW(szIPAddress, szIPFormat,
3102 	 lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x000000FF,
3103         (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x0000FF00)>>8,
3104         (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x00FF0000)>>16,
3105         (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0xFF000000)>>24,
3106         lpwfs->lstnSocketAddress.sin_port & 0xFF,
3107         (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3108 
3109     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3110         goto lend;
3111 
3112     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3113     if (nResCode)
3114     {
3115         if (nResCode == 200)
3116             bSuccess = TRUE;
3117         else
3118             FTP_SetResponseError(nResCode);
3119     }
3120 
3121 lend:
3122     return bSuccess;
3123 }
3124 
3125 
3126 /***********************************************************************
3127  *           FTP_DoPassive (internal)
3128  *
3129  * Tell server that we want to do passive transfers
3130  * and connect data socket
3131  *
3132  * RETURNS
3133  *   TRUE on success
3134  *   FALSE on failure
3135  *
3136  */
3137 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3138 {
3139     INT nResCode;
3140     BOOL bSuccess = FALSE;
3141 
3142     TRACE("\n");
3143     if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3144         goto lend;
3145 
3146     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3147     if (nResCode)
3148     {
3149         if (nResCode == 227)
3150 	{
3151 	    LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3152 	    LPSTR p;
3153 	    int f[6];
3154 	    int i;
3155 	    char *pAddr, *pPort;
3156 	    INT nsocket = -1;
3157 	    struct sockaddr_in dataSocketAddress;
3158 
3159 	    p = lpszResponseBuffer+4; /* skip status code */
3160 	    while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3161 
3162 	    if (*p == '\0')
3163 	    {
3164 		ERR("no address found in response, aborting\n");
3165 		goto lend;
3166 	    }
3167 
3168 	    if (sscanf(p, "%d,%d,%d,%d,%d,%d",  &f[0], &f[1], &f[2], &f[3],
3169 				    		&f[4], &f[5]) != 6)
3170 	    {
3171 		ERR("unknown response address format '%s', aborting\n", p);
3172 		goto lend;
3173 	    }
3174 	    for (i=0; i < 6; i++)
3175 		f[i] = f[i] & 0xff;
3176 
3177 	    dataSocketAddress = lpwfs->socketAddress;
3178 	    pAddr = (char *)&(dataSocketAddress.sin_addr.S_un.S_addr);
3179 	    pPort = (char *)&(dataSocketAddress.sin_port);
3180             pAddr[0] = f[0];
3181             pAddr[1] = f[1];
3182             pAddr[2] = f[2];
3183             pAddr[3] = f[3];
3184 	    pPort[0] = f[4];
3185 	    pPort[1] = f[5];
3186 
3187             nsocket = socket(AF_INET,SOCK_STREAM,0);
3188             if (nsocket == -1)
3189                 goto lend;
3190 
3191 	    if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3192             {
3193 	        ERR("can't connect passive FTP data port.\n");
3194                 closesocket(nsocket);
3195 	        goto lend;
3196             }
3197 	    lpwfs->pasvSocket = nsocket;
3198             bSuccess = TRUE;
3199 	}
3200         else
3201             FTP_SetResponseError(nResCode);
3202     }
3203 
3204 lend:
3205     return bSuccess;
3206 }
3207 
3208 
3209 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3210 {
3211     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3212     {
3213         if (!FTP_DoPassive(lpwfs))
3214             return FALSE;
3215     }
3216     else
3217     {
3218 	if (!FTP_SendPort(lpwfs))
3219             return FALSE;
3220     }
3221     return TRUE;
3222 }
3223 
3224 
3225 /***********************************************************************
3226  *           FTP_GetDataSocket (internal)
3227  *
3228  * Either accepts an incoming data socket connection from the server
3229  * or just returns the already opened socket after a PASV command
3230  * in case of passive FTP.
3231  *
3232  *
3233  * RETURNS
3234  *   TRUE on success
3235  *   FALSE on failure
3236  *
3237  */
3238 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3239 {
3240     struct sockaddr_in saddr;
3241     socklen_t addrlen = sizeof(saddr);
3242 
3243     TRACE("\n");
3244     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3245     {
3246 	*nDataSocket = lpwfs->pasvSocket;
3247 	lpwfs->pasvSocket = -1;
3248     }
3249     else
3250     {
3251         *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3252         closesocket(lpwfs->lstnSocket);
3253         lpwfs->lstnSocket = -1;
3254     }
3255     return *nDataSocket != -1;
3256 }
3257 
3258 
3259 /***********************************************************************
3260  *           FTP_SendData (internal)
3261  *
3262  * Send data to the server
3263  *
3264  * RETURNS
3265  *   TRUE on success
3266  *   FALSE on failure
3267  *
3268  */
3269 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3270 {
3271     BY_HANDLE_FILE_INFORMATION fi;
3272     DWORD nBytesRead = 0;
3273     DWORD nBytesSent = 0;
3274     DWORD nTotalSent = 0;
3275     DWORD nBytesToSend, nLen;
3276     int nRC = 1;
3277     time_t s_long_time, e_long_time;
3278     LONG nSeconds;
3279     CHAR *lpszBuffer;
3280 
3281     TRACE("\n");
3282     lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3283 
3284     /* Get the size of the file. */
3285     GetFileInformationByHandle(hFile, &fi);
3286     time(&s_long_time);
3287 
3288     do
3289     {
3290         nBytesToSend = nBytesRead - nBytesSent;
3291 
3292         if (nBytesToSend <= 0)
3293         {
3294             /* Read data from file. */
3295             nBytesSent = 0;
3296             if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3297             ERR("Failed reading from file\n");
3298 
3299             if (nBytesRead > 0)
3300                 nBytesToSend = nBytesRead;
3301             else
3302                 break;
3303         }
3304 
3305         nLen = DATA_PACKET_SIZE < nBytesToSend ?
3306             DATA_PACKET_SIZE : nBytesToSend;
3307         nRC  = sock_send(nDataSocket, lpszBuffer, nLen, 0);
3308 
3309         if (nRC != -1)
3310         {
3311             nBytesSent += nRC;
3312             nTotalSent += nRC;
3313         }
3314 
3315         /* Do some computation to display the status. */
3316         time(&e_long_time);
3317         nSeconds = e_long_time - s_long_time;
3318         if( nSeconds / 60 > 0 )
3319         {
3320             TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3321             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3322             nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3323         }
3324         else
3325         {
3326             TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3327             nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3328             (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3329         }
3330     } while (nRC != -1);
3331 
3332     TRACE("file transfer complete!\n");
3333 
3334     heap_free(lpszBuffer);
3335     return nTotalSent;
3336 }
3337 
3338 
3339 /***********************************************************************
3340  *           FTP_SendRetrieve (internal)
3341  *
3342  * Send request to retrieve a file
3343  *
3344  * RETURNS
3345  *   Number of bytes to be received on success
3346  *   0 on failure
3347  *
3348  */
3349 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3350 {
3351     INT nResCode;
3352     BOOL ret;
3353 
3354     TRACE("\n");
3355     if (!(ret = FTP_InitListenSocket(lpwfs)))
3356         goto lend;
3357 
3358     if (!(ret = FTP_SendType(lpwfs, dwType)))
3359         goto lend;
3360 
3361     if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3362         goto lend;
3363 
3364     if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3365         goto lend;
3366 
3367     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3368     if ((nResCode != 125) && (nResCode != 150)) {
3369 	/* That means that we got an error getting the file. */
3370         FTP_SetResponseError(nResCode);
3371 	ret = FALSE;
3372     }
3373 
3374 lend:
3375     if (!ret && lpwfs->lstnSocket != -1)
3376     {
3377         closesocket(lpwfs->lstnSocket);
3378         lpwfs->lstnSocket = -1;
3379     }
3380 
3381     return ret;
3382 }
3383 
3384 
3385 /***********************************************************************
3386  *           FTP_RetrieveData  (internal)
3387  *
3388  * Retrieve data from server
3389  *
3390  * RETURNS
3391  *   TRUE on success
3392  *   FALSE on failure
3393  *
3394  */
3395 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3396 {
3397     DWORD nBytesWritten;
3398     INT nRC = 0;
3399     CHAR *lpszBuffer;
3400 
3401     TRACE("\n");
3402 
3403     lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3404     if (NULL == lpszBuffer)
3405     {
3406         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3407         return FALSE;
3408     }
3409 
3410     while (nRC != -1)
3411     {
3412         nRC = sock_recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3413         if (nRC != -1)
3414         {
3415             /* other side closed socket. */
3416             if (nRC == 0)
3417                 goto recv_end;
3418             WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3419         }
3420     }
3421 
3422     TRACE("Data transfer complete\n");
3423 
3424 recv_end:
3425     heap_free(lpszBuffer);
3426     return (nRC != -1);
3427 }
3428 
3429 /***********************************************************************
3430  *           FTPFINDNEXT_Destroy (internal)
3431  *
3432  * Deallocate session handle
3433  */
3434 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3435 {
3436     LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3437     DWORD i;
3438 
3439     TRACE("\n");
3440 
3441     WININET_Release(&lpwfn->lpFtpSession->hdr);
3442 
3443     for (i = 0; i < lpwfn->size; i++)
3444     {
3445         heap_free(lpwfn->lpafp[i].lpszName);
3446     }
3447     heap_free(lpwfn->lpafp);
3448 }
3449 
3450 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3451 {
3452     WIN32_FIND_DATAW *find_data = data;
3453     DWORD res = ERROR_SUCCESS;
3454 
3455     TRACE("index(%d) size(%d)\n", find->index, find->size);
3456 
3457     ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3458 
3459     if (find->index < find->size) {
3460         FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3461         find->index++;
3462 
3463         TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3464     }else {
3465         res = ERROR_NO_MORE_FILES;
3466     }
3467 
3468     if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3469     {
3470         INTERNET_ASYNC_RESULT iar;
3471 
3472         iar.dwResult = (res == ERROR_SUCCESS);
3473         iar.dwError = res;
3474 
3475         INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3476                               INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3477                               sizeof(INTERNET_ASYNC_RESULT));
3478     }
3479 
3480     return res;
3481 }
3482 
3483 typedef struct {
3484     task_header_t hdr;
3485     WIN32_FIND_DATAW *find_data;
3486 } find_next_task_t;
3487 
3488 static void FTPFINDNEXT_AsyncFindNextFileProc(task_header_t *hdr)
3489 {
3490     find_next_task_t *task = (find_next_task_t*)hdr;
3491 
3492     FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)task->hdr.hdr, task->find_data);
3493 }
3494 
3495 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3496 {
3497     switch(option) {
3498     case INTERNET_OPTION_HANDLE_TYPE:
3499         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3500 
3501         if (*size < sizeof(ULONG))
3502             return ERROR_INSUFFICIENT_BUFFER;
3503 
3504         *size = sizeof(DWORD);
3505         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3506         return ERROR_SUCCESS;
3507     }
3508 
3509     return INET_QueryOption(hdr, option, buffer, size, unicode);
3510 }
3511 
3512 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3513 {
3514     WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3515 
3516     if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3517     {
3518         find_next_task_t *task;
3519 
3520         task = alloc_async_task(&find->hdr, FTPFINDNEXT_AsyncFindNextFileProc, sizeof(*task));
3521         task->find_data = data;
3522 
3523         INTERNET_AsyncCall(&task->hdr);
3524         return ERROR_SUCCESS;
3525     }
3526 
3527     return FTPFINDNEXT_FindNextFileProc(find, data);
3528 }
3529 
3530 static const object_vtbl_t FTPFINDNEXTVtbl = {
3531     FTPFINDNEXT_Destroy,
3532     NULL,
3533     FTPFINDNEXT_QueryOption,
3534     INET_SetOption,
3535     NULL,
3536     NULL,
3537     NULL,
3538     FTPFINDNEXT_FindNextFileW
3539 };
3540 
3541 /***********************************************************************
3542  *           FTP_ReceiveFileList (internal)
3543  *
3544  * Read file list from server
3545  *
3546  * RETURNS
3547  *   Handle to file list on success
3548  *   NULL on failure
3549  *
3550  */
3551 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3552 	LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3553 {
3554     DWORD dwSize = 0;
3555     LPFILEPROPERTIESW lpafp = NULL;
3556     LPWININETFTPFINDNEXTW lpwfn = NULL;
3557 
3558     TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3559 
3560     if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3561     {
3562         if(lpFindFileData)
3563             FTP_ConvertFileProp(lpafp, lpFindFileData);
3564 
3565         lpwfn = alloc_object(&lpwfs->hdr, &FTPFINDNEXTVtbl, sizeof(WININETFTPFINDNEXTW));
3566         if (lpwfn)
3567         {
3568             lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3569             lpwfn->hdr.dwContext = dwContext;
3570             lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3571             lpwfn->size = dwSize;
3572             lpwfn->lpafp = lpafp;
3573 
3574             WININET_AddRef( &lpwfs->hdr );
3575             lpwfn->lpFtpSession = lpwfs;
3576             list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3577         }
3578     }
3579 
3580     TRACE("Matched %d files\n", dwSize);
3581     return lpwfn ? lpwfn->hdr.hInternet : NULL;
3582 }
3583 
3584 
3585 /***********************************************************************
3586  *           FTP_ConvertFileProp (internal)
3587  *
3588  * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3589  *
3590  * RETURNS
3591  *   TRUE on success
3592  *   FALSE on failure
3593  *
3594  */
3595 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3596 {
3597     BOOL bSuccess = FALSE;
3598 
3599     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3600 
3601     if (lpafp)
3602     {
3603         SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3604 	lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3605 	lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3606 
3607         /* Not all fields are filled in */
3608         lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3609         lpFindFileData->nFileSizeLow = lpafp->nSize;
3610 
3611 	if (lpafp->bIsDirectory)
3612 	    lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3613 
3614         if (lpafp->lpszName)
3615             lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3616 
3617 	bSuccess = TRUE;
3618     }
3619 
3620     return bSuccess;
3621 }
3622 
3623 /***********************************************************************
3624  *           FTP_ParseNextFile (internal)
3625  *
3626  * Parse the next line in file listing
3627  *
3628  * RETURNS
3629  *   TRUE on success
3630  *   FALSE on failure
3631  */
3632 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3633 {
3634     static const char szSpace[] = " \t";
3635     DWORD nBufLen;
3636     char *pszLine;
3637     char *pszToken;
3638     char *pszTmp;
3639     BOOL found = FALSE;
3640     int i;
3641 
3642     lpfp->lpszName = NULL;
3643     do {
3644         if(!(pszLine = FTP_GetNextLine(nSocket, &nBufLen)))
3645             return FALSE;
3646 
3647         pszToken = strtok(pszLine, szSpace);
3648         /* ls format
3649          * <Permissions> <NoLinks> <owner>   <group> <size> <date>  <time or year> <filename>
3650          *
3651          * For instance:
3652          * drwx--s---     2         pcarrier  ens     512    Sep 28  1995           pcarrier
3653          */
3654         if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3655             if(!FTP_ParsePermission(pszToken, lpfp))
3656                 lpfp->bIsDirectory = FALSE;
3657             for(i=0; i<=3; i++) {
3658               if(!(pszToken = strtok(NULL, szSpace)))
3659                   break;
3660             }
3661             if(!pszToken) continue;
3662             if(lpfp->bIsDirectory) {
3663                 TRACE("Is directory\n");
3664                 lpfp->nSize = 0;
3665             }
3666             else {
3667                 TRACE("Size: %s\n", pszToken);
3668                 lpfp->nSize = atol(pszToken);
3669             }
3670 
3671             lpfp->tmLastModified.wSecond = 0;
3672             lpfp->tmLastModified.wMinute = 0;
3673             lpfp->tmLastModified.wHour   = 0;
3674             lpfp->tmLastModified.wDay    = 0;
3675             lpfp->tmLastModified.wMonth  = 0;
3676             lpfp->tmLastModified.wYear   = 0;
3677 
3678             /* Determine month */
3679             pszToken = strtok(NULL, szSpace);
3680             if(!pszToken) continue;
3681             if(strlen(pszToken) >= 3) {
3682                 pszToken[3] = 0;
3683                 if((pszTmp = StrStrIA(szMonths, pszToken)))
3684                     lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3685             }
3686             /* Determine day */
3687             pszToken = strtok(NULL, szSpace);
3688             if(!pszToken) continue;
3689             lpfp->tmLastModified.wDay = atoi(pszToken);
3690             /* Determine time or year */
3691             pszToken = strtok(NULL, szSpace);
3692             if(!pszToken) continue;
3693             if((pszTmp = strchr(pszToken, ':'))) {
3694                 SYSTEMTIME curr_time;
3695                 *pszTmp = 0;
3696                 pszTmp++;
3697                 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3698                 lpfp->tmLastModified.wHour = atoi(pszToken);
3699                 GetLocalTime( &curr_time );
3700                 lpfp->tmLastModified.wYear = curr_time.wYear;
3701             }
3702             else {
3703                 lpfp->tmLastModified.wYear = atoi(pszToken);
3704                 lpfp->tmLastModified.wHour = 12;
3705             }
3706             TRACE("Mod time: %02d:%02d:%02d  %04d/%02d/%02d\n",
3707                   lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3708                   lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3709 
3710             pszToken = strtok(NULL, szSpace);
3711             if(!pszToken) continue;
3712             lpfp->lpszName = heap_strdupAtoW(pszToken);
3713             TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3714         }
3715         /* NT way of parsing ... :
3716 
3717                 07-13-03  08:55PM       <DIR>          sakpatch
3718                 05-09-03  06:02PM             12656686 2003-04-21bgm_cmd_e.rgz
3719         */
3720         else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3721             int mon, mday, year, hour, min;
3722             lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3723 
3724             sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3725             lpfp->tmLastModified.wDay   = mday;
3726             lpfp->tmLastModified.wMonth = mon;
3727             lpfp->tmLastModified.wYear  = year;
3728 
3729             /* Hacky and bad Y2K protection :-) */
3730             if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3731 
3732             pszToken = strtok(NULL, szSpace);
3733             if(!pszToken) continue;
3734             sscanf(pszToken, "%d:%d", &hour, &min);
3735             lpfp->tmLastModified.wHour   = hour;
3736             lpfp->tmLastModified.wMinute = min;
3737             if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3738                 lpfp->tmLastModified.wHour += 12;
3739             }
3740             lpfp->tmLastModified.wSecond = 0;
3741 
3742             TRACE("Mod time: %02d:%02d:%02d  %04d/%02d/%02d\n",
3743                   lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3744                   lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3745 
3746             pszToken = strtok(NULL, szSpace);
3747             if(!pszToken) continue;
3748             if(!_strnicmp(pszToken, "<DIR>", -1)) {
3749                 lpfp->bIsDirectory = TRUE;
3750                 lpfp->nSize = 0;
3751                 TRACE("Is directory\n");
3752             }
3753             else {
3754                 lpfp->bIsDirectory = FALSE;
3755                 lpfp->nSize = atol(pszToken);
3756                 TRACE("Size: %d\n", lpfp->nSize);
3757             }
3758 
3759             pszToken = strtok(NULL, szSpace);
3760             if(!pszToken) continue;
3761             lpfp->lpszName = heap_strdupAtoW(pszToken);
3762             TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3763         }
3764         /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3765         else if(pszToken[0] == '+') {
3766             FIXME("EPLF Format not implemented\n");
3767         }
3768 
3769         if(lpfp->lpszName) {
3770             if((lpszSearchFile == NULL) ||
3771 	       (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3772                 found = TRUE;
3773                 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3774             }
3775             else {
3776                 heap_free(lpfp->lpszName);
3777                 lpfp->lpszName = NULL;
3778             }
3779         }
3780     } while(!found);
3781     return TRUE;
3782 }
3783 
3784 /***********************************************************************
3785  *           FTP_ParseDirectory (internal)
3786  *
3787  * Parse string of directory information
3788  *
3789  * RETURNS
3790  *   TRUE on success
3791  *   FALSE on failure
3792  */
3793 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3794     LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3795 {
3796     BOOL bSuccess = TRUE;
3797     INT sizeFilePropArray = 500;/*20; */
3798     INT indexFilePropArray = -1;
3799 
3800     TRACE("\n");
3801 
3802     /* Allocate initial file properties array */
3803     *lpafp = heap_alloc_zero(sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3804     if (!*lpafp)
3805         return FALSE;
3806 
3807     do {
3808         if (indexFilePropArray+1 >= sizeFilePropArray)
3809         {
3810             LPFILEPROPERTIESW tmpafp;
3811 
3812             sizeFilePropArray *= 2;
3813             tmpafp = heap_realloc_zero(*lpafp, sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3814             if (NULL == tmpafp)
3815             {
3816                 bSuccess = FALSE;
3817                 break;
3818             }
3819 
3820             *lpafp = tmpafp;
3821         }
3822         indexFilePropArray++;
3823     } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3824 
3825     if (bSuccess && indexFilePropArray)
3826     {
3827         if (indexFilePropArray < sizeFilePropArray - 1)
3828         {
3829             LPFILEPROPERTIESW tmpafp;
3830 
3831             tmpafp = heap_realloc(*lpafp, sizeof(FILEPROPERTIESW)*indexFilePropArray);
3832             if (NULL != tmpafp)
3833                 *lpafp = tmpafp;
3834         }
3835         *dwfp = indexFilePropArray;
3836     }
3837     else
3838     {
3839         heap_free(*lpafp);
3840         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3841         bSuccess = FALSE;
3842     }
3843 
3844     return bSuccess;
3845 }
3846 
3847 
3848 /***********************************************************************
3849  *           FTP_ParsePermission (internal)
3850  *
3851  * Parse permission string of directory information
3852  *
3853  * RETURNS
3854  *   TRUE on success
3855  *   FALSE on failure
3856  *
3857  */
3858 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3859 {
3860     BOOL bSuccess = TRUE;
3861     unsigned short nPermission = 0;
3862     INT nPos = 1;
3863     INT nLast  = 9;
3864 
3865     TRACE("\n");
3866     if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3867     {
3868         bSuccess = FALSE;
3869         return bSuccess;
3870     }
3871 
3872     lpfp->bIsDirectory = (*lpszPermission == 'd');
3873     do
3874     {
3875         switch (nPos)
3876         {
3877             case 1:
3878                 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3879                 break;
3880             case 2:
3881                 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3882                 break;
3883             case 3:
3884                 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3885                 break;
3886             case 4:
3887                 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3888                 break;
3889             case 5:
3890                 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3891                 break;
3892             case 6:
3893                 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3894                 break;
3895             case 7:
3896                 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3897                 break;
3898             case 8:
3899                 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3900                 break;
3901             case 9:
3902                 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3903                 break;
3904         }
3905         nPos++;
3906     }while (nPos <= nLast);
3907 
3908     lpfp->permissions = nPermission;
3909     return bSuccess;
3910 }
3911 
3912 
3913 /***********************************************************************
3914  *           FTP_SetResponseError (internal)
3915  *
3916  * Set the appropriate error code for a given response from the server
3917  *
3918  * RETURNS
3919  *
3920  */
3921 static DWORD FTP_SetResponseError(DWORD dwResponse)
3922 {
3923     DWORD dwCode = 0;
3924 
3925     switch(dwResponse)
3926     {
3927     case 425: /* Cannot open data connection. */
3928         dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3929         break;
3930 
3931     case 426: /* Connection closed, transer aborted. */
3932         dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3933         break;
3934 
3935     case 530: /* Not logged in. Login incorrect. */
3936         dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3937         break;
3938 
3939     case 421: /* Service not available - Server may be shutting down. */
3940     case 450: /* File action not taken. File may be busy. */
3941     case 451: /* Action aborted. Server error. */
3942     case 452: /* Action not taken. Insufficient storage space on server. */
3943     case 500: /* Syntax error. Command unrecognized. */
3944     case 501: /* Syntax error. Error in parameters or arguments. */
3945     case 502: /* Command not implemented. */
3946     case 503: /* Bad sequence of commands. */
3947     case 504: /* Command not implemented for that parameter. */
3948     case 532: /* Need account for storing files */
3949     case 550: /* File action not taken. File not found or no access. */
3950     case 551: /* Requested action aborted. Page type unknown */
3951     case 552: /* Action aborted. Exceeded storage allocation */
3952     case 553: /* Action not taken. File name not allowed. */
3953 
3954     default:
3955         dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3956         break;
3957     }
3958 
3959     INTERNET_SetLastError(dwCode);
3960     return dwCode;
3961 }
3962