jscript: Added support for VT_UI4 variants.
[wine.git] / dlls / wininet / ftp.c
blob64c06c3868d06bf81af1093ad78c0d2f3e99d7f3
1 /*
2 * WININET - Ftp implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2004 Mike McCormack for CodeWeavers
6 * Copyright 2004 Kevin Koltzau
7 * Copyright 2007 Hans Leidekker
9 * Ulrich Czekalla
10 * Noureddine Jemmali
12 * Copyright 2000 Andreas Mohr
13 * Copyright 2002 Jaco Greeff
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "config.h"
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
34 #include <ws2tcpip.h>
35 #endif
37 #include <errno.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/types.h>
43 #ifdef HAVE_SYS_SOCKET_H
44 # include <sys/socket.h>
45 #endif
46 #ifdef HAVE_ARPA_INET_H
47 # include <arpa/inet.h>
48 #endif
49 #ifdef HAVE_UNISTD_H
50 # include <unistd.h>
51 #endif
52 #ifdef HAVE_SYS_IOCTL_H
53 # include <sys/ioctl.h>
54 #endif
55 #include <time.h>
56 #include <assert.h>
58 #include "windef.h"
59 #include "winbase.h"
60 #include "wingdi.h"
61 #include "winuser.h"
62 #include "wininet.h"
63 #include "winnls.h"
64 #include "winerror.h"
65 #include "winreg.h"
66 #include "winternl.h"
67 #include "shlwapi.h"
69 #include "wine/debug.h"
70 #include "internet.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
74 typedef struct _ftp_session_t ftp_session_t;
76 typedef struct
78 object_header_t hdr;
79 ftp_session_t *lpFtpSession;
80 BOOL session_deleted;
81 int nDataSocket;
82 WCHAR *cache_file;
83 HANDLE cache_file_handle;
84 } ftp_file_t;
86 struct _ftp_session_t
88 object_header_t hdr;
89 appinfo_t *lpAppInfo;
90 int sndSocket;
91 int lstnSocket;
92 int pasvSocket; /* data socket connected by us in case of passive FTP */
93 ftp_file_t *download_in_progress;
94 struct sockaddr_in socketAddress;
95 struct sockaddr_in lstnSocketAddress;
96 LPWSTR servername;
97 INTERNET_PORT serverport;
98 LPWSTR lpszPassword;
99 LPWSTR lpszUserName;
102 typedef struct
104 BOOL bIsDirectory;
105 LPWSTR lpszName;
106 DWORD nSize;
107 SYSTEMTIME tmLastModified;
108 unsigned short permissions;
109 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
111 typedef struct
113 object_header_t hdr;
114 ftp_session_t *lpFtpSession;
115 DWORD index;
116 DWORD size;
117 LPFILEPROPERTIESW lpafp;
118 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
120 #define DATA_PACKET_SIZE 0x2000
121 #define szCRLF "\r\n"
122 #define MAX_BACKLOG 5
124 /* Testing shows that Windows only accepts dwFlags where the last
125 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
127 #define FTP_CONDITION_MASK 0x0007
129 typedef enum {
130 /* FTP commands with arguments. */
131 FTP_CMD_ACCT,
132 FTP_CMD_CWD,
133 FTP_CMD_DELE,
134 FTP_CMD_MKD,
135 FTP_CMD_PASS,
136 FTP_CMD_PORT,
137 FTP_CMD_RETR,
138 FTP_CMD_RMD,
139 FTP_CMD_RNFR,
140 FTP_CMD_RNTO,
141 FTP_CMD_STOR,
142 FTP_CMD_TYPE,
143 FTP_CMD_USER,
144 FTP_CMD_SIZE,
146 /* FTP commands without arguments. */
147 FTP_CMD_ABOR,
148 FTP_CMD_LIST,
149 FTP_CMD_NLST,
150 FTP_CMD_PASV,
151 FTP_CMD_PWD,
152 FTP_CMD_QUIT,
153 } FTP_COMMAND;
155 static const CHAR *const szFtpCommands[] = {
156 "ACCT",
157 "CWD",
158 "DELE",
159 "MKD",
160 "PASS",
161 "PORT",
162 "RETR",
163 "RMD",
164 "RNFR",
165 "RNTO",
166 "STOR",
167 "TYPE",
168 "USER",
169 "SIZE",
170 "ABOR",
171 "LIST",
172 "NLST",
173 "PASV",
174 "PWD",
175 "QUIT",
178 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
179 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
181 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
182 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext);
183 static BOOL FTP_SendStore(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
184 static BOOL FTP_GetDataSocket(ftp_session_t*, LPINT nDataSocket);
185 static BOOL FTP_SendData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
186 static INT FTP_ReceiveResponse(ftp_session_t*, DWORD_PTR dwContext);
187 static BOOL FTP_SendRetrieve(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
188 static BOOL FTP_RetrieveFileData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
189 static BOOL FTP_InitListenSocket(ftp_session_t*);
190 static BOOL FTP_ConnectToHost(ftp_session_t*);
191 static BOOL FTP_SendPassword(ftp_session_t*);
192 static BOOL FTP_SendAccount(ftp_session_t*);
193 static BOOL FTP_SendType(ftp_session_t*, DWORD dwType);
194 static BOOL FTP_SendPort(ftp_session_t*);
195 static BOOL FTP_DoPassive(ftp_session_t*);
196 static BOOL FTP_SendPortOrPasv(ftp_session_t*);
197 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
198 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
199 static BOOL FTP_ParseDirectory(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
200 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
201 static HINTERNET FTP_ReceiveFileList(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
202 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
203 static DWORD FTP_SetResponseError(DWORD dwResponse);
204 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
205 static BOOL FTP_FtpPutFileW(ftp_session_t*, LPCWSTR lpszLocalFile,
206 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
207 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
208 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
209 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t*,
210 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
211 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t*, LPWSTR lpszCurrentDirectory,
212 LPDWORD lpdwCurrentDirectory);
213 static BOOL FTP_FtpRenameFileW(ftp_session_t*, LPCWSTR lpszSrc, LPCWSTR lpszDest);
214 static BOOL FTP_FtpRemoveDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
215 static BOOL FTP_FtpDeleteFileW(ftp_session_t*, LPCWSTR lpszFileName);
216 static BOOL FTP_FtpGetFileW(ftp_session_t*, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
217 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
218 DWORD_PTR dwContext);
220 /* A temporary helper until we get rid of INTERNET_GetLastError calls */
221 static BOOL res_to_le(DWORD res)
223 if(res != ERROR_SUCCESS)
224 INTERNET_SetLastError(res);
225 return res == ERROR_SUCCESS;
228 /***********************************************************************
229 * FtpPutFileA (WININET.@)
231 * Uploads a file to the FTP server
233 * RETURNS
234 * TRUE on success
235 * FALSE on failure
238 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
239 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
241 LPWSTR lpwzLocalFile;
242 LPWSTR lpwzNewRemoteFile;
243 BOOL ret;
245 lpwzLocalFile = heap_strdupAtoW(lpszLocalFile);
246 lpwzNewRemoteFile = heap_strdupAtoW(lpszNewRemoteFile);
247 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
248 dwFlags, dwContext);
249 heap_free(lpwzLocalFile);
250 heap_free(lpwzNewRemoteFile);
251 return ret;
254 typedef struct {
255 task_header_t hdr;
256 WCHAR *local_file;
257 WCHAR *remote_file;
258 DWORD flags;
259 DWORD_PTR context;
260 } put_file_task_t;
262 static void AsyncFtpPutFileProc(task_header_t *hdr)
264 put_file_task_t *task = (put_file_task_t*)hdr;
265 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
267 TRACE("%p\n", session);
269 FTP_FtpPutFileW(session, task->local_file, task->remote_file,
270 task->flags, task->context);
272 heap_free(task->local_file);
273 heap_free(task->remote_file);
276 /***********************************************************************
277 * FtpPutFileW (WININET.@)
279 * Uploads a file to the FTP server
281 * RETURNS
282 * TRUE on success
283 * FALSE on failure
286 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
287 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
289 ftp_session_t *lpwfs;
290 appinfo_t *hIC = NULL;
291 BOOL r = FALSE;
293 if (!lpszLocalFile || !lpszNewRemoteFile)
295 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
296 return FALSE;
299 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
300 if (!lpwfs)
302 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
303 return FALSE;
306 if (WH_HFTPSESSION != lpwfs->hdr.htype)
308 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
309 goto lend;
312 if (lpwfs->download_in_progress != NULL)
314 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
315 goto lend;
318 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
320 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
321 goto lend;
324 hIC = lpwfs->lpAppInfo;
325 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
327 put_file_task_t *task = alloc_async_task(&lpwfs->hdr, AsyncFtpPutFileProc, sizeof(*task));
329 task->local_file = heap_strdupW(lpszLocalFile);
330 task->remote_file = heap_strdupW(lpszNewRemoteFile);
331 task->flags = dwFlags;
332 task->context = dwContext;
334 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
336 else
338 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
339 lpszNewRemoteFile, dwFlags, dwContext);
342 lend:
343 WININET_Release( &lpwfs->hdr );
345 return r;
348 /***********************************************************************
349 * FTP_FtpPutFileW (Internal)
351 * Uploads a file to the FTP server
353 * RETURNS
354 * TRUE on success
355 * FALSE on failure
358 static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
359 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
361 HANDLE hFile;
362 BOOL bSuccess = FALSE;
363 appinfo_t *hIC = NULL;
364 INT nResCode;
366 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
368 /* Clear any error information */
369 INTERNET_SetLastError(0);
371 /* Open file to be uploaded */
372 if (INVALID_HANDLE_VALUE ==
373 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
374 /* Let CreateFile set the appropriate error */
375 return FALSE;
377 hIC = lpwfs->lpAppInfo;
379 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
381 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
383 INT nDataSocket;
385 /* Get data socket to server */
386 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
388 FTP_SendData(lpwfs, nDataSocket, hFile);
389 closesocket(nDataSocket);
390 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
391 if (nResCode)
393 if (nResCode == 226)
394 bSuccess = TRUE;
395 else
396 FTP_SetResponseError(nResCode);
401 if (lpwfs->lstnSocket != -1)
403 closesocket(lpwfs->lstnSocket);
404 lpwfs->lstnSocket = -1;
407 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
409 INTERNET_ASYNC_RESULT iar;
411 iar.dwResult = (DWORD)bSuccess;
412 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
413 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
414 &iar, sizeof(INTERNET_ASYNC_RESULT));
417 CloseHandle(hFile);
419 return bSuccess;
423 /***********************************************************************
424 * FtpSetCurrentDirectoryA (WININET.@)
426 * Change the working directory on the FTP server
428 * RETURNS
429 * TRUE on success
430 * FALSE on failure
433 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
435 LPWSTR lpwzDirectory;
436 BOOL ret;
438 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
439 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
440 heap_free(lpwzDirectory);
441 return ret;
444 typedef struct {
445 task_header_t hdr;
446 WCHAR *directory;
447 } directory_task_t;
449 static void AsyncFtpSetCurrentDirectoryProc(task_header_t *hdr)
451 directory_task_t *task = (directory_task_t*)hdr;
452 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
454 TRACE("%p\n", session);
456 FTP_FtpSetCurrentDirectoryW(session, task->directory);
457 heap_free(task->directory);
460 /***********************************************************************
461 * FtpSetCurrentDirectoryW (WININET.@)
463 * Change the working directory on the FTP server
465 * RETURNS
466 * TRUE on success
467 * FALSE on failure
470 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
472 ftp_session_t *lpwfs = NULL;
473 appinfo_t *hIC = NULL;
474 BOOL r = FALSE;
476 if (!lpszDirectory)
478 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
479 goto lend;
482 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
483 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
485 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
486 goto lend;
489 if (lpwfs->download_in_progress != NULL)
491 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
492 goto lend;
495 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
497 hIC = lpwfs->lpAppInfo;
498 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
500 directory_task_t *task;
502 task = alloc_async_task(&lpwfs->hdr, AsyncFtpSetCurrentDirectoryProc, sizeof(*task));
503 task->directory = heap_strdupW(lpszDirectory);
505 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
507 else
509 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
512 lend:
513 if( lpwfs )
514 WININET_Release( &lpwfs->hdr );
516 return r;
520 /***********************************************************************
521 * FTP_FtpSetCurrentDirectoryW (Internal)
523 * Change the working directory on the FTP server
525 * RETURNS
526 * TRUE on success
527 * FALSE on failure
530 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
532 INT nResCode;
533 appinfo_t *hIC = NULL;
534 BOOL bSuccess = FALSE;
536 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
538 /* Clear any error information */
539 INTERNET_SetLastError(0);
541 hIC = lpwfs->lpAppInfo;
542 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
543 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
544 goto lend;
546 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
548 if (nResCode)
550 if (nResCode == 250)
551 bSuccess = TRUE;
552 else
553 FTP_SetResponseError(nResCode);
556 lend:
557 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
559 INTERNET_ASYNC_RESULT iar;
561 iar.dwResult = bSuccess;
562 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
563 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
564 &iar, sizeof(INTERNET_ASYNC_RESULT));
566 return bSuccess;
570 /***********************************************************************
571 * FtpCreateDirectoryA (WININET.@)
573 * Create new directory on the FTP server
575 * RETURNS
576 * TRUE on success
577 * FALSE on failure
580 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
582 LPWSTR lpwzDirectory;
583 BOOL ret;
585 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
586 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
587 heap_free(lpwzDirectory);
588 return ret;
592 static void AsyncFtpCreateDirectoryProc(task_header_t *hdr)
594 directory_task_t *task = (directory_task_t*)hdr;
595 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
597 TRACE(" %p\n", session);
599 FTP_FtpCreateDirectoryW(session, task->directory);
600 heap_free(task->directory);
603 /***********************************************************************
604 * FtpCreateDirectoryW (WININET.@)
606 * Create new directory on the FTP server
608 * RETURNS
609 * TRUE on success
610 * FALSE on failure
613 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
615 ftp_session_t *lpwfs;
616 appinfo_t *hIC = NULL;
617 BOOL r = FALSE;
619 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
620 if (!lpwfs)
622 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
623 return FALSE;
626 if (WH_HFTPSESSION != lpwfs->hdr.htype)
628 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
629 goto lend;
632 if (lpwfs->download_in_progress != NULL)
634 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
635 goto lend;
638 if (!lpszDirectory)
640 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
641 goto lend;
644 hIC = lpwfs->lpAppInfo;
645 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
647 directory_task_t *task;
649 task = alloc_async_task(&lpwfs->hdr, AsyncFtpCreateDirectoryProc, sizeof(*task));
650 task->directory = heap_strdupW(lpszDirectory);
652 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
654 else
656 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
658 lend:
659 WININET_Release( &lpwfs->hdr );
661 return r;
665 /***********************************************************************
666 * FTP_FtpCreateDirectoryW (Internal)
668 * Create new directory on the FTP server
670 * RETURNS
671 * TRUE on success
672 * FALSE on failure
675 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
677 INT nResCode;
678 BOOL bSuccess = FALSE;
679 appinfo_t *hIC = NULL;
681 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
683 /* Clear any error information */
684 INTERNET_SetLastError(0);
686 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
687 goto lend;
689 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
690 if (nResCode)
692 if (nResCode == 257)
693 bSuccess = TRUE;
694 else
695 FTP_SetResponseError(nResCode);
698 lend:
699 hIC = lpwfs->lpAppInfo;
700 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
702 INTERNET_ASYNC_RESULT iar;
704 iar.dwResult = (DWORD)bSuccess;
705 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
706 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
707 &iar, sizeof(INTERNET_ASYNC_RESULT));
710 return bSuccess;
713 /***********************************************************************
714 * FtpFindFirstFileA (WININET.@)
716 * Search the specified directory
718 * RETURNS
719 * HINTERNET on success
720 * NULL on failure
723 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
724 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
726 LPWSTR lpwzSearchFile;
727 WIN32_FIND_DATAW wfd;
728 LPWIN32_FIND_DATAW lpFindFileDataW;
729 HINTERNET ret;
731 lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
732 lpFindFileDataW = lpFindFileData?&wfd:NULL;
733 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
734 heap_free(lpwzSearchFile);
736 if (ret && lpFindFileData)
737 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
739 return ret;
742 typedef struct {
743 task_header_t hdr;
744 WCHAR *search_file;
745 WIN32_FIND_DATAW *find_file_data;
746 DWORD flags;
747 DWORD_PTR context;
748 } find_first_file_task_t;
750 static void AsyncFtpFindFirstFileProc(task_header_t *hdr)
752 find_first_file_task_t *task = (find_first_file_task_t*)hdr;
753 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
755 TRACE("%p\n", session);
757 FTP_FtpFindFirstFileW(session, task->search_file, task->find_file_data, task->flags, task->context);
758 heap_free(task->search_file);
761 /***********************************************************************
762 * FtpFindFirstFileW (WININET.@)
764 * Search the specified directory
766 * RETURNS
767 * HINTERNET on success
768 * NULL on failure
771 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
772 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
774 ftp_session_t *lpwfs;
775 appinfo_t *hIC = NULL;
776 HINTERNET r = NULL;
778 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
779 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
781 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
782 goto lend;
785 if (lpwfs->download_in_progress != NULL)
787 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
788 goto lend;
791 hIC = lpwfs->lpAppInfo;
792 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
794 find_first_file_task_t *task;
796 task = alloc_async_task(&lpwfs->hdr, AsyncFtpFindFirstFileProc, sizeof(*task));
797 task->search_file = heap_strdupW(lpszSearchFile);
798 task->find_file_data = lpFindFileData;
799 task->flags = dwFlags;
800 task->context = dwContext;
802 INTERNET_AsyncCall(&task->hdr);
803 r = NULL;
805 else
807 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
808 dwFlags, dwContext);
810 lend:
811 if( lpwfs )
812 WININET_Release( &lpwfs->hdr );
814 return r;
818 /***********************************************************************
819 * FTP_FtpFindFirstFileW (Internal)
821 * Search the specified directory
823 * RETURNS
824 * HINTERNET on success
825 * NULL on failure
828 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
829 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
831 INT nResCode;
832 appinfo_t *hIC = NULL;
833 HINTERNET hFindNext = NULL;
834 LPWSTR lpszSearchPath = NULL;
836 TRACE("\n");
838 /* Clear any error information */
839 INTERNET_SetLastError(0);
841 if (!FTP_InitListenSocket(lpwfs))
842 goto lend;
844 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
845 goto lend;
847 if (!FTP_SendPortOrPasv(lpwfs))
848 goto lend;
850 /* split search path into file and path */
851 if (lpszSearchFile)
853 LPCWSTR name = lpszSearchFile, p;
854 if ((p = strrchrW( name, '\\' ))) name = p + 1;
855 if ((p = strrchrW( name, '/' ))) name = p + 1;
856 if (name != lpszSearchFile)
858 lpszSearchPath = heap_strndupW(lpszSearchFile, name - lpszSearchFile);
859 lpszSearchFile = name;
863 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, lpszSearchPath,
864 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
865 goto lend;
867 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
868 if (nResCode)
870 if (nResCode == 125 || nResCode == 150)
872 INT nDataSocket;
874 /* Get data socket to server */
875 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
877 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
878 closesocket(nDataSocket);
879 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
880 if (nResCode != 226 && nResCode != 250)
881 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
884 else
885 FTP_SetResponseError(nResCode);
888 lend:
889 heap_free(lpszSearchPath);
891 if (lpwfs->lstnSocket != -1)
893 closesocket(lpwfs->lstnSocket);
894 lpwfs->lstnSocket = -1;
897 hIC = lpwfs->lpAppInfo;
898 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
900 INTERNET_ASYNC_RESULT iar;
902 if (hFindNext)
904 iar.dwResult = (DWORD_PTR)hFindNext;
905 iar.dwError = ERROR_SUCCESS;
906 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
907 &iar, sizeof(INTERNET_ASYNC_RESULT));
910 iar.dwResult = (DWORD_PTR)hFindNext;
911 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
912 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
913 &iar, sizeof(INTERNET_ASYNC_RESULT));
916 return hFindNext;
920 /***********************************************************************
921 * FtpGetCurrentDirectoryA (WININET.@)
923 * Retrieves the current directory
925 * RETURNS
926 * TRUE on success
927 * FALSE on failure
930 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
931 LPDWORD lpdwCurrentDirectory)
933 WCHAR *dir = NULL;
934 DWORD len;
935 BOOL ret;
937 if(lpdwCurrentDirectory) {
938 len = *lpdwCurrentDirectory;
939 if(lpszCurrentDirectory)
941 dir = heap_alloc(len * sizeof(WCHAR));
942 if (NULL == dir)
944 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
945 return FALSE;
949 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
951 if (ret && lpszCurrentDirectory)
952 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
954 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
955 heap_free(dir);
956 return ret;
959 typedef struct {
960 task_header_t hdr;
961 WCHAR *directory;
962 DWORD *directory_len;
963 } get_current_dir_task_t;
965 static void AsyncFtpGetCurrentDirectoryProc(task_header_t *hdr)
967 get_current_dir_task_t *task = (get_current_dir_task_t*)hdr;
968 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
970 TRACE("%p\n", session);
972 FTP_FtpGetCurrentDirectoryW(session, task->directory, task->directory_len);
975 /***********************************************************************
976 * FtpGetCurrentDirectoryW (WININET.@)
978 * Retrieves the current directory
980 * RETURNS
981 * TRUE on success
982 * FALSE on failure
985 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
986 LPDWORD lpdwCurrentDirectory)
988 ftp_session_t *lpwfs;
989 appinfo_t *hIC = NULL;
990 BOOL r = FALSE;
992 TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
994 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
995 if (NULL == lpwfs)
997 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
998 goto lend;
1001 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1003 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1004 goto lend;
1007 if (!lpdwCurrentDirectory)
1009 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1010 goto lend;
1013 if (lpszCurrentDirectory == NULL)
1015 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1016 goto lend;
1019 if (lpwfs->download_in_progress != NULL)
1021 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1022 goto lend;
1025 hIC = lpwfs->lpAppInfo;
1026 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1028 get_current_dir_task_t *task;
1030 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetCurrentDirectoryProc, sizeof(*task));
1031 task->directory = lpszCurrentDirectory;
1032 task->directory_len = lpdwCurrentDirectory;
1034 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1036 else
1038 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
1039 lpdwCurrentDirectory);
1042 lend:
1043 if( lpwfs )
1044 WININET_Release( &lpwfs->hdr );
1046 return r;
1050 /***********************************************************************
1051 * FTP_FtpGetCurrentDirectoryW (Internal)
1053 * Retrieves the current directory
1055 * RETURNS
1056 * TRUE on success
1057 * FALSE on failure
1060 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
1061 LPDWORD lpdwCurrentDirectory)
1063 INT nResCode;
1064 appinfo_t *hIC = NULL;
1065 BOOL bSuccess = FALSE;
1067 /* Clear any error information */
1068 INTERNET_SetLastError(0);
1070 hIC = lpwfs->lpAppInfo;
1071 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1072 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1073 goto lend;
1075 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1076 if (nResCode)
1078 if (nResCode == 257) /* Extract directory name */
1080 DWORD firstpos, lastpos, len;
1081 LPWSTR lpszResponseBuffer = heap_strdupAtoW(INTERNET_GetResponseBuffer());
1083 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1085 if ('"' == lpszResponseBuffer[lastpos])
1087 if (!firstpos)
1088 firstpos = lastpos;
1089 else
1090 break;
1093 len = lastpos - firstpos;
1094 if (*lpdwCurrentDirectory >= len)
1096 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1097 lpszCurrentDirectory[len - 1] = 0;
1098 *lpdwCurrentDirectory = len;
1099 bSuccess = TRUE;
1101 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1103 heap_free(lpszResponseBuffer);
1105 else
1106 FTP_SetResponseError(nResCode);
1109 lend:
1110 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1112 INTERNET_ASYNC_RESULT iar;
1114 iar.dwResult = bSuccess;
1115 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1116 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1117 &iar, sizeof(INTERNET_ASYNC_RESULT));
1120 return bSuccess;
1124 /***********************************************************************
1125 * FTPFILE_Destroy(internal)
1127 * Closes the file transfer handle. This also 'cleans' the data queue of
1128 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1131 static void FTPFILE_Destroy(object_header_t *hdr)
1133 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1134 ftp_session_t *lpwfs = lpwh->lpFtpSession;
1135 INT nResCode;
1137 TRACE("\n");
1139 if (lpwh->cache_file_handle != INVALID_HANDLE_VALUE)
1140 CloseHandle(lpwh->cache_file_handle);
1142 heap_free(lpwh->cache_file);
1144 if (!lpwh->session_deleted)
1145 lpwfs->download_in_progress = NULL;
1147 if (lpwh->nDataSocket != -1)
1148 closesocket(lpwh->nDataSocket);
1150 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1151 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1153 WININET_Release(&lpwh->lpFtpSession->hdr);
1156 static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1158 switch(option) {
1159 case INTERNET_OPTION_HANDLE_TYPE:
1160 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1162 if (*size < sizeof(ULONG))
1163 return ERROR_INSUFFICIENT_BUFFER;
1165 *size = sizeof(DWORD);
1166 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1167 return ERROR_SUCCESS;
1168 case INTERNET_OPTION_DATAFILE_NAME:
1170 DWORD required;
1171 ftp_file_t *file = (ftp_file_t *)hdr;
1173 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1175 if (!file->cache_file)
1177 *size = 0;
1178 return ERROR_INTERNET_ITEM_NOT_FOUND;
1180 if (unicode)
1182 required = (lstrlenW(file->cache_file) + 1) * sizeof(WCHAR);
1183 if (*size < required)
1184 return ERROR_INSUFFICIENT_BUFFER;
1186 *size = required;
1187 memcpy(buffer, file->cache_file, *size);
1188 return ERROR_SUCCESS;
1190 else
1192 required = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, NULL, 0, NULL, NULL);
1193 if (required > *size)
1194 return ERROR_INSUFFICIENT_BUFFER;
1196 *size = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, buffer, *size, NULL, NULL);
1197 return ERROR_SUCCESS;
1201 return INET_QueryOption(hdr, option, buffer, size, unicode);
1204 static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
1206 ftp_file_t *file = (ftp_file_t*)hdr;
1207 int res;
1208 DWORD error;
1210 if (file->nDataSocket == -1)
1211 return ERROR_INTERNET_DISCONNECTED;
1213 /* FIXME: FTP should use NETCON_ stuff */
1214 res = sock_recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1215 *read = res>0 ? res : 0;
1217 error = res >= 0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME */
1218 if (error == ERROR_SUCCESS && file->cache_file)
1220 DWORD bytes_written;
1222 if (!WriteFile(file->cache_file_handle, buffer, *read, &bytes_written, NULL))
1223 WARN("WriteFile failed: %u\n", GetLastError());
1225 return error;
1228 static DWORD FTPFILE_ReadFileEx(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_size,
1229 DWORD flags, DWORD_PTR context)
1231 return FTPFILE_ReadFile(hdr, buf, size, ret_size);
1234 static DWORD FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1236 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1237 int res;
1239 res = sock_send(lpwh->nDataSocket, buffer, size, 0);
1241 *written = res>0 ? res : 0;
1242 return res >= 0 ? ERROR_SUCCESS : sock_get_error(errno);
1245 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
1247 INTERNET_ASYNC_RESULT iar;
1248 BYTE buffer[4096];
1249 int available;
1251 TRACE("%p\n", file);
1253 available = sock_recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1255 if(available != -1) {
1256 iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1257 iar.dwError = first_notif ? 0 : available;
1258 }else {
1259 iar.dwResult = 0;
1260 iar.dwError = INTERNET_GetLastError();
1263 INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1264 sizeof(INTERNET_ASYNC_RESULT));
1267 static void FTPFILE_AsyncQueryDataAvailableProc(task_header_t *task)
1269 ftp_file_t *file = (ftp_file_t*)task->hdr;
1271 FTP_ReceiveRequestData(file, FALSE);
1274 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1276 ftp_file_t *file = (ftp_file_t*) hdr;
1277 int retval, unread = 0;
1279 TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
1281 #ifdef FIONREAD
1282 retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
1283 if (!retval)
1284 TRACE("%d bytes of queued, but unread data\n", unread);
1285 #else
1286 FIXME("FIONREAD not available\n");
1287 #endif
1289 *available = unread;
1291 if(!unread) {
1292 BYTE byte;
1294 *available = 0;
1296 retval = sock_recv(file->nDataSocket, &byte, 1, MSG_PEEK);
1297 if(retval > 0) {
1298 task_header_t *task;
1300 task = alloc_async_task(&file->hdr, FTPFILE_AsyncQueryDataAvailableProc, sizeof(*task));
1301 INTERNET_AsyncCall(task);
1303 return ERROR_IO_PENDING;
1307 return ERROR_SUCCESS;
1310 static DWORD FTPFILE_LockRequestFile(object_header_t *hdr, req_file_t **ret)
1312 ftp_file_t *file = (ftp_file_t*)hdr;
1313 FIXME("%p\n", file);
1314 return ERROR_NOT_SUPPORTED;
1317 static const object_vtbl_t FTPFILEVtbl = {
1318 FTPFILE_Destroy,
1319 NULL,
1320 FTPFILE_QueryOption,
1321 INET_SetOption,
1322 FTPFILE_ReadFile,
1323 FTPFILE_ReadFileEx,
1324 FTPFILE_WriteFile,
1325 FTPFILE_QueryDataAvailable,
1326 NULL,
1327 FTPFILE_LockRequestFile
1330 /***********************************************************************
1331 * FTP_FtpOpenFileW (Internal)
1333 * Open a remote file for writing or reading
1335 * RETURNS
1336 * HINTERNET handle on success
1337 * NULL on failure
1340 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1341 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1342 DWORD_PTR dwContext)
1344 INT nDataSocket;
1345 BOOL bSuccess = FALSE;
1346 ftp_file_t *lpwh = NULL;
1347 appinfo_t *hIC = NULL;
1349 TRACE("\n");
1351 /* Clear any error information */
1352 INTERNET_SetLastError(0);
1354 if (GENERIC_READ == fdwAccess)
1356 /* Set up socket to retrieve data */
1357 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1359 else if (GENERIC_WRITE == fdwAccess)
1361 /* Set up socket to send data */
1362 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1365 /* Get data socket to server */
1366 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1368 lpwh = alloc_object(&lpwfs->hdr, &FTPFILEVtbl, sizeof(ftp_file_t));
1369 lpwh->hdr.htype = WH_HFILE;
1370 lpwh->hdr.dwFlags = dwFlags;
1371 lpwh->hdr.dwContext = dwContext;
1372 lpwh->nDataSocket = nDataSocket;
1373 lpwh->cache_file = NULL;
1374 lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1375 lpwh->session_deleted = FALSE;
1377 WININET_AddRef( &lpwfs->hdr );
1378 lpwh->lpFtpSession = lpwfs;
1379 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1381 /* Indicate that a download is currently in progress */
1382 lpwfs->download_in_progress = lpwh;
1385 if (lpwfs->lstnSocket != -1)
1387 closesocket(lpwfs->lstnSocket);
1388 lpwfs->lstnSocket = -1;
1391 if (bSuccess && fdwAccess == GENERIC_READ)
1393 WCHAR filename[MAX_PATH + 1];
1394 URL_COMPONENTSW uc;
1395 DWORD len;
1397 memset(&uc, 0, sizeof(uc));
1398 uc.dwStructSize = sizeof(uc);
1399 uc.nScheme = INTERNET_SCHEME_FTP;
1400 uc.lpszHostName = lpwfs->servername;
1401 uc.nPort = lpwfs->serverport;
1402 uc.lpszUserName = lpwfs->lpszUserName;
1403 uc.lpszUrlPath = heap_strdupW(lpszFileName);
1405 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1407 WCHAR *url = heap_alloc(len * sizeof(WCHAR));
1409 if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1411 lpwh->cache_file = heap_strdupW(filename);
1412 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1413 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1414 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1416 WARN("Could not create cache file: %u\n", GetLastError());
1417 heap_free(lpwh->cache_file);
1418 lpwh->cache_file = NULL;
1421 heap_free(url);
1423 heap_free(uc.lpszUrlPath);
1426 hIC = lpwfs->lpAppInfo;
1427 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1429 INTERNET_ASYNC_RESULT iar;
1431 if (lpwh)
1433 iar.dwResult = (DWORD_PTR)lpwh->hdr.hInternet;
1434 iar.dwError = ERROR_SUCCESS;
1435 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1436 &iar, sizeof(INTERNET_ASYNC_RESULT));
1439 if(bSuccess) {
1440 FTP_ReceiveRequestData(lpwh, TRUE);
1441 }else {
1442 iar.dwResult = 0;
1443 iar.dwError = INTERNET_GetLastError();
1444 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1445 &iar, sizeof(INTERNET_ASYNC_RESULT));
1449 if(!bSuccess)
1450 return FALSE;
1452 return lpwh->hdr.hInternet;
1456 /***********************************************************************
1457 * FtpOpenFileA (WININET.@)
1459 * Open a remote file for writing or reading
1461 * RETURNS
1462 * HINTERNET handle on success
1463 * NULL on failure
1466 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1467 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1468 DWORD_PTR dwContext)
1470 LPWSTR lpwzFileName;
1471 HINTERNET ret;
1473 lpwzFileName = heap_strdupAtoW(lpszFileName);
1474 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1475 heap_free(lpwzFileName);
1476 return ret;
1479 typedef struct {
1480 task_header_t hdr;
1481 WCHAR *file_name;
1482 DWORD access;
1483 DWORD flags;
1484 DWORD_PTR context;
1485 } open_file_task_t;
1487 static void AsyncFtpOpenFileProc(task_header_t *hdr)
1489 open_file_task_t *task = (open_file_task_t*)hdr;
1490 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1492 TRACE("%p\n", session);
1494 FTP_FtpOpenFileW(session, task->file_name, task->access, task->flags, task->context);
1495 heap_free(task->file_name);
1498 /***********************************************************************
1499 * FtpOpenFileW (WININET.@)
1501 * Open a remote file for writing or reading
1503 * RETURNS
1504 * HINTERNET handle on success
1505 * NULL on failure
1508 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1509 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1510 DWORD_PTR dwContext)
1512 ftp_session_t *lpwfs;
1513 appinfo_t *hIC = NULL;
1514 HINTERNET r = NULL;
1516 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1517 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1519 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1520 if (!lpwfs)
1522 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1523 return FALSE;
1526 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1528 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1529 goto lend;
1532 if ((!lpszFileName) ||
1533 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1534 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1536 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1537 goto lend;
1540 if (lpwfs->download_in_progress != NULL)
1542 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1543 goto lend;
1546 hIC = lpwfs->lpAppInfo;
1547 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1549 open_file_task_t *task;
1551 task = alloc_async_task(&lpwfs->hdr, AsyncFtpOpenFileProc, sizeof(*task));
1552 task->file_name = heap_strdupW(lpszFileName);
1553 task->access = fdwAccess;
1554 task->flags = dwFlags;
1555 task->context = dwContext;
1557 INTERNET_AsyncCall(&task->hdr);
1558 r = NULL;
1560 else
1562 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1565 lend:
1566 WININET_Release( &lpwfs->hdr );
1568 return r;
1572 /***********************************************************************
1573 * FtpGetFileA (WININET.@)
1575 * Retrieve file from the FTP server
1577 * RETURNS
1578 * TRUE on success
1579 * FALSE on failure
1582 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1583 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1584 DWORD_PTR dwContext)
1586 LPWSTR lpwzRemoteFile;
1587 LPWSTR lpwzNewFile;
1588 BOOL ret;
1590 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1591 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1592 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1593 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1594 heap_free(lpwzRemoteFile);
1595 heap_free(lpwzNewFile);
1596 return ret;
1599 typedef struct {
1600 task_header_t hdr;
1601 WCHAR *remote_file;
1602 WCHAR *new_file;
1603 BOOL fail_if_exists;
1604 DWORD local_attr;
1605 DWORD flags;
1606 DWORD_PTR context;
1607 } get_file_task_t;
1609 static void AsyncFtpGetFileProc(task_header_t *hdr)
1611 get_file_task_t *task = (get_file_task_t*)hdr;
1612 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1614 TRACE("%p\n", session);
1616 FTP_FtpGetFileW(session, task->remote_file, task->new_file, task->fail_if_exists,
1617 task->local_attr, task->flags, task->context);
1618 heap_free(task->remote_file);
1619 heap_free(task->new_file);
1623 /***********************************************************************
1624 * FtpGetFileW (WININET.@)
1626 * Retrieve file from the FTP server
1628 * RETURNS
1629 * TRUE on success
1630 * FALSE on failure
1633 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1634 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1635 DWORD_PTR dwContext)
1637 ftp_session_t *lpwfs;
1638 appinfo_t *hIC = NULL;
1639 BOOL r = FALSE;
1641 if (!lpszRemoteFile || !lpszNewFile)
1643 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1644 return FALSE;
1647 lpwfs = (ftp_session_t*) get_handle_object( hInternet );
1648 if (!lpwfs)
1650 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1651 return FALSE;
1654 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1656 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1657 goto lend;
1660 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1662 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1663 goto lend;
1666 if (lpwfs->download_in_progress != NULL)
1668 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1669 goto lend;
1672 hIC = lpwfs->lpAppInfo;
1673 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1675 get_file_task_t *task;
1677 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetFileProc, sizeof(*task));
1678 task->remote_file = heap_strdupW(lpszRemoteFile);
1679 task->new_file = heap_strdupW(lpszNewFile);
1680 task->local_attr = dwLocalFlagsAttribute;
1681 task->fail_if_exists = fFailIfExists;
1682 task->flags = dwInternetFlags;
1683 task->context = dwContext;
1685 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1687 else
1689 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1690 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1693 lend:
1694 WININET_Release( &lpwfs->hdr );
1696 return r;
1700 /***********************************************************************
1701 * FTP_FtpGetFileW (Internal)
1703 * Retrieve file from the FTP server
1705 * RETURNS
1706 * TRUE on success
1707 * FALSE on failure
1710 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1711 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1712 DWORD_PTR dwContext)
1714 BOOL bSuccess = FALSE;
1715 HANDLE hFile;
1716 appinfo_t *hIC = NULL;
1718 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1720 /* Clear any error information */
1721 INTERNET_SetLastError(0);
1723 /* Ensure we can write to lpszNewfile by opening it */
1724 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1725 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1726 if (INVALID_HANDLE_VALUE == hFile)
1727 return FALSE;
1729 /* Set up socket to retrieve data */
1730 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1732 INT nDataSocket;
1734 /* Get data socket to server */
1735 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1737 INT nResCode;
1739 /* Receive data */
1740 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1741 closesocket(nDataSocket);
1743 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1744 if (nResCode)
1746 if (nResCode == 226)
1747 bSuccess = TRUE;
1748 else
1749 FTP_SetResponseError(nResCode);
1754 if (lpwfs->lstnSocket != -1)
1756 closesocket(lpwfs->lstnSocket);
1757 lpwfs->lstnSocket = -1;
1760 CloseHandle(hFile);
1762 hIC = lpwfs->lpAppInfo;
1763 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1765 INTERNET_ASYNC_RESULT iar;
1767 iar.dwResult = (DWORD)bSuccess;
1768 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1769 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1770 &iar, sizeof(INTERNET_ASYNC_RESULT));
1773 if (!bSuccess) DeleteFileW(lpszNewFile);
1774 return bSuccess;
1777 /***********************************************************************
1778 * FtpGetFileSize (WININET.@)
1780 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1782 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1784 if (lpdwFileSizeHigh)
1785 *lpdwFileSizeHigh = 0;
1787 return 0;
1790 /***********************************************************************
1791 * FtpDeleteFileA (WININET.@)
1793 * Delete a file on the ftp server
1795 * RETURNS
1796 * TRUE on success
1797 * FALSE on failure
1800 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1802 LPWSTR lpwzFileName;
1803 BOOL ret;
1805 lpwzFileName = heap_strdupAtoW(lpszFileName);
1806 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1807 heap_free(lpwzFileName);
1808 return ret;
1811 typedef struct {
1812 task_header_t hdr;
1813 WCHAR *file_name;
1814 } delete_file_task_t;
1816 static void AsyncFtpDeleteFileProc(task_header_t *hdr)
1818 delete_file_task_t *task = (delete_file_task_t*)hdr;
1819 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1821 TRACE("%p\n", session);
1823 FTP_FtpDeleteFileW(session, task->file_name);
1824 heap_free(task->file_name);
1827 /***********************************************************************
1828 * FtpDeleteFileW (WININET.@)
1830 * Delete a file on the ftp server
1832 * RETURNS
1833 * TRUE on success
1834 * FALSE on failure
1837 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1839 ftp_session_t *lpwfs;
1840 appinfo_t *hIC = NULL;
1841 BOOL r = FALSE;
1843 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1844 if (!lpwfs)
1846 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1847 return FALSE;
1850 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1852 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1853 goto lend;
1856 if (lpwfs->download_in_progress != NULL)
1858 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1859 goto lend;
1862 if (!lpszFileName)
1864 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1865 goto lend;
1868 hIC = lpwfs->lpAppInfo;
1869 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1871 delete_file_task_t *task;
1873 task = alloc_async_task(&lpwfs->hdr, AsyncFtpDeleteFileProc, sizeof(*task));
1874 task->file_name = heap_strdupW(lpszFileName);
1876 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1878 else
1880 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1883 lend:
1884 WININET_Release( &lpwfs->hdr );
1886 return r;
1889 /***********************************************************************
1890 * FTP_FtpDeleteFileW (Internal)
1892 * Delete a file on the ftp server
1894 * RETURNS
1895 * TRUE on success
1896 * FALSE on failure
1899 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1901 INT nResCode;
1902 BOOL bSuccess = FALSE;
1903 appinfo_t *hIC = NULL;
1905 TRACE("%p\n", lpwfs);
1907 /* Clear any error information */
1908 INTERNET_SetLastError(0);
1910 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1911 goto lend;
1913 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1914 if (nResCode)
1916 if (nResCode == 250)
1917 bSuccess = TRUE;
1918 else
1919 FTP_SetResponseError(nResCode);
1921 lend:
1922 hIC = lpwfs->lpAppInfo;
1923 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1925 INTERNET_ASYNC_RESULT iar;
1927 iar.dwResult = (DWORD)bSuccess;
1928 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1929 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1930 &iar, sizeof(INTERNET_ASYNC_RESULT));
1933 return bSuccess;
1937 /***********************************************************************
1938 * FtpRemoveDirectoryA (WININET.@)
1940 * Remove a directory on the ftp server
1942 * RETURNS
1943 * TRUE on success
1944 * FALSE on failure
1947 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1949 LPWSTR lpwzDirectory;
1950 BOOL ret;
1952 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1953 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1954 heap_free(lpwzDirectory);
1955 return ret;
1958 static void AsyncFtpRemoveDirectoryProc(task_header_t *hdr)
1960 directory_task_t *task = (directory_task_t*)hdr;
1961 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1963 TRACE("%p\n", session);
1965 FTP_FtpRemoveDirectoryW(session, task->directory);
1966 heap_free(task->directory);
1969 /***********************************************************************
1970 * FtpRemoveDirectoryW (WININET.@)
1972 * Remove a directory on the ftp server
1974 * RETURNS
1975 * TRUE on success
1976 * FALSE on failure
1979 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1981 ftp_session_t *lpwfs;
1982 appinfo_t *hIC = NULL;
1983 BOOL r = FALSE;
1985 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1986 if (!lpwfs)
1988 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1989 return FALSE;
1992 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1994 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1995 goto lend;
1998 if (lpwfs->download_in_progress != NULL)
2000 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2001 goto lend;
2004 if (!lpszDirectory)
2006 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2007 goto lend;
2010 hIC = lpwfs->lpAppInfo;
2011 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2013 directory_task_t *task;
2015 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRemoveDirectoryProc, sizeof(*task));
2016 task->directory = heap_strdupW(lpszDirectory);
2018 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
2020 else
2022 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
2025 lend:
2026 WININET_Release( &lpwfs->hdr );
2028 return r;
2031 /***********************************************************************
2032 * FTP_FtpRemoveDirectoryW (Internal)
2034 * Remove a directory on the ftp server
2036 * RETURNS
2037 * TRUE on success
2038 * FALSE on failure
2041 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
2043 INT nResCode;
2044 BOOL bSuccess = FALSE;
2045 appinfo_t *hIC = NULL;
2047 TRACE("\n");
2049 /* Clear any error information */
2050 INTERNET_SetLastError(0);
2052 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2053 goto lend;
2055 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2056 if (nResCode)
2058 if (nResCode == 250)
2059 bSuccess = TRUE;
2060 else
2061 FTP_SetResponseError(nResCode);
2064 lend:
2065 hIC = lpwfs->lpAppInfo;
2066 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2068 INTERNET_ASYNC_RESULT iar;
2070 iar.dwResult = (DWORD)bSuccess;
2071 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2072 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2073 &iar, sizeof(INTERNET_ASYNC_RESULT));
2076 return bSuccess;
2080 /***********************************************************************
2081 * FtpRenameFileA (WININET.@)
2083 * Rename a file on the ftp server
2085 * RETURNS
2086 * TRUE on success
2087 * FALSE on failure
2090 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2092 LPWSTR lpwzSrc;
2093 LPWSTR lpwzDest;
2094 BOOL ret;
2096 lpwzSrc = heap_strdupAtoW(lpszSrc);
2097 lpwzDest = heap_strdupAtoW(lpszDest);
2098 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2099 heap_free(lpwzSrc);
2100 heap_free(lpwzDest);
2101 return ret;
2104 typedef struct {
2105 task_header_t hdr;
2106 WCHAR *src_file;
2107 WCHAR *dst_file;
2108 } rename_file_task_t;
2110 static void AsyncFtpRenameFileProc(task_header_t *hdr)
2112 rename_file_task_t *task = (rename_file_task_t*)hdr;
2113 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
2115 TRACE("%p\n", session);
2117 FTP_FtpRenameFileW(session, task->src_file, task->dst_file);
2118 heap_free(task->src_file);
2119 heap_free(task->dst_file);
2122 /***********************************************************************
2123 * FtpRenameFileW (WININET.@)
2125 * Rename a file on the ftp server
2127 * RETURNS
2128 * TRUE on success
2129 * FALSE on failure
2132 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2134 ftp_session_t *lpwfs;
2135 appinfo_t *hIC = NULL;
2136 BOOL r = FALSE;
2138 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
2139 if (!lpwfs)
2141 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2142 return FALSE;
2145 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2147 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2148 goto lend;
2151 if (lpwfs->download_in_progress != NULL)
2153 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2154 goto lend;
2157 if (!lpszSrc || !lpszDest)
2159 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2160 goto lend;
2163 hIC = lpwfs->lpAppInfo;
2164 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2166 rename_file_task_t *task;
2168 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRenameFileProc, sizeof(*task));
2169 task->src_file = heap_strdupW(lpszSrc);
2170 task->dst_file = heap_strdupW(lpszDest);
2172 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
2174 else
2176 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2179 lend:
2180 WININET_Release( &lpwfs->hdr );
2182 return r;
2185 /***********************************************************************
2186 * FTP_FtpRenameFileW (Internal)
2188 * Rename a file on the ftp server
2190 * RETURNS
2191 * TRUE on success
2192 * FALSE on failure
2195 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2197 INT nResCode;
2198 BOOL bSuccess = FALSE;
2199 appinfo_t *hIC = NULL;
2201 TRACE("\n");
2203 /* Clear any error information */
2204 INTERNET_SetLastError(0);
2206 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2207 goto lend;
2209 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2210 if (nResCode == 350)
2212 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2213 goto lend;
2215 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2218 if (nResCode == 250)
2219 bSuccess = TRUE;
2220 else
2221 FTP_SetResponseError(nResCode);
2223 lend:
2224 hIC = lpwfs->lpAppInfo;
2225 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2227 INTERNET_ASYNC_RESULT iar;
2229 iar.dwResult = (DWORD)bSuccess;
2230 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2231 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2232 &iar, sizeof(INTERNET_ASYNC_RESULT));
2235 return bSuccess;
2238 /***********************************************************************
2239 * FtpCommandA (WININET.@)
2241 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2242 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2244 BOOL r;
2245 WCHAR *cmdW;
2247 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2248 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2250 if (fExpectResponse)
2252 FIXME("data connection not supported\n");
2253 return FALSE;
2256 if (!lpszCommand || !lpszCommand[0])
2258 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2259 return FALSE;
2262 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2264 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2265 return FALSE;
2268 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2270 heap_free(cmdW);
2271 return r;
2274 /***********************************************************************
2275 * FtpCommandW (WININET.@)
2277 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2278 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2280 BOOL r = FALSE;
2281 ftp_session_t *lpwfs;
2282 LPSTR cmd = NULL;
2283 DWORD len, nBytesSent= 0;
2284 INT nResCode, nRC = 0;
2286 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2287 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2289 if (!lpszCommand || !lpszCommand[0])
2291 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2292 return FALSE;
2295 if (fExpectResponse)
2297 FIXME("data connection not supported\n");
2298 return FALSE;
2301 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
2302 if (!lpwfs)
2304 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2305 return FALSE;
2308 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2310 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2311 goto lend;
2314 if (lpwfs->download_in_progress != NULL)
2316 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2317 goto lend;
2320 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2321 if ((cmd = heap_alloc(len)))
2322 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2323 else
2325 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2326 goto lend;
2329 strcat(cmd, szCRLF);
2330 len--;
2332 TRACE("Sending (%s) len(%d)\n", cmd, len);
2333 while ((nBytesSent < len) && (nRC != -1))
2335 nRC = sock_send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2336 if (nRC != -1)
2338 nBytesSent += nRC;
2339 TRACE("Sent %d bytes\n", nRC);
2343 if (nBytesSent)
2345 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2346 if (nResCode > 0 && nResCode < 400)
2347 r = TRUE;
2348 else
2349 FTP_SetResponseError(nResCode);
2352 lend:
2353 WININET_Release( &lpwfs->hdr );
2354 heap_free( cmd );
2355 return r;
2359 /***********************************************************************
2360 * FTPSESSION_Destroy (internal)
2362 * Deallocate session handle
2364 static void FTPSESSION_Destroy(object_header_t *hdr)
2366 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2368 TRACE("\n");
2370 WININET_Release(&lpwfs->lpAppInfo->hdr);
2372 heap_free(lpwfs->lpszPassword);
2373 heap_free(lpwfs->lpszUserName);
2374 heap_free(lpwfs->servername);
2377 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2379 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2381 TRACE("\n");
2383 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2384 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2386 if (lpwfs->download_in_progress != NULL)
2387 lpwfs->download_in_progress->session_deleted = TRUE;
2389 if (lpwfs->sndSocket != -1)
2390 closesocket(lpwfs->sndSocket);
2392 if (lpwfs->lstnSocket != -1)
2393 closesocket(lpwfs->lstnSocket);
2395 if (lpwfs->pasvSocket != -1)
2396 closesocket(lpwfs->pasvSocket);
2398 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2399 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2402 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2404 switch(option) {
2405 case INTERNET_OPTION_HANDLE_TYPE:
2406 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2408 if (*size < sizeof(ULONG))
2409 return ERROR_INSUFFICIENT_BUFFER;
2411 *size = sizeof(DWORD);
2412 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2413 return ERROR_SUCCESS;
2416 return INET_QueryOption(hdr, option, buffer, size, unicode);
2419 static const object_vtbl_t FTPSESSIONVtbl = {
2420 FTPSESSION_Destroy,
2421 FTPSESSION_CloseConnection,
2422 FTPSESSION_QueryOption,
2423 INET_SetOption,
2424 NULL,
2425 NULL,
2426 NULL,
2427 NULL,
2428 NULL
2432 /***********************************************************************
2433 * FTP_Connect (internal)
2435 * Connect to a ftp server
2437 * RETURNS
2438 * HINTERNET a session handle on success
2439 * NULL on failure
2441 * NOTES:
2443 * Windows uses 'anonymous' as the username, when given a NULL username
2444 * and a NULL password. The password is first looked up in:
2446 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2448 * If this entry is not present it uses the current username as the password.
2452 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2453 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2454 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2455 DWORD dwInternalFlags)
2457 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2458 'M','i','c','r','o','s','o','f','t','\\',
2459 'W','i','n','d','o','w','s','\\',
2460 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2461 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2462 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2463 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2464 static const WCHAR szEmpty[] = {'\0'};
2465 struct sockaddr_in socketAddr;
2466 INT nsocket = -1;
2467 socklen_t sock_namelen;
2468 BOOL bSuccess = FALSE;
2469 ftp_session_t *lpwfs = NULL;
2470 char szaddr[INET_ADDRSTRLEN];
2472 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2473 hIC, debugstr_w(lpszServerName),
2474 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2476 assert( hIC->hdr.htype == WH_HINIT );
2478 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2480 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2481 return NULL;
2484 lpwfs = alloc_object(&hIC->hdr, &FTPSESSIONVtbl, sizeof(ftp_session_t));
2485 if (NULL == lpwfs)
2487 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2488 return NULL;
2491 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2492 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2493 else
2494 lpwfs->serverport = nServerPort;
2496 lpwfs->hdr.htype = WH_HFTPSESSION;
2497 lpwfs->hdr.dwFlags = dwFlags;
2498 lpwfs->hdr.dwContext = dwContext;
2499 lpwfs->hdr.dwInternalFlags |= dwInternalFlags;
2500 lpwfs->download_in_progress = NULL;
2501 lpwfs->sndSocket = -1;
2502 lpwfs->lstnSocket = -1;
2503 lpwfs->pasvSocket = -1;
2505 WININET_AddRef( &hIC->hdr );
2506 lpwfs->lpAppInfo = hIC;
2507 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2509 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
2510 if(strchrW(hIC->proxy, ' '))
2511 FIXME("Several proxies not implemented.\n");
2512 if(hIC->proxyBypass)
2513 FIXME("Proxy bypass is ignored.\n");
2515 if (!lpszUserName || !lpszUserName[0]) {
2516 HKEY key;
2517 WCHAR szPassword[MAX_PATH];
2518 DWORD len = sizeof(szPassword);
2520 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2522 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2523 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2524 /* Nothing in the registry, get the username and use that as the password */
2525 if (!GetUserNameW(szPassword, &len)) {
2526 /* Should never get here, but use an empty password as failsafe */
2527 strcpyW(szPassword, szEmpty);
2530 RegCloseKey(key);
2532 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2533 lpwfs->lpszPassword = heap_strdupW(szPassword);
2535 else {
2536 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2537 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2539 lpwfs->servername = heap_strdupW(lpszServerName);
2541 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2542 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2544 INTERNET_ASYNC_RESULT iar;
2546 iar.dwResult = (DWORD_PTR)lpwfs->hdr.hInternet;
2547 iar.dwError = ERROR_SUCCESS;
2549 SendAsyncCallback(&hIC->hdr, dwContext,
2550 INTERNET_STATUS_HANDLE_CREATED, &iar,
2551 sizeof(INTERNET_ASYNC_RESULT));
2554 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2555 (LPWSTR) lpszServerName, (strlenW(lpszServerName)+1) * sizeof(WCHAR));
2557 sock_namelen = sizeof(socketAddr);
2558 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen))
2560 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2561 goto lerror;
2564 if (socketAddr.sin_family != AF_INET)
2566 WARN("unsupported address family %d\n", socketAddr.sin_family);
2567 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2568 goto lerror;
2571 inet_ntop(socketAddr.sin_family, &socketAddr.sin_addr, szaddr, sizeof(szaddr));
2572 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2573 szaddr, strlen(szaddr)+1);
2575 nsocket = socket(AF_INET,SOCK_STREAM,0);
2576 if (nsocket == -1)
2578 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2579 goto lerror;
2582 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2583 szaddr, strlen(szaddr)+1);
2585 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2587 ERR("Unable to connect (%s)\n", strerror(errno));
2588 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2589 closesocket(nsocket);
2591 else
2593 TRACE("Connected to server\n");
2594 lpwfs->sndSocket = nsocket;
2595 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2596 szaddr, strlen(szaddr)+1);
2598 sock_namelen = sizeof(lpwfs->socketAddress);
2599 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2601 if (FTP_ConnectToHost(lpwfs))
2603 TRACE("Successfully logged into server\n");
2604 bSuccess = TRUE;
2608 lerror:
2609 if (!bSuccess)
2611 if(lpwfs)
2612 WININET_Release( &lpwfs->hdr );
2613 return NULL;
2616 return lpwfs->hdr.hInternet;
2620 /***********************************************************************
2621 * FTP_ConnectToHost (internal)
2623 * Connect to a ftp server
2625 * RETURNS
2626 * TRUE on success
2627 * NULL on failure
2630 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2632 INT nResCode;
2633 BOOL bSuccess = FALSE;
2635 TRACE("\n");
2636 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2638 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2639 goto lend;
2641 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2642 if (nResCode)
2644 /* Login successful... */
2645 if (nResCode == 230)
2646 bSuccess = TRUE;
2647 /* User name okay, need password... */
2648 else if (nResCode == 331)
2649 bSuccess = FTP_SendPassword(lpwfs);
2650 /* Need account for login... */
2651 else if (nResCode == 332)
2652 bSuccess = FTP_SendAccount(lpwfs);
2653 else
2654 FTP_SetResponseError(nResCode);
2657 TRACE("Returning %d\n", bSuccess);
2658 lend:
2659 return bSuccess;
2663 /***********************************************************************
2664 * FTP_SendCommandA (internal)
2666 * Send command to server
2668 * RETURNS
2669 * TRUE on success
2670 * NULL on failure
2673 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2674 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2676 DWORD len;
2677 CHAR *buf;
2678 DWORD nBytesSent = 0;
2679 int nRC = 0;
2680 DWORD dwParamLen;
2682 TRACE("%d: (%s) %d\n", ftpCmd, debugstr_a(lpszParam), nSocket);
2684 if (lpfnStatusCB)
2686 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2689 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2690 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2691 if (NULL == (buf = heap_alloc(len+1)))
2693 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2694 return FALSE;
2696 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2697 dwParamLen ? lpszParam : "", szCRLF);
2699 TRACE("Sending (%s) len(%d)\n", buf, len);
2700 while((nBytesSent < len) && (nRC != -1))
2702 nRC = sock_send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2703 nBytesSent += nRC;
2705 heap_free(buf);
2707 if (lpfnStatusCB)
2709 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2710 &nBytesSent, sizeof(DWORD));
2713 TRACE("Sent %d bytes\n", nBytesSent);
2714 return (nRC != -1);
2717 /***********************************************************************
2718 * FTP_SendCommand (internal)
2720 * Send command to server
2722 * RETURNS
2723 * TRUE on success
2724 * NULL on failure
2727 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2728 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2730 BOOL ret;
2731 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2732 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2733 heap_free(lpszParamA);
2734 return ret;
2737 /***********************************************************************
2738 * FTP_ReceiveResponse (internal)
2740 * Receive response from server
2742 * RETURNS
2743 * Reply code on success
2744 * 0 on failure
2747 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2749 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2750 DWORD nRecv;
2751 INT rc = 0;
2752 char firstprefix[5];
2753 BOOL multiline = FALSE;
2755 TRACE("socket(%d)\n", lpwfs->sndSocket);
2757 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2759 while(1)
2761 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2762 goto lerror;
2764 if (nRecv >= 3)
2766 if(!multiline)
2768 if(lpszResponse[3] != '-')
2769 break;
2770 else
2771 { /* Start of multiline response. Loop until we get "nnn " */
2772 multiline = TRUE;
2773 memcpy(firstprefix, lpszResponse, 3);
2774 firstprefix[3] = ' ';
2775 firstprefix[4] = '\0';
2778 else
2780 if(!memcmp(firstprefix, lpszResponse, 4))
2781 break;
2786 if (nRecv >= 3)
2788 rc = atoi(lpszResponse);
2790 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2791 &nRecv, sizeof(DWORD));
2794 lerror:
2795 TRACE("return %d\n", rc);
2796 return rc;
2800 /***********************************************************************
2801 * FTP_SendPassword (internal)
2803 * Send password to ftp server
2805 * RETURNS
2806 * TRUE on success
2807 * NULL on failure
2810 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2812 INT nResCode;
2813 BOOL bSuccess = FALSE;
2815 TRACE("\n");
2816 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2817 goto lend;
2819 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2820 if (nResCode)
2822 TRACE("Received reply code %d\n", nResCode);
2823 /* Login successful... */
2824 if (nResCode == 230)
2825 bSuccess = TRUE;
2826 /* Command not implemented, superfluous at the server site... */
2827 /* Need account for login... */
2828 else if (nResCode == 332)
2829 bSuccess = FTP_SendAccount(lpwfs);
2830 else
2831 FTP_SetResponseError(nResCode);
2834 lend:
2835 TRACE("Returning %d\n", bSuccess);
2836 return bSuccess;
2840 /***********************************************************************
2841 * FTP_SendAccount (internal)
2845 * RETURNS
2846 * TRUE on success
2847 * FALSE on failure
2850 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2852 INT nResCode;
2853 BOOL bSuccess = FALSE;
2855 TRACE("\n");
2856 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2857 goto lend;
2859 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2860 if (nResCode)
2861 bSuccess = TRUE;
2862 else
2863 FTP_SetResponseError(nResCode);
2865 lend:
2866 return bSuccess;
2870 /***********************************************************************
2871 * FTP_SendStore (internal)
2873 * Send request to upload file to ftp server
2875 * RETURNS
2876 * TRUE on success
2877 * FALSE on failure
2880 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2882 INT nResCode;
2883 BOOL bSuccess = FALSE;
2885 TRACE("\n");
2886 if (!FTP_InitListenSocket(lpwfs))
2887 goto lend;
2889 if (!FTP_SendType(lpwfs, dwType))
2890 goto lend;
2892 if (!FTP_SendPortOrPasv(lpwfs))
2893 goto lend;
2895 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2896 goto lend;
2897 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2898 if (nResCode)
2900 if (nResCode == 150 || nResCode == 125)
2901 bSuccess = TRUE;
2902 else
2903 FTP_SetResponseError(nResCode);
2906 lend:
2907 if (!bSuccess && lpwfs->lstnSocket != -1)
2909 closesocket(lpwfs->lstnSocket);
2910 lpwfs->lstnSocket = -1;
2913 return bSuccess;
2917 /***********************************************************************
2918 * FTP_InitListenSocket (internal)
2920 * Create a socket to listen for server response
2922 * RETURNS
2923 * TRUE on success
2924 * FALSE on failure
2927 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2929 BOOL bSuccess = FALSE;
2930 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2932 TRACE("\n");
2934 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2935 if (lpwfs->lstnSocket == -1)
2937 TRACE("Unable to create listening socket\n");
2938 goto lend;
2941 /* We obtain our ip addr from the name of the command channel socket */
2942 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2944 /* and get the system to assign us a port */
2945 lpwfs->lstnSocketAddress.sin_port = htons(0);
2947 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2949 TRACE("Unable to bind socket\n");
2950 goto lend;
2953 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2955 TRACE("listen failed\n");
2956 goto lend;
2959 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2960 bSuccess = TRUE;
2962 lend:
2963 if (!bSuccess && lpwfs->lstnSocket != -1)
2965 closesocket(lpwfs->lstnSocket);
2966 lpwfs->lstnSocket = -1;
2969 return bSuccess;
2973 /***********************************************************************
2974 * FTP_SendType (internal)
2976 * Tell server type of data being transferred
2978 * RETURNS
2979 * TRUE on success
2980 * FALSE on failure
2982 * W98SE doesn't cache the type that's currently set
2983 * (i.e. it sends it always),
2984 * so we probably don't want to do that either.
2986 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2988 INT nResCode;
2989 WCHAR type[] = { 'I','\0' };
2990 BOOL bSuccess = FALSE;
2992 TRACE("\n");
2993 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2994 type[0] = 'A';
2996 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2997 goto lend;
2999 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
3000 if (nResCode)
3002 if (nResCode == 2)
3003 bSuccess = TRUE;
3004 else
3005 FTP_SetResponseError(nResCode);
3008 lend:
3009 return bSuccess;
3013 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
3014 /***********************************************************************
3015 * FTP_GetFileSize (internal)
3017 * Retrieves from the server the size of the given file
3019 * RETURNS
3020 * TRUE on success
3021 * FALSE on failure
3024 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
3026 INT nResCode;
3027 BOOL bSuccess = FALSE;
3029 TRACE("\n");
3031 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
3032 goto lend;
3034 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3035 if (nResCode)
3037 if (nResCode == 213) {
3038 /* Now parses the output to get the actual file size */
3039 int i;
3040 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3042 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3043 if (lpszResponseBuffer[i] == '\0') return FALSE;
3044 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3046 bSuccess = TRUE;
3047 } else {
3048 FTP_SetResponseError(nResCode);
3052 lend:
3053 return bSuccess;
3055 #endif
3058 /***********************************************************************
3059 * FTP_SendPort (internal)
3061 * Tell server which port to use
3063 * RETURNS
3064 * TRUE on success
3065 * FALSE on failure
3068 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3070 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3071 INT nResCode;
3072 WCHAR szIPAddress[64];
3073 BOOL bSuccess = FALSE;
3074 TRACE("\n");
3076 sprintfW(szIPAddress, szIPFormat,
3077 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
3078 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
3079 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
3080 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
3081 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3082 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3084 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3085 goto lend;
3087 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3088 if (nResCode)
3090 if (nResCode == 200)
3091 bSuccess = TRUE;
3092 else
3093 FTP_SetResponseError(nResCode);
3096 lend:
3097 return bSuccess;
3101 /***********************************************************************
3102 * FTP_DoPassive (internal)
3104 * Tell server that we want to do passive transfers
3105 * and connect data socket
3107 * RETURNS
3108 * TRUE on success
3109 * FALSE on failure
3112 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3114 INT nResCode;
3115 BOOL bSuccess = FALSE;
3117 TRACE("\n");
3118 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3119 goto lend;
3121 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3122 if (nResCode)
3124 if (nResCode == 227)
3126 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3127 LPSTR p;
3128 int f[6];
3129 int i;
3130 char *pAddr, *pPort;
3131 INT nsocket = -1;
3132 struct sockaddr_in dataSocketAddress;
3134 p = lpszResponseBuffer+4; /* skip status code */
3135 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3137 if (*p == '\0')
3139 ERR("no address found in response, aborting\n");
3140 goto lend;
3143 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3144 &f[4], &f[5]) != 6)
3146 ERR("unknown response address format '%s', aborting\n", p);
3147 goto lend;
3149 for (i=0; i < 6; i++)
3150 f[i] = f[i] & 0xff;
3152 dataSocketAddress = lpwfs->socketAddress;
3153 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3154 pPort = (char *)&(dataSocketAddress.sin_port);
3155 pAddr[0] = f[0];
3156 pAddr[1] = f[1];
3157 pAddr[2] = f[2];
3158 pAddr[3] = f[3];
3159 pPort[0] = f[4];
3160 pPort[1] = f[5];
3162 nsocket = socket(AF_INET,SOCK_STREAM,0);
3163 if (nsocket == -1)
3164 goto lend;
3166 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3168 ERR("can't connect passive FTP data port.\n");
3169 closesocket(nsocket);
3170 goto lend;
3172 lpwfs->pasvSocket = nsocket;
3173 bSuccess = TRUE;
3175 else
3176 FTP_SetResponseError(nResCode);
3179 lend:
3180 return bSuccess;
3184 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3186 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3188 if (!FTP_DoPassive(lpwfs))
3189 return FALSE;
3191 else
3193 if (!FTP_SendPort(lpwfs))
3194 return FALSE;
3196 return TRUE;
3200 /***********************************************************************
3201 * FTP_GetDataSocket (internal)
3203 * Either accepts an incoming data socket connection from the server
3204 * or just returns the already opened socket after a PASV command
3205 * in case of passive FTP.
3208 * RETURNS
3209 * TRUE on success
3210 * FALSE on failure
3213 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3215 struct sockaddr_in saddr;
3216 socklen_t addrlen = sizeof(struct sockaddr);
3218 TRACE("\n");
3219 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3221 *nDataSocket = lpwfs->pasvSocket;
3222 lpwfs->pasvSocket = -1;
3224 else
3226 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3227 closesocket(lpwfs->lstnSocket);
3228 lpwfs->lstnSocket = -1;
3230 return *nDataSocket != -1;
3234 /***********************************************************************
3235 * FTP_SendData (internal)
3237 * Send data to the server
3239 * RETURNS
3240 * TRUE on success
3241 * FALSE on failure
3244 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3246 BY_HANDLE_FILE_INFORMATION fi;
3247 DWORD nBytesRead = 0;
3248 DWORD nBytesSent = 0;
3249 DWORD nTotalSent = 0;
3250 DWORD nBytesToSend, nLen;
3251 int nRC = 1;
3252 time_t s_long_time, e_long_time;
3253 LONG nSeconds;
3254 CHAR *lpszBuffer;
3256 TRACE("\n");
3257 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3259 /* Get the size of the file. */
3260 GetFileInformationByHandle(hFile, &fi);
3261 time(&s_long_time);
3265 nBytesToSend = nBytesRead - nBytesSent;
3267 if (nBytesToSend <= 0)
3269 /* Read data from file. */
3270 nBytesSent = 0;
3271 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3272 ERR("Failed reading from file\n");
3274 if (nBytesRead > 0)
3275 nBytesToSend = nBytesRead;
3276 else
3277 break;
3280 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3281 DATA_PACKET_SIZE : nBytesToSend;
3282 nRC = sock_send(nDataSocket, lpszBuffer, nLen, 0);
3284 if (nRC != -1)
3286 nBytesSent += nRC;
3287 nTotalSent += nRC;
3290 /* Do some computation to display the status. */
3291 time(&e_long_time);
3292 nSeconds = e_long_time - s_long_time;
3293 if( nSeconds / 60 > 0 )
3295 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3296 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3297 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3299 else
3301 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3302 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3303 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3305 } while (nRC != -1);
3307 TRACE("file transfer complete!\n");
3309 heap_free(lpszBuffer);
3310 return nTotalSent;
3314 /***********************************************************************
3315 * FTP_SendRetrieve (internal)
3317 * Send request to retrieve a file
3319 * RETURNS
3320 * Number of bytes to be received on success
3321 * 0 on failure
3324 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3326 INT nResCode;
3327 BOOL ret;
3329 TRACE("\n");
3330 if (!(ret = FTP_InitListenSocket(lpwfs)))
3331 goto lend;
3333 if (!(ret = FTP_SendType(lpwfs, dwType)))
3334 goto lend;
3336 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3337 goto lend;
3339 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3340 goto lend;
3342 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3343 if ((nResCode != 125) && (nResCode != 150)) {
3344 /* That means that we got an error getting the file. */
3345 FTP_SetResponseError(nResCode);
3346 ret = FALSE;
3349 lend:
3350 if (!ret && lpwfs->lstnSocket != -1)
3352 closesocket(lpwfs->lstnSocket);
3353 lpwfs->lstnSocket = -1;
3356 return ret;
3360 /***********************************************************************
3361 * FTP_RetrieveData (internal)
3363 * Retrieve data from server
3365 * RETURNS
3366 * TRUE on success
3367 * FALSE on failure
3370 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3372 DWORD nBytesWritten;
3373 INT nRC = 0;
3374 CHAR *lpszBuffer;
3376 TRACE("\n");
3378 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3379 if (NULL == lpszBuffer)
3381 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3382 return FALSE;
3385 while (nRC != -1)
3387 nRC = sock_recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3388 if (nRC != -1)
3390 /* other side closed socket. */
3391 if (nRC == 0)
3392 goto recv_end;
3393 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3397 TRACE("Data transfer complete\n");
3399 recv_end:
3400 heap_free(lpszBuffer);
3401 return (nRC != -1);
3404 /***********************************************************************
3405 * FTPFINDNEXT_Destroy (internal)
3407 * Deallocate session handle
3409 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3411 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3412 DWORD i;
3414 TRACE("\n");
3416 WININET_Release(&lpwfn->lpFtpSession->hdr);
3418 for (i = 0; i < lpwfn->size; i++)
3420 heap_free(lpwfn->lpafp[i].lpszName);
3422 heap_free(lpwfn->lpafp);
3425 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3427 WIN32_FIND_DATAW *find_data = data;
3428 DWORD res = ERROR_SUCCESS;
3430 TRACE("index(%d) size(%d)\n", find->index, find->size);
3432 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3434 if (find->index < find->size) {
3435 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3436 find->index++;
3438 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3439 }else {
3440 res = ERROR_NO_MORE_FILES;
3443 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3445 INTERNET_ASYNC_RESULT iar;
3447 iar.dwResult = (res == ERROR_SUCCESS);
3448 iar.dwError = res;
3450 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3451 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3452 sizeof(INTERNET_ASYNC_RESULT));
3455 return res;
3458 typedef struct {
3459 task_header_t hdr;
3460 WIN32_FIND_DATAW *find_data;
3461 } find_next_task_t;
3463 static void FTPFINDNEXT_AsyncFindNextFileProc(task_header_t *hdr)
3465 find_next_task_t *task = (find_next_task_t*)hdr;
3467 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)task->hdr.hdr, task->find_data);
3470 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3472 switch(option) {
3473 case INTERNET_OPTION_HANDLE_TYPE:
3474 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3476 if (*size < sizeof(ULONG))
3477 return ERROR_INSUFFICIENT_BUFFER;
3479 *size = sizeof(DWORD);
3480 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3481 return ERROR_SUCCESS;
3484 return INET_QueryOption(hdr, option, buffer, size, unicode);
3487 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3489 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3491 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3493 find_next_task_t *task;
3495 task = alloc_async_task(&find->hdr, FTPFINDNEXT_AsyncFindNextFileProc, sizeof(*task));
3496 task->find_data = data;
3498 INTERNET_AsyncCall(&task->hdr);
3499 return ERROR_SUCCESS;
3502 return FTPFINDNEXT_FindNextFileProc(find, data);
3505 static const object_vtbl_t FTPFINDNEXTVtbl = {
3506 FTPFINDNEXT_Destroy,
3507 NULL,
3508 FTPFINDNEXT_QueryOption,
3509 INET_SetOption,
3510 NULL,
3511 NULL,
3512 NULL,
3513 NULL,
3514 FTPFINDNEXT_FindNextFileW
3517 /***********************************************************************
3518 * FTP_ReceiveFileList (internal)
3520 * Read file list from server
3522 * RETURNS
3523 * Handle to file list on success
3524 * NULL on failure
3527 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3528 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3530 DWORD dwSize = 0;
3531 LPFILEPROPERTIESW lpafp = NULL;
3532 LPWININETFTPFINDNEXTW lpwfn = NULL;
3534 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3536 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3538 if(lpFindFileData)
3539 FTP_ConvertFileProp(lpafp, lpFindFileData);
3541 lpwfn = alloc_object(&lpwfs->hdr, &FTPFINDNEXTVtbl, sizeof(WININETFTPFINDNEXTW));
3542 if (lpwfn)
3544 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3545 lpwfn->hdr.dwContext = dwContext;
3546 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3547 lpwfn->size = dwSize;
3548 lpwfn->lpafp = lpafp;
3550 WININET_AddRef( &lpwfs->hdr );
3551 lpwfn->lpFtpSession = lpwfs;
3552 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3556 TRACE("Matched %d files\n", dwSize);
3557 return lpwfn ? lpwfn->hdr.hInternet : NULL;
3561 /***********************************************************************
3562 * FTP_ConvertFileProp (internal)
3564 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3566 * RETURNS
3567 * TRUE on success
3568 * FALSE on failure
3571 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3573 BOOL bSuccess = FALSE;
3575 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3577 if (lpafp)
3579 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3580 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3581 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3583 /* Not all fields are filled in */
3584 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3585 lpFindFileData->nFileSizeLow = lpafp->nSize;
3587 if (lpafp->bIsDirectory)
3588 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3590 if (lpafp->lpszName)
3591 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3593 bSuccess = TRUE;
3596 return bSuccess;
3599 /***********************************************************************
3600 * FTP_ParseNextFile (internal)
3602 * Parse the next line in file listing
3604 * RETURNS
3605 * TRUE on success
3606 * FALSE on failure
3608 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3610 static const char szSpace[] = " \t";
3611 DWORD nBufLen;
3612 char *pszLine;
3613 char *pszToken;
3614 char *pszTmp;
3615 BOOL found = FALSE;
3616 int i;
3618 lpfp->lpszName = NULL;
3619 do {
3620 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3621 return FALSE;
3623 pszToken = strtok(pszLine, szSpace);
3624 /* ls format
3625 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3627 * For instance:
3628 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3630 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3631 if(!FTP_ParsePermission(pszToken, lpfp))
3632 lpfp->bIsDirectory = FALSE;
3633 for(i=0; i<=3; i++) {
3634 if(!(pszToken = strtok(NULL, szSpace)))
3635 break;
3637 if(!pszToken) continue;
3638 if(lpfp->bIsDirectory) {
3639 TRACE("Is directory\n");
3640 lpfp->nSize = 0;
3642 else {
3643 TRACE("Size: %s\n", pszToken);
3644 lpfp->nSize = atol(pszToken);
3647 lpfp->tmLastModified.wSecond = 0;
3648 lpfp->tmLastModified.wMinute = 0;
3649 lpfp->tmLastModified.wHour = 0;
3650 lpfp->tmLastModified.wDay = 0;
3651 lpfp->tmLastModified.wMonth = 0;
3652 lpfp->tmLastModified.wYear = 0;
3654 /* Determine month */
3655 pszToken = strtok(NULL, szSpace);
3656 if(!pszToken) continue;
3657 if(strlen(pszToken) >= 3) {
3658 pszToken[3] = 0;
3659 if((pszTmp = StrStrIA(szMonths, pszToken)))
3660 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3662 /* Determine day */
3663 pszToken = strtok(NULL, szSpace);
3664 if(!pszToken) continue;
3665 lpfp->tmLastModified.wDay = atoi(pszToken);
3666 /* Determine time or year */
3667 pszToken = strtok(NULL, szSpace);
3668 if(!pszToken) continue;
3669 if((pszTmp = strchr(pszToken, ':'))) {
3670 SYSTEMTIME curr_time;
3671 *pszTmp = 0;
3672 pszTmp++;
3673 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3674 lpfp->tmLastModified.wHour = atoi(pszToken);
3675 GetLocalTime( &curr_time );
3676 lpfp->tmLastModified.wYear = curr_time.wYear;
3678 else {
3679 lpfp->tmLastModified.wYear = atoi(pszToken);
3680 lpfp->tmLastModified.wHour = 12;
3682 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3683 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3684 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3686 pszToken = strtok(NULL, szSpace);
3687 if(!pszToken) continue;
3688 lpfp->lpszName = heap_strdupAtoW(pszToken);
3689 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3691 /* NT way of parsing ... :
3693 07-13-03 08:55PM <DIR> sakpatch
3694 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3696 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3697 int mon, mday, year, hour, min;
3698 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3700 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3701 lpfp->tmLastModified.wDay = mday;
3702 lpfp->tmLastModified.wMonth = mon;
3703 lpfp->tmLastModified.wYear = year;
3705 /* Hacky and bad Y2K protection :-) */
3706 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3708 pszToken = strtok(NULL, szSpace);
3709 if(!pszToken) continue;
3710 sscanf(pszToken, "%d:%d", &hour, &min);
3711 lpfp->tmLastModified.wHour = hour;
3712 lpfp->tmLastModified.wMinute = min;
3713 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3714 lpfp->tmLastModified.wHour += 12;
3716 lpfp->tmLastModified.wSecond = 0;
3718 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3719 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3720 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3722 pszToken = strtok(NULL, szSpace);
3723 if(!pszToken) continue;
3724 if(!strcasecmp(pszToken, "<DIR>")) {
3725 lpfp->bIsDirectory = TRUE;
3726 lpfp->nSize = 0;
3727 TRACE("Is directory\n");
3729 else {
3730 lpfp->bIsDirectory = FALSE;
3731 lpfp->nSize = atol(pszToken);
3732 TRACE("Size: %d\n", lpfp->nSize);
3735 pszToken = strtok(NULL, szSpace);
3736 if(!pszToken) continue;
3737 lpfp->lpszName = heap_strdupAtoW(pszToken);
3738 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3740 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3741 else if(pszToken[0] == '+') {
3742 FIXME("EPLF Format not implemented\n");
3745 if(lpfp->lpszName) {
3746 if((lpszSearchFile == NULL) ||
3747 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3748 found = TRUE;
3749 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3751 else {
3752 heap_free(lpfp->lpszName);
3753 lpfp->lpszName = NULL;
3756 } while(!found);
3757 return TRUE;
3760 /***********************************************************************
3761 * FTP_ParseDirectory (internal)
3763 * Parse string of directory information
3765 * RETURNS
3766 * TRUE on success
3767 * FALSE on failure
3769 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3770 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3772 BOOL bSuccess = TRUE;
3773 INT sizeFilePropArray = 500;/*20; */
3774 INT indexFilePropArray = -1;
3776 TRACE("\n");
3778 /* Allocate initial file properties array */
3779 *lpafp = heap_alloc_zero(sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3780 if (!*lpafp)
3781 return FALSE;
3783 do {
3784 if (indexFilePropArray+1 >= sizeFilePropArray)
3786 LPFILEPROPERTIESW tmpafp;
3788 sizeFilePropArray *= 2;
3789 tmpafp = heap_realloc_zero(*lpafp, sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3790 if (NULL == tmpafp)
3792 bSuccess = FALSE;
3793 break;
3796 *lpafp = tmpafp;
3798 indexFilePropArray++;
3799 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3801 if (bSuccess && indexFilePropArray)
3803 if (indexFilePropArray < sizeFilePropArray - 1)
3805 LPFILEPROPERTIESW tmpafp;
3807 tmpafp = heap_realloc(*lpafp, sizeof(FILEPROPERTIESW)*indexFilePropArray);
3808 if (NULL != tmpafp)
3809 *lpafp = tmpafp;
3811 *dwfp = indexFilePropArray;
3813 else
3815 heap_free(*lpafp);
3816 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3817 bSuccess = FALSE;
3820 return bSuccess;
3824 /***********************************************************************
3825 * FTP_ParsePermission (internal)
3827 * Parse permission string of directory information
3829 * RETURNS
3830 * TRUE on success
3831 * FALSE on failure
3834 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3836 BOOL bSuccess = TRUE;
3837 unsigned short nPermission = 0;
3838 INT nPos = 1;
3839 INT nLast = 9;
3841 TRACE("\n");
3842 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3844 bSuccess = FALSE;
3845 return bSuccess;
3848 lpfp->bIsDirectory = (*lpszPermission == 'd');
3851 switch (nPos)
3853 case 1:
3854 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3855 break;
3856 case 2:
3857 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3858 break;
3859 case 3:
3860 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3861 break;
3862 case 4:
3863 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3864 break;
3865 case 5:
3866 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3867 break;
3868 case 6:
3869 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3870 break;
3871 case 7:
3872 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3873 break;
3874 case 8:
3875 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3876 break;
3877 case 9:
3878 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3879 break;
3881 nPos++;
3882 }while (nPos <= nLast);
3884 lpfp->permissions = nPermission;
3885 return bSuccess;
3889 /***********************************************************************
3890 * FTP_SetResponseError (internal)
3892 * Set the appropriate error code for a given response from the server
3894 * RETURNS
3897 static DWORD FTP_SetResponseError(DWORD dwResponse)
3899 DWORD dwCode = 0;
3901 switch(dwResponse)
3903 case 425: /* Cannot open data connection. */
3904 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3905 break;
3907 case 426: /* Connection closed, transer aborted. */
3908 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3909 break;
3911 case 530: /* Not logged in. Login incorrect. */
3912 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3913 break;
3915 case 421: /* Service not available - Server may be shutting down. */
3916 case 450: /* File action not taken. File may be busy. */
3917 case 451: /* Action aborted. Server error. */
3918 case 452: /* Action not taken. Insufficient storage space on server. */
3919 case 500: /* Syntax error. Command unrecognized. */
3920 case 501: /* Syntax error. Error in parameters or arguments. */
3921 case 502: /* Command not implemented. */
3922 case 503: /* Bad sequence of commands. */
3923 case 504: /* Command not implemented for that parameter. */
3924 case 532: /* Need account for storing files */
3925 case 550: /* File action not taken. File not found or no access. */
3926 case 551: /* Requested action aborted. Page type unknown */
3927 case 552: /* Action aborted. Exceeded storage allocation */
3928 case 553: /* Action not taken. File name not allowed. */
3930 default:
3931 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3932 break;
3935 INTERNET_SetLastError(dwCode);
3936 return dwCode;