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