ntdll: Add a wrapper to call the unhandled exception filter.
[wine.git] / dlls / wininet / ftp.c
blobe12e4941605f59608909702682fa1bf6b23e5892
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 "ws2tcpip.h"
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <time.h>
37 #include <assert.h>
39 #include "windef.h"
40 #include "winbase.h"
41 #include "wingdi.h"
42 #include "winuser.h"
43 #include "wininet.h"
44 #include "winnls.h"
45 #include "winerror.h"
46 #include "winreg.h"
47 #include "winternl.h"
48 #include "shlwapi.h"
50 #include "wine/debug.h"
51 #include "internet.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
55 #define RESPONSE_TIMEOUT 30
57 typedef struct _ftp_session_t ftp_session_t;
59 typedef struct
61 object_header_t hdr;
62 ftp_session_t *lpFtpSession;
63 BOOL session_deleted;
64 int nDataSocket;
65 WCHAR *cache_file;
66 HANDLE cache_file_handle;
67 } ftp_file_t;
69 struct _ftp_session_t
71 object_header_t hdr;
72 appinfo_t *lpAppInfo;
73 int sndSocket;
74 int lstnSocket;
75 int pasvSocket; /* data socket connected by us in case of passive FTP */
76 ftp_file_t *download_in_progress;
77 struct sockaddr_in socketAddress;
78 struct sockaddr_in lstnSocketAddress;
79 LPWSTR servername;
80 INTERNET_PORT serverport;
81 LPWSTR lpszPassword;
82 LPWSTR lpszUserName;
85 typedef struct
87 BOOL bIsDirectory;
88 LPWSTR lpszName;
89 DWORD nSize;
90 SYSTEMTIME tmLastModified;
91 unsigned short permissions;
92 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
94 typedef struct
96 object_header_t hdr;
97 ftp_session_t *lpFtpSession;
98 DWORD index;
99 DWORD size;
100 LPFILEPROPERTIESW lpafp;
101 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
103 #define DATA_PACKET_SIZE 0x2000
104 #define szCRLF "\r\n"
105 #define MAX_BACKLOG 5
107 /* Testing shows that Windows only accepts dwFlags where the last
108 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
110 #define FTP_CONDITION_MASK 0x0007
112 typedef enum {
113 /* FTP commands with arguments. */
114 FTP_CMD_ACCT,
115 FTP_CMD_CWD,
116 FTP_CMD_DELE,
117 FTP_CMD_MKD,
118 FTP_CMD_PASS,
119 FTP_CMD_PORT,
120 FTP_CMD_RETR,
121 FTP_CMD_RMD,
122 FTP_CMD_RNFR,
123 FTP_CMD_RNTO,
124 FTP_CMD_STOR,
125 FTP_CMD_TYPE,
126 FTP_CMD_USER,
127 FTP_CMD_SIZE,
129 /* FTP commands without arguments. */
130 FTP_CMD_ABOR,
131 FTP_CMD_LIST,
132 FTP_CMD_NLST,
133 FTP_CMD_PASV,
134 FTP_CMD_PWD,
135 FTP_CMD_QUIT,
136 } FTP_COMMAND;
138 static const CHAR *const szFtpCommands[] = {
139 "ACCT",
140 "CWD",
141 "DELE",
142 "MKD",
143 "PASS",
144 "PORT",
145 "RETR",
146 "RMD",
147 "RNFR",
148 "RNTO",
149 "STOR",
150 "TYPE",
151 "USER",
152 "SIZE",
153 "ABOR",
154 "LIST",
155 "NLST",
156 "PASV",
157 "PWD",
158 "QUIT",
161 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
162 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
164 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
165 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext);
166 static BOOL FTP_SendStore(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
167 static BOOL FTP_GetDataSocket(ftp_session_t*, LPINT nDataSocket);
168 static BOOL FTP_SendData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
169 static INT FTP_ReceiveResponse(ftp_session_t*, DWORD_PTR dwContext);
170 static BOOL FTP_SendRetrieve(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
171 static BOOL FTP_RetrieveFileData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
172 static BOOL FTP_InitListenSocket(ftp_session_t*);
173 static BOOL FTP_ConnectToHost(ftp_session_t*);
174 static BOOL FTP_SendPassword(ftp_session_t*);
175 static BOOL FTP_SendAccount(ftp_session_t*);
176 static BOOL FTP_SendType(ftp_session_t*, DWORD dwType);
177 static BOOL FTP_SendPort(ftp_session_t*);
178 static BOOL FTP_DoPassive(ftp_session_t*);
179 static BOOL FTP_SendPortOrPasv(ftp_session_t*);
180 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
181 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
182 static BOOL FTP_ParseDirectory(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
183 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
184 static HINTERNET FTP_ReceiveFileList(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
185 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
186 static DWORD FTP_SetResponseError(DWORD dwResponse);
187 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
188 static BOOL FTP_FtpPutFileW(ftp_session_t*, LPCWSTR lpszLocalFile,
189 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
190 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
191 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
192 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t*,
193 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
194 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t*, LPWSTR lpszCurrentDirectory,
195 LPDWORD lpdwCurrentDirectory);
196 static BOOL FTP_FtpRenameFileW(ftp_session_t*, LPCWSTR lpszSrc, LPCWSTR lpszDest);
197 static BOOL FTP_FtpRemoveDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
198 static BOOL FTP_FtpDeleteFileW(ftp_session_t*, LPCWSTR lpszFileName);
199 static BOOL FTP_FtpGetFileW(ftp_session_t*, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
200 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
201 DWORD_PTR dwContext);
203 /* A temporary helper until we get rid of INTERNET_GetLastError calls */
204 static BOOL res_to_le(DWORD res)
206 if(res != ERROR_SUCCESS)
207 INTERNET_SetLastError(res);
208 return res == ERROR_SUCCESS;
211 /***********************************************************************
212 * FtpPutFileA (WININET.@)
214 * Uploads a file to the FTP server
216 * RETURNS
217 * TRUE on success
218 * FALSE on failure
221 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
222 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
224 LPWSTR lpwzLocalFile;
225 LPWSTR lpwzNewRemoteFile;
226 BOOL ret;
228 lpwzLocalFile = heap_strdupAtoW(lpszLocalFile);
229 lpwzNewRemoteFile = heap_strdupAtoW(lpszNewRemoteFile);
230 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
231 dwFlags, dwContext);
232 heap_free(lpwzLocalFile);
233 heap_free(lpwzNewRemoteFile);
234 return ret;
237 typedef struct {
238 task_header_t hdr;
239 WCHAR *local_file;
240 WCHAR *remote_file;
241 DWORD flags;
242 DWORD_PTR context;
243 } put_file_task_t;
245 static void AsyncFtpPutFileProc(task_header_t *hdr)
247 put_file_task_t *task = (put_file_task_t*)hdr;
248 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
250 TRACE("%p\n", session);
252 FTP_FtpPutFileW(session, task->local_file, task->remote_file,
253 task->flags, task->context);
255 heap_free(task->local_file);
256 heap_free(task->remote_file);
259 /***********************************************************************
260 * FtpPutFileW (WININET.@)
262 * Uploads a file to the FTP server
264 * RETURNS
265 * TRUE on success
266 * FALSE on failure
269 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
270 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
272 ftp_session_t *lpwfs;
273 appinfo_t *hIC = NULL;
274 BOOL r = FALSE;
276 if (!lpszLocalFile || !lpszNewRemoteFile)
278 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
279 return FALSE;
282 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
283 if (!lpwfs)
285 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
286 return FALSE;
289 if (WH_HFTPSESSION != lpwfs->hdr.htype)
291 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
292 goto lend;
295 if (lpwfs->download_in_progress != NULL)
297 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
298 goto lend;
301 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
303 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
304 goto lend;
307 hIC = lpwfs->lpAppInfo;
308 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
310 put_file_task_t *task = alloc_async_task(&lpwfs->hdr, AsyncFtpPutFileProc, sizeof(*task));
312 task->local_file = heap_strdupW(lpszLocalFile);
313 task->remote_file = heap_strdupW(lpszNewRemoteFile);
314 task->flags = dwFlags;
315 task->context = dwContext;
317 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
319 else
321 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
322 lpszNewRemoteFile, dwFlags, dwContext);
325 lend:
326 WININET_Release( &lpwfs->hdr );
328 return r;
331 /***********************************************************************
332 * FTP_FtpPutFileW (Internal)
334 * Uploads a file to the FTP server
336 * RETURNS
337 * TRUE on success
338 * FALSE on failure
341 static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
342 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
344 HANDLE hFile;
345 BOOL bSuccess = FALSE;
346 appinfo_t *hIC = NULL;
347 INT nResCode;
349 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
351 /* Clear any error information */
352 INTERNET_SetLastError(0);
354 /* Open file to be uploaded */
355 if (INVALID_HANDLE_VALUE ==
356 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
357 /* Let CreateFile set the appropriate error */
358 return FALSE;
360 hIC = lpwfs->lpAppInfo;
362 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
364 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
366 INT nDataSocket;
368 /* Get data socket to server */
369 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
371 FTP_SendData(lpwfs, nDataSocket, hFile);
372 closesocket(nDataSocket);
373 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
374 if (nResCode)
376 if (nResCode == 226)
377 bSuccess = TRUE;
378 else
379 FTP_SetResponseError(nResCode);
384 if (lpwfs->lstnSocket != -1)
386 closesocket(lpwfs->lstnSocket);
387 lpwfs->lstnSocket = -1;
390 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
392 INTERNET_ASYNC_RESULT iar;
394 iar.dwResult = (DWORD)bSuccess;
395 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
396 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
397 &iar, sizeof(INTERNET_ASYNC_RESULT));
400 CloseHandle(hFile);
402 return bSuccess;
406 /***********************************************************************
407 * FtpSetCurrentDirectoryA (WININET.@)
409 * Change the working directory on the FTP server
411 * RETURNS
412 * TRUE on success
413 * FALSE on failure
416 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
418 LPWSTR lpwzDirectory;
419 BOOL ret;
421 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
422 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
423 heap_free(lpwzDirectory);
424 return ret;
427 typedef struct {
428 task_header_t hdr;
429 WCHAR *directory;
430 } directory_task_t;
432 static void AsyncFtpSetCurrentDirectoryProc(task_header_t *hdr)
434 directory_task_t *task = (directory_task_t*)hdr;
435 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
437 TRACE("%p\n", session);
439 FTP_FtpSetCurrentDirectoryW(session, task->directory);
440 heap_free(task->directory);
443 /***********************************************************************
444 * FtpSetCurrentDirectoryW (WININET.@)
446 * Change the working directory on the FTP server
448 * RETURNS
449 * TRUE on success
450 * FALSE on failure
453 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
455 ftp_session_t *lpwfs = NULL;
456 appinfo_t *hIC = NULL;
457 BOOL r = FALSE;
459 if (!lpszDirectory)
461 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
462 goto lend;
465 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
466 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
468 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
469 goto lend;
472 if (lpwfs->download_in_progress != NULL)
474 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
475 goto lend;
478 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
480 hIC = lpwfs->lpAppInfo;
481 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
483 directory_task_t *task;
485 task = alloc_async_task(&lpwfs->hdr, AsyncFtpSetCurrentDirectoryProc, sizeof(*task));
486 task->directory = heap_strdupW(lpszDirectory);
488 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
490 else
492 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
495 lend:
496 if( lpwfs )
497 WININET_Release( &lpwfs->hdr );
499 return r;
503 /***********************************************************************
504 * FTP_FtpSetCurrentDirectoryW (Internal)
506 * Change the working directory on the FTP server
508 * RETURNS
509 * TRUE on success
510 * FALSE on failure
513 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
515 INT nResCode;
516 appinfo_t *hIC = NULL;
517 BOOL bSuccess = FALSE;
519 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
521 /* Clear any error information */
522 INTERNET_SetLastError(0);
524 hIC = lpwfs->lpAppInfo;
525 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
526 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
527 goto lend;
529 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
531 if (nResCode)
533 if (nResCode == 250)
534 bSuccess = TRUE;
535 else
536 FTP_SetResponseError(nResCode);
539 lend:
540 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
542 INTERNET_ASYNC_RESULT iar;
544 iar.dwResult = bSuccess;
545 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
546 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
547 &iar, sizeof(INTERNET_ASYNC_RESULT));
549 return bSuccess;
553 /***********************************************************************
554 * FtpCreateDirectoryA (WININET.@)
556 * Create new directory on the FTP server
558 * RETURNS
559 * TRUE on success
560 * FALSE on failure
563 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
565 LPWSTR lpwzDirectory;
566 BOOL ret;
568 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
569 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
570 heap_free(lpwzDirectory);
571 return ret;
575 static void AsyncFtpCreateDirectoryProc(task_header_t *hdr)
577 directory_task_t *task = (directory_task_t*)hdr;
578 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
580 TRACE(" %p\n", session);
582 FTP_FtpCreateDirectoryW(session, task->directory);
583 heap_free(task->directory);
586 /***********************************************************************
587 * FtpCreateDirectoryW (WININET.@)
589 * Create new directory on the FTP server
591 * RETURNS
592 * TRUE on success
593 * FALSE on failure
596 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
598 ftp_session_t *lpwfs;
599 appinfo_t *hIC = NULL;
600 BOOL r = FALSE;
602 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
603 if (!lpwfs)
605 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
606 return FALSE;
609 if (WH_HFTPSESSION != lpwfs->hdr.htype)
611 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
612 goto lend;
615 if (lpwfs->download_in_progress != NULL)
617 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
618 goto lend;
621 if (!lpszDirectory)
623 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
624 goto lend;
627 hIC = lpwfs->lpAppInfo;
628 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
630 directory_task_t *task;
632 task = alloc_async_task(&lpwfs->hdr, AsyncFtpCreateDirectoryProc, sizeof(*task));
633 task->directory = heap_strdupW(lpszDirectory);
635 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
637 else
639 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
641 lend:
642 WININET_Release( &lpwfs->hdr );
644 return r;
648 /***********************************************************************
649 * FTP_FtpCreateDirectoryW (Internal)
651 * Create new directory on the FTP server
653 * RETURNS
654 * TRUE on success
655 * FALSE on failure
658 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
660 INT nResCode;
661 BOOL bSuccess = FALSE;
662 appinfo_t *hIC = NULL;
664 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
666 /* Clear any error information */
667 INTERNET_SetLastError(0);
669 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
670 goto lend;
672 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
673 if (nResCode)
675 if (nResCode == 257)
676 bSuccess = TRUE;
677 else
678 FTP_SetResponseError(nResCode);
681 lend:
682 hIC = lpwfs->lpAppInfo;
683 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
685 INTERNET_ASYNC_RESULT iar;
687 iar.dwResult = (DWORD)bSuccess;
688 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
689 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
690 &iar, sizeof(INTERNET_ASYNC_RESULT));
693 return bSuccess;
696 /***********************************************************************
697 * FtpFindFirstFileA (WININET.@)
699 * Search the specified directory
701 * RETURNS
702 * HINTERNET on success
703 * NULL on failure
706 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
707 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
709 LPWSTR lpwzSearchFile;
710 WIN32_FIND_DATAW wfd;
711 LPWIN32_FIND_DATAW lpFindFileDataW;
712 HINTERNET ret;
714 lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
715 lpFindFileDataW = lpFindFileData?&wfd:NULL;
716 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
717 heap_free(lpwzSearchFile);
719 if (ret && lpFindFileData)
720 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
722 return ret;
725 typedef struct {
726 task_header_t hdr;
727 WCHAR *search_file;
728 WIN32_FIND_DATAW *find_file_data;
729 DWORD flags;
730 DWORD_PTR context;
731 } find_first_file_task_t;
733 static void AsyncFtpFindFirstFileProc(task_header_t *hdr)
735 find_first_file_task_t *task = (find_first_file_task_t*)hdr;
736 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
738 TRACE("%p\n", session);
740 FTP_FtpFindFirstFileW(session, task->search_file, task->find_file_data, task->flags, task->context);
741 heap_free(task->search_file);
744 /***********************************************************************
745 * FtpFindFirstFileW (WININET.@)
747 * Search the specified directory
749 * RETURNS
750 * HINTERNET on success
751 * NULL on failure
754 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
755 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
757 ftp_session_t *lpwfs;
758 appinfo_t *hIC = NULL;
759 HINTERNET r = NULL;
761 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
762 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
764 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
765 goto lend;
768 if (lpwfs->download_in_progress != NULL)
770 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
771 goto lend;
774 hIC = lpwfs->lpAppInfo;
775 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
777 find_first_file_task_t *task;
779 task = alloc_async_task(&lpwfs->hdr, AsyncFtpFindFirstFileProc, sizeof(*task));
780 task->search_file = heap_strdupW(lpszSearchFile);
781 task->find_file_data = lpFindFileData;
782 task->flags = dwFlags;
783 task->context = dwContext;
785 INTERNET_AsyncCall(&task->hdr);
786 r = NULL;
788 else
790 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
791 dwFlags, dwContext);
793 lend:
794 if( lpwfs )
795 WININET_Release( &lpwfs->hdr );
797 return r;
801 /***********************************************************************
802 * FTP_FtpFindFirstFileW (Internal)
804 * Search the specified directory
806 * RETURNS
807 * HINTERNET on success
808 * NULL on failure
811 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
812 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
814 INT nResCode;
815 appinfo_t *hIC = NULL;
816 HINTERNET hFindNext = NULL;
817 LPWSTR lpszSearchPath = NULL;
819 TRACE("\n");
821 /* Clear any error information */
822 INTERNET_SetLastError(0);
824 if (!FTP_InitListenSocket(lpwfs))
825 goto lend;
827 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
828 goto lend;
830 if (!FTP_SendPortOrPasv(lpwfs))
831 goto lend;
833 /* split search path into file and path */
834 if (lpszSearchFile)
836 LPCWSTR name = lpszSearchFile, p;
837 if ((p = strrchrW( name, '\\' ))) name = p + 1;
838 if ((p = strrchrW( name, '/' ))) name = p + 1;
839 if (name != lpszSearchFile)
841 lpszSearchPath = heap_strndupW(lpszSearchFile, name - lpszSearchFile);
842 lpszSearchFile = name;
846 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, lpszSearchPath,
847 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
848 goto lend;
850 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
851 if (nResCode)
853 if (nResCode == 125 || nResCode == 150)
855 INT nDataSocket;
857 /* Get data socket to server */
858 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
860 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
861 closesocket(nDataSocket);
862 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
863 if (nResCode != 226 && nResCode != 250)
864 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
867 else
868 FTP_SetResponseError(nResCode);
871 lend:
872 heap_free(lpszSearchPath);
874 if (lpwfs->lstnSocket != -1)
876 closesocket(lpwfs->lstnSocket);
877 lpwfs->lstnSocket = -1;
880 hIC = lpwfs->lpAppInfo;
881 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
883 INTERNET_ASYNC_RESULT iar;
885 if (hFindNext)
887 iar.dwResult = (DWORD_PTR)hFindNext;
888 iar.dwError = ERROR_SUCCESS;
889 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
890 &iar, sizeof(INTERNET_ASYNC_RESULT));
893 iar.dwResult = (DWORD_PTR)hFindNext;
894 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
895 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
896 &iar, sizeof(INTERNET_ASYNC_RESULT));
899 return hFindNext;
903 /***********************************************************************
904 * FtpGetCurrentDirectoryA (WININET.@)
906 * Retrieves the current directory
908 * RETURNS
909 * TRUE on success
910 * FALSE on failure
913 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
914 LPDWORD lpdwCurrentDirectory)
916 WCHAR *dir = NULL;
917 DWORD len;
918 BOOL ret;
920 if(lpdwCurrentDirectory) {
921 len = *lpdwCurrentDirectory;
922 if(lpszCurrentDirectory)
924 dir = heap_alloc(len * sizeof(WCHAR));
925 if (NULL == dir)
927 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
928 return FALSE;
932 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
934 if (ret && lpszCurrentDirectory)
935 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
937 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
938 heap_free(dir);
939 return ret;
942 typedef struct {
943 task_header_t hdr;
944 WCHAR *directory;
945 DWORD *directory_len;
946 } get_current_dir_task_t;
948 static void AsyncFtpGetCurrentDirectoryProc(task_header_t *hdr)
950 get_current_dir_task_t *task = (get_current_dir_task_t*)hdr;
951 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
953 TRACE("%p\n", session);
955 FTP_FtpGetCurrentDirectoryW(session, task->directory, task->directory_len);
958 /***********************************************************************
959 * FtpGetCurrentDirectoryW (WININET.@)
961 * Retrieves the current directory
963 * RETURNS
964 * TRUE on success
965 * FALSE on failure
968 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
969 LPDWORD lpdwCurrentDirectory)
971 ftp_session_t *lpwfs;
972 appinfo_t *hIC = NULL;
973 BOOL r = FALSE;
975 TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
977 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
978 if (NULL == lpwfs)
980 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
981 goto lend;
984 if (WH_HFTPSESSION != lpwfs->hdr.htype)
986 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
987 goto lend;
990 if (!lpdwCurrentDirectory)
992 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
993 goto lend;
996 if (lpszCurrentDirectory == NULL)
998 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
999 goto lend;
1002 if (lpwfs->download_in_progress != NULL)
1004 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1005 goto lend;
1008 hIC = lpwfs->lpAppInfo;
1009 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1011 get_current_dir_task_t *task;
1013 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetCurrentDirectoryProc, sizeof(*task));
1014 task->directory = lpszCurrentDirectory;
1015 task->directory_len = lpdwCurrentDirectory;
1017 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1019 else
1021 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
1022 lpdwCurrentDirectory);
1025 lend:
1026 if( lpwfs )
1027 WININET_Release( &lpwfs->hdr );
1029 return r;
1033 /***********************************************************************
1034 * FTP_FtpGetCurrentDirectoryW (Internal)
1036 * Retrieves the current directory
1038 * RETURNS
1039 * TRUE on success
1040 * FALSE on failure
1043 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
1044 LPDWORD lpdwCurrentDirectory)
1046 INT nResCode;
1047 appinfo_t *hIC = NULL;
1048 BOOL bSuccess = FALSE;
1050 /* Clear any error information */
1051 INTERNET_SetLastError(0);
1053 hIC = lpwfs->lpAppInfo;
1054 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1055 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1056 goto lend;
1058 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1059 if (nResCode)
1061 if (nResCode == 257) /* Extract directory name */
1063 DWORD firstpos, lastpos, len;
1064 LPWSTR lpszResponseBuffer = heap_strdupAtoW(INTERNET_GetResponseBuffer());
1066 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1068 if ('"' == lpszResponseBuffer[lastpos])
1070 if (!firstpos)
1071 firstpos = lastpos;
1072 else
1073 break;
1076 len = lastpos - firstpos;
1077 if (*lpdwCurrentDirectory >= len)
1079 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1080 lpszCurrentDirectory[len - 1] = 0;
1081 *lpdwCurrentDirectory = len;
1082 bSuccess = TRUE;
1084 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1086 heap_free(lpszResponseBuffer);
1088 else
1089 FTP_SetResponseError(nResCode);
1092 lend:
1093 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1095 INTERNET_ASYNC_RESULT iar;
1097 iar.dwResult = bSuccess;
1098 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1099 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1100 &iar, sizeof(INTERNET_ASYNC_RESULT));
1103 return bSuccess;
1107 /***********************************************************************
1108 * FTPFILE_Destroy(internal)
1110 * Closes the file transfer handle. This also 'cleans' the data queue of
1111 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1114 static void FTPFILE_Destroy(object_header_t *hdr)
1116 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1117 ftp_session_t *lpwfs = lpwh->lpFtpSession;
1118 INT nResCode;
1120 TRACE("\n");
1122 if (lpwh->cache_file_handle != INVALID_HANDLE_VALUE)
1123 CloseHandle(lpwh->cache_file_handle);
1125 heap_free(lpwh->cache_file);
1127 if (!lpwh->session_deleted)
1128 lpwfs->download_in_progress = NULL;
1130 if (lpwh->nDataSocket != -1)
1131 closesocket(lpwh->nDataSocket);
1133 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1134 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1136 WININET_Release(&lpwh->lpFtpSession->hdr);
1139 static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1141 switch(option) {
1142 case INTERNET_OPTION_HANDLE_TYPE:
1143 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1145 if (*size < sizeof(ULONG))
1146 return ERROR_INSUFFICIENT_BUFFER;
1148 *size = sizeof(DWORD);
1149 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1150 return ERROR_SUCCESS;
1151 case INTERNET_OPTION_DATAFILE_NAME:
1153 DWORD required;
1154 ftp_file_t *file = (ftp_file_t *)hdr;
1156 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1158 if (!file->cache_file)
1160 *size = 0;
1161 return ERROR_INTERNET_ITEM_NOT_FOUND;
1163 if (unicode)
1165 required = (lstrlenW(file->cache_file) + 1) * sizeof(WCHAR);
1166 if (*size < required)
1167 return ERROR_INSUFFICIENT_BUFFER;
1169 *size = required;
1170 memcpy(buffer, file->cache_file, *size);
1171 return ERROR_SUCCESS;
1173 else
1175 required = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, NULL, 0, NULL, NULL);
1176 if (required > *size)
1177 return ERROR_INSUFFICIENT_BUFFER;
1179 *size = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, buffer, *size, NULL, NULL);
1180 return ERROR_SUCCESS;
1184 return INET_QueryOption(hdr, option, buffer, size, unicode);
1187 static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read,
1188 DWORD flags, DWORD_PTR context)
1190 ftp_file_t *file = (ftp_file_t*)hdr;
1191 int res;
1192 DWORD error;
1194 if (file->nDataSocket == -1)
1195 return ERROR_INTERNET_DISCONNECTED;
1197 /* FIXME: FTP should use NETCON_ stuff */
1198 res = sock_recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1199 *read = res>0 ? res : 0;
1201 error = res >= 0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME */
1202 if (error == ERROR_SUCCESS && file->cache_file)
1204 DWORD bytes_written;
1206 if (!WriteFile(file->cache_file_handle, buffer, *read, &bytes_written, NULL))
1207 WARN("WriteFile failed: %u\n", GetLastError());
1209 return error;
1212 static DWORD FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1214 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1215 int res;
1217 res = sock_send(lpwh->nDataSocket, buffer, size, 0);
1219 *written = res>0 ? res : 0;
1220 return res >= 0 ? ERROR_SUCCESS : WSAGetLastError();
1223 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
1225 INTERNET_ASYNC_RESULT iar;
1226 BYTE buffer[4096];
1227 int available;
1229 TRACE("%p\n", file);
1231 available = sock_recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1233 if(available != -1) {
1234 iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1235 iar.dwError = first_notif ? 0 : available;
1236 }else {
1237 iar.dwResult = 0;
1238 iar.dwError = INTERNET_GetLastError();
1241 INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1242 sizeof(INTERNET_ASYNC_RESULT));
1245 static void FTPFILE_AsyncQueryDataAvailableProc(task_header_t *task)
1247 ftp_file_t *file = (ftp_file_t*)task->hdr;
1249 FTP_ReceiveRequestData(file, FALSE);
1252 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1254 ftp_file_t *file = (ftp_file_t*) hdr;
1255 ULONG unread = 0;
1256 int retval;
1258 TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
1260 retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
1261 if (!retval)
1262 TRACE("%d bytes of queued, but unread data\n", unread);
1264 *available = unread;
1266 if(!unread) {
1267 BYTE byte;
1269 *available = 0;
1271 retval = sock_recv(file->nDataSocket, &byte, 1, MSG_PEEK);
1272 if(retval > 0) {
1273 task_header_t *task;
1275 task = alloc_async_task(&file->hdr, FTPFILE_AsyncQueryDataAvailableProc, sizeof(*task));
1276 INTERNET_AsyncCall(task);
1278 return ERROR_IO_PENDING;
1282 return ERROR_SUCCESS;
1285 static DWORD FTPFILE_LockRequestFile(object_header_t *hdr, req_file_t **ret)
1287 ftp_file_t *file = (ftp_file_t*)hdr;
1288 FIXME("%p\n", file);
1289 return ERROR_NOT_SUPPORTED;
1292 static const object_vtbl_t FTPFILEVtbl = {
1293 FTPFILE_Destroy,
1294 NULL,
1295 FTPFILE_QueryOption,
1296 INET_SetOption,
1297 FTPFILE_ReadFile,
1298 FTPFILE_WriteFile,
1299 FTPFILE_QueryDataAvailable,
1300 NULL,
1301 FTPFILE_LockRequestFile
1304 /***********************************************************************
1305 * FTP_FtpOpenFileW (Internal)
1307 * Open a remote file for writing or reading
1309 * RETURNS
1310 * HINTERNET handle on success
1311 * NULL on failure
1314 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1315 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1316 DWORD_PTR dwContext)
1318 INT nDataSocket;
1319 BOOL bSuccess = FALSE;
1320 ftp_file_t *lpwh = NULL;
1321 appinfo_t *hIC = NULL;
1323 TRACE("\n");
1325 /* Clear any error information */
1326 INTERNET_SetLastError(0);
1328 if (GENERIC_READ == fdwAccess)
1330 /* Set up socket to retrieve data */
1331 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1333 else if (GENERIC_WRITE == fdwAccess)
1335 /* Set up socket to send data */
1336 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1339 /* Get data socket to server */
1340 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1342 lpwh = alloc_object(&lpwfs->hdr, &FTPFILEVtbl, sizeof(ftp_file_t));
1343 lpwh->hdr.htype = WH_HFILE;
1344 lpwh->hdr.dwFlags = dwFlags;
1345 lpwh->hdr.dwContext = dwContext;
1346 lpwh->nDataSocket = nDataSocket;
1347 lpwh->cache_file = NULL;
1348 lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1349 lpwh->session_deleted = FALSE;
1351 WININET_AddRef( &lpwfs->hdr );
1352 lpwh->lpFtpSession = lpwfs;
1353 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1355 /* Indicate that a download is currently in progress */
1356 lpwfs->download_in_progress = lpwh;
1359 if (lpwfs->lstnSocket != -1)
1361 closesocket(lpwfs->lstnSocket);
1362 lpwfs->lstnSocket = -1;
1365 if (bSuccess && fdwAccess == GENERIC_READ)
1367 WCHAR filename[MAX_PATH + 1];
1368 URL_COMPONENTSW uc;
1369 DWORD len;
1371 memset(&uc, 0, sizeof(uc));
1372 uc.dwStructSize = sizeof(uc);
1373 uc.nScheme = INTERNET_SCHEME_FTP;
1374 uc.lpszHostName = lpwfs->servername;
1375 uc.nPort = lpwfs->serverport;
1376 uc.lpszUserName = lpwfs->lpszUserName;
1377 uc.lpszUrlPath = heap_strdupW(lpszFileName);
1379 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1381 WCHAR *url = heap_alloc(len * sizeof(WCHAR));
1383 if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1385 lpwh->cache_file = heap_strdupW(filename);
1386 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1387 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1388 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1390 WARN("Could not create cache file: %u\n", GetLastError());
1391 heap_free(lpwh->cache_file);
1392 lpwh->cache_file = NULL;
1395 heap_free(url);
1397 heap_free(uc.lpszUrlPath);
1400 hIC = lpwfs->lpAppInfo;
1401 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1403 INTERNET_ASYNC_RESULT iar;
1405 if (lpwh)
1407 iar.dwResult = (DWORD_PTR)lpwh->hdr.hInternet;
1408 iar.dwError = ERROR_SUCCESS;
1409 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1410 &iar, sizeof(INTERNET_ASYNC_RESULT));
1413 if(bSuccess) {
1414 FTP_ReceiveRequestData(lpwh, TRUE);
1415 }else {
1416 iar.dwResult = 0;
1417 iar.dwError = INTERNET_GetLastError();
1418 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1419 &iar, sizeof(INTERNET_ASYNC_RESULT));
1423 if(!bSuccess)
1424 return FALSE;
1426 return lpwh->hdr.hInternet;
1430 /***********************************************************************
1431 * FtpOpenFileA (WININET.@)
1433 * Open a remote file for writing or reading
1435 * RETURNS
1436 * HINTERNET handle on success
1437 * NULL on failure
1440 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1441 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1442 DWORD_PTR dwContext)
1444 LPWSTR lpwzFileName;
1445 HINTERNET ret;
1447 lpwzFileName = heap_strdupAtoW(lpszFileName);
1448 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1449 heap_free(lpwzFileName);
1450 return ret;
1453 typedef struct {
1454 task_header_t hdr;
1455 WCHAR *file_name;
1456 DWORD access;
1457 DWORD flags;
1458 DWORD_PTR context;
1459 } open_file_task_t;
1461 static void AsyncFtpOpenFileProc(task_header_t *hdr)
1463 open_file_task_t *task = (open_file_task_t*)hdr;
1464 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1466 TRACE("%p\n", session);
1468 FTP_FtpOpenFileW(session, task->file_name, task->access, task->flags, task->context);
1469 heap_free(task->file_name);
1472 /***********************************************************************
1473 * FtpOpenFileW (WININET.@)
1475 * Open a remote file for writing or reading
1477 * RETURNS
1478 * HINTERNET handle on success
1479 * NULL on failure
1482 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1483 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1484 DWORD_PTR dwContext)
1486 ftp_session_t *lpwfs;
1487 appinfo_t *hIC = NULL;
1488 HINTERNET r = NULL;
1490 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1491 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1493 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1494 if (!lpwfs)
1496 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1497 return FALSE;
1500 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1502 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1503 goto lend;
1506 if ((!lpszFileName) ||
1507 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1508 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1510 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1511 goto lend;
1514 if (lpwfs->download_in_progress != NULL)
1516 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1517 goto lend;
1520 hIC = lpwfs->lpAppInfo;
1521 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1523 open_file_task_t *task;
1525 task = alloc_async_task(&lpwfs->hdr, AsyncFtpOpenFileProc, sizeof(*task));
1526 task->file_name = heap_strdupW(lpszFileName);
1527 task->access = fdwAccess;
1528 task->flags = dwFlags;
1529 task->context = dwContext;
1531 INTERNET_AsyncCall(&task->hdr);
1532 r = NULL;
1534 else
1536 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1539 lend:
1540 WININET_Release( &lpwfs->hdr );
1542 return r;
1546 /***********************************************************************
1547 * FtpGetFileA (WININET.@)
1549 * Retrieve file from the FTP server
1551 * RETURNS
1552 * TRUE on success
1553 * FALSE on failure
1556 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1557 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1558 DWORD_PTR dwContext)
1560 LPWSTR lpwzRemoteFile;
1561 LPWSTR lpwzNewFile;
1562 BOOL ret;
1564 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1565 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1566 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1567 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1568 heap_free(lpwzRemoteFile);
1569 heap_free(lpwzNewFile);
1570 return ret;
1573 typedef struct {
1574 task_header_t hdr;
1575 WCHAR *remote_file;
1576 WCHAR *new_file;
1577 BOOL fail_if_exists;
1578 DWORD local_attr;
1579 DWORD flags;
1580 DWORD_PTR context;
1581 } get_file_task_t;
1583 static void AsyncFtpGetFileProc(task_header_t *hdr)
1585 get_file_task_t *task = (get_file_task_t*)hdr;
1586 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1588 TRACE("%p\n", session);
1590 FTP_FtpGetFileW(session, task->remote_file, task->new_file, task->fail_if_exists,
1591 task->local_attr, task->flags, task->context);
1592 heap_free(task->remote_file);
1593 heap_free(task->new_file);
1597 /***********************************************************************
1598 * FtpGetFileW (WININET.@)
1600 * Retrieve file from the FTP server
1602 * RETURNS
1603 * TRUE on success
1604 * FALSE on failure
1607 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1608 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1609 DWORD_PTR dwContext)
1611 ftp_session_t *lpwfs;
1612 appinfo_t *hIC = NULL;
1613 BOOL r = FALSE;
1615 if (!lpszRemoteFile || !lpszNewFile)
1617 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1618 return FALSE;
1621 lpwfs = (ftp_session_t*) get_handle_object( hInternet );
1622 if (!lpwfs)
1624 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1625 return FALSE;
1628 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1630 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1631 goto lend;
1634 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1636 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1637 goto lend;
1640 if (lpwfs->download_in_progress != NULL)
1642 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1643 goto lend;
1646 hIC = lpwfs->lpAppInfo;
1647 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1649 get_file_task_t *task;
1651 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetFileProc, sizeof(*task));
1652 task->remote_file = heap_strdupW(lpszRemoteFile);
1653 task->new_file = heap_strdupW(lpszNewFile);
1654 task->local_attr = dwLocalFlagsAttribute;
1655 task->fail_if_exists = fFailIfExists;
1656 task->flags = dwInternetFlags;
1657 task->context = dwContext;
1659 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1661 else
1663 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1664 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1667 lend:
1668 WININET_Release( &lpwfs->hdr );
1670 return r;
1674 /***********************************************************************
1675 * FTP_FtpGetFileW (Internal)
1677 * Retrieve file from the FTP server
1679 * RETURNS
1680 * TRUE on success
1681 * FALSE on failure
1684 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1685 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1686 DWORD_PTR dwContext)
1688 BOOL bSuccess = FALSE;
1689 HANDLE hFile;
1690 appinfo_t *hIC = NULL;
1692 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1694 /* Clear any error information */
1695 INTERNET_SetLastError(0);
1697 /* Ensure we can write to lpszNewfile by opening it */
1698 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1699 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1700 if (INVALID_HANDLE_VALUE == hFile)
1701 return FALSE;
1703 /* Set up socket to retrieve data */
1704 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1706 INT nDataSocket;
1708 /* Get data socket to server */
1709 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1711 INT nResCode;
1713 /* Receive data */
1714 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1715 closesocket(nDataSocket);
1717 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1718 if (nResCode)
1720 if (nResCode == 226)
1721 bSuccess = TRUE;
1722 else
1723 FTP_SetResponseError(nResCode);
1728 if (lpwfs->lstnSocket != -1)
1730 closesocket(lpwfs->lstnSocket);
1731 lpwfs->lstnSocket = -1;
1734 CloseHandle(hFile);
1736 hIC = lpwfs->lpAppInfo;
1737 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1739 INTERNET_ASYNC_RESULT iar;
1741 iar.dwResult = (DWORD)bSuccess;
1742 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1743 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1744 &iar, sizeof(INTERNET_ASYNC_RESULT));
1747 if (!bSuccess) DeleteFileW(lpszNewFile);
1748 return bSuccess;
1751 /***********************************************************************
1752 * FtpGetFileSize (WININET.@)
1754 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1756 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1758 if (lpdwFileSizeHigh)
1759 *lpdwFileSizeHigh = 0;
1761 return 0;
1764 /***********************************************************************
1765 * FtpDeleteFileA (WININET.@)
1767 * Delete a file on the ftp server
1769 * RETURNS
1770 * TRUE on success
1771 * FALSE on failure
1774 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1776 LPWSTR lpwzFileName;
1777 BOOL ret;
1779 lpwzFileName = heap_strdupAtoW(lpszFileName);
1780 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1781 heap_free(lpwzFileName);
1782 return ret;
1785 typedef struct {
1786 task_header_t hdr;
1787 WCHAR *file_name;
1788 } delete_file_task_t;
1790 static void AsyncFtpDeleteFileProc(task_header_t *hdr)
1792 delete_file_task_t *task = (delete_file_task_t*)hdr;
1793 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1795 TRACE("%p\n", session);
1797 FTP_FtpDeleteFileW(session, task->file_name);
1798 heap_free(task->file_name);
1801 /***********************************************************************
1802 * FtpDeleteFileW (WININET.@)
1804 * Delete a file on the ftp server
1806 * RETURNS
1807 * TRUE on success
1808 * FALSE on failure
1811 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1813 ftp_session_t *lpwfs;
1814 appinfo_t *hIC = NULL;
1815 BOOL r = FALSE;
1817 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1818 if (!lpwfs)
1820 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1821 return FALSE;
1824 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1826 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1827 goto lend;
1830 if (lpwfs->download_in_progress != NULL)
1832 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1833 goto lend;
1836 if (!lpszFileName)
1838 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1839 goto lend;
1842 hIC = lpwfs->lpAppInfo;
1843 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1845 delete_file_task_t *task;
1847 task = alloc_async_task(&lpwfs->hdr, AsyncFtpDeleteFileProc, sizeof(*task));
1848 task->file_name = heap_strdupW(lpszFileName);
1850 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1852 else
1854 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1857 lend:
1858 WININET_Release( &lpwfs->hdr );
1860 return r;
1863 /***********************************************************************
1864 * FTP_FtpDeleteFileW (Internal)
1866 * Delete a file on the ftp server
1868 * RETURNS
1869 * TRUE on success
1870 * FALSE on failure
1873 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1875 INT nResCode;
1876 BOOL bSuccess = FALSE;
1877 appinfo_t *hIC = NULL;
1879 TRACE("%p\n", lpwfs);
1881 /* Clear any error information */
1882 INTERNET_SetLastError(0);
1884 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1885 goto lend;
1887 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1888 if (nResCode)
1890 if (nResCode == 250)
1891 bSuccess = TRUE;
1892 else
1893 FTP_SetResponseError(nResCode);
1895 lend:
1896 hIC = lpwfs->lpAppInfo;
1897 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1899 INTERNET_ASYNC_RESULT iar;
1901 iar.dwResult = (DWORD)bSuccess;
1902 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1903 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1904 &iar, sizeof(INTERNET_ASYNC_RESULT));
1907 return bSuccess;
1911 /***********************************************************************
1912 * FtpRemoveDirectoryA (WININET.@)
1914 * Remove a directory on the ftp server
1916 * RETURNS
1917 * TRUE on success
1918 * FALSE on failure
1921 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1923 LPWSTR lpwzDirectory;
1924 BOOL ret;
1926 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1927 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1928 heap_free(lpwzDirectory);
1929 return ret;
1932 static void AsyncFtpRemoveDirectoryProc(task_header_t *hdr)
1934 directory_task_t *task = (directory_task_t*)hdr;
1935 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1937 TRACE("%p\n", session);
1939 FTP_FtpRemoveDirectoryW(session, task->directory);
1940 heap_free(task->directory);
1943 /***********************************************************************
1944 * FtpRemoveDirectoryW (WININET.@)
1946 * Remove a directory on the ftp server
1948 * RETURNS
1949 * TRUE on success
1950 * FALSE on failure
1953 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1955 ftp_session_t *lpwfs;
1956 appinfo_t *hIC = NULL;
1957 BOOL r = FALSE;
1959 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1960 if (!lpwfs)
1962 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1963 return FALSE;
1966 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1968 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1969 goto lend;
1972 if (lpwfs->download_in_progress != NULL)
1974 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1975 goto lend;
1978 if (!lpszDirectory)
1980 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1981 goto lend;
1984 hIC = lpwfs->lpAppInfo;
1985 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1987 directory_task_t *task;
1989 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRemoveDirectoryProc, sizeof(*task));
1990 task->directory = heap_strdupW(lpszDirectory);
1992 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1994 else
1996 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1999 lend:
2000 WININET_Release( &lpwfs->hdr );
2002 return r;
2005 /***********************************************************************
2006 * FTP_FtpRemoveDirectoryW (Internal)
2008 * Remove a directory on the ftp server
2010 * RETURNS
2011 * TRUE on success
2012 * FALSE on failure
2015 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
2017 INT nResCode;
2018 BOOL bSuccess = FALSE;
2019 appinfo_t *hIC = NULL;
2021 TRACE("\n");
2023 /* Clear any error information */
2024 INTERNET_SetLastError(0);
2026 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2027 goto lend;
2029 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2030 if (nResCode)
2032 if (nResCode == 250)
2033 bSuccess = TRUE;
2034 else
2035 FTP_SetResponseError(nResCode);
2038 lend:
2039 hIC = lpwfs->lpAppInfo;
2040 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2042 INTERNET_ASYNC_RESULT iar;
2044 iar.dwResult = (DWORD)bSuccess;
2045 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2046 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2047 &iar, sizeof(INTERNET_ASYNC_RESULT));
2050 return bSuccess;
2054 /***********************************************************************
2055 * FtpRenameFileA (WININET.@)
2057 * Rename a file on the ftp server
2059 * RETURNS
2060 * TRUE on success
2061 * FALSE on failure
2064 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2066 LPWSTR lpwzSrc;
2067 LPWSTR lpwzDest;
2068 BOOL ret;
2070 lpwzSrc = heap_strdupAtoW(lpszSrc);
2071 lpwzDest = heap_strdupAtoW(lpszDest);
2072 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2073 heap_free(lpwzSrc);
2074 heap_free(lpwzDest);
2075 return ret;
2078 typedef struct {
2079 task_header_t hdr;
2080 WCHAR *src_file;
2081 WCHAR *dst_file;
2082 } rename_file_task_t;
2084 static void AsyncFtpRenameFileProc(task_header_t *hdr)
2086 rename_file_task_t *task = (rename_file_task_t*)hdr;
2087 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
2089 TRACE("%p\n", session);
2091 FTP_FtpRenameFileW(session, task->src_file, task->dst_file);
2092 heap_free(task->src_file);
2093 heap_free(task->dst_file);
2096 /***********************************************************************
2097 * FtpRenameFileW (WININET.@)
2099 * Rename a file on the ftp server
2101 * RETURNS
2102 * TRUE on success
2103 * FALSE on failure
2106 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2108 ftp_session_t *lpwfs;
2109 appinfo_t *hIC = NULL;
2110 BOOL r = FALSE;
2112 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
2113 if (!lpwfs)
2115 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2116 return FALSE;
2119 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2121 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2122 goto lend;
2125 if (lpwfs->download_in_progress != NULL)
2127 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2128 goto lend;
2131 if (!lpszSrc || !lpszDest)
2133 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2134 goto lend;
2137 hIC = lpwfs->lpAppInfo;
2138 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2140 rename_file_task_t *task;
2142 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRenameFileProc, sizeof(*task));
2143 task->src_file = heap_strdupW(lpszSrc);
2144 task->dst_file = heap_strdupW(lpszDest);
2146 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
2148 else
2150 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2153 lend:
2154 WININET_Release( &lpwfs->hdr );
2156 return r;
2159 /***********************************************************************
2160 * FTP_FtpRenameFileW (Internal)
2162 * Rename a file on the ftp server
2164 * RETURNS
2165 * TRUE on success
2166 * FALSE on failure
2169 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2171 INT nResCode;
2172 BOOL bSuccess = FALSE;
2173 appinfo_t *hIC = NULL;
2175 TRACE("\n");
2177 /* Clear any error information */
2178 INTERNET_SetLastError(0);
2180 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2181 goto lend;
2183 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2184 if (nResCode == 350)
2186 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2187 goto lend;
2189 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2192 if (nResCode == 250)
2193 bSuccess = TRUE;
2194 else
2195 FTP_SetResponseError(nResCode);
2197 lend:
2198 hIC = lpwfs->lpAppInfo;
2199 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2201 INTERNET_ASYNC_RESULT iar;
2203 iar.dwResult = (DWORD)bSuccess;
2204 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2205 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2206 &iar, sizeof(INTERNET_ASYNC_RESULT));
2209 return bSuccess;
2212 /***********************************************************************
2213 * FtpCommandA (WININET.@)
2215 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2216 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2218 BOOL r;
2219 WCHAR *cmdW;
2221 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2222 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2224 if (fExpectResponse)
2226 FIXME("data connection not supported\n");
2227 return FALSE;
2230 if (!lpszCommand || !lpszCommand[0])
2232 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2233 return FALSE;
2236 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2238 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2239 return FALSE;
2242 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2244 heap_free(cmdW);
2245 return r;
2248 /***********************************************************************
2249 * FtpCommandW (WININET.@)
2251 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2252 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2254 BOOL r = FALSE;
2255 ftp_session_t *lpwfs;
2256 LPSTR cmd = NULL;
2257 DWORD len, nBytesSent= 0;
2258 INT nResCode, nRC = 0;
2260 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2261 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2263 if (!lpszCommand || !lpszCommand[0])
2265 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2266 return FALSE;
2269 if (fExpectResponse)
2271 FIXME("data connection not supported\n");
2272 return FALSE;
2275 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
2276 if (!lpwfs)
2278 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2279 return FALSE;
2282 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2284 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2285 goto lend;
2288 if (lpwfs->download_in_progress != NULL)
2290 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2291 goto lend;
2294 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2295 if ((cmd = heap_alloc(len)))
2296 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2297 else
2299 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2300 goto lend;
2303 strcat(cmd, szCRLF);
2304 len--;
2306 TRACE("Sending (%s) len(%d)\n", debugstr_a(cmd), len);
2307 while ((nBytesSent < len) && (nRC != -1))
2309 nRC = sock_send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2310 if (nRC != -1)
2312 nBytesSent += nRC;
2313 TRACE("Sent %d bytes\n", nRC);
2317 if (nBytesSent)
2319 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2320 if (nResCode > 0 && nResCode < 400)
2321 r = TRUE;
2322 else
2323 FTP_SetResponseError(nResCode);
2326 lend:
2327 WININET_Release( &lpwfs->hdr );
2328 heap_free( cmd );
2329 return r;
2333 /***********************************************************************
2334 * FTPSESSION_Destroy (internal)
2336 * Deallocate session handle
2338 static void FTPSESSION_Destroy(object_header_t *hdr)
2340 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2342 TRACE("\n");
2344 WININET_Release(&lpwfs->lpAppInfo->hdr);
2346 heap_free(lpwfs->lpszPassword);
2347 heap_free(lpwfs->lpszUserName);
2348 heap_free(lpwfs->servername);
2351 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2353 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2355 TRACE("\n");
2357 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2358 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2360 if (lpwfs->download_in_progress != NULL)
2361 lpwfs->download_in_progress->session_deleted = TRUE;
2363 if (lpwfs->sndSocket != -1)
2364 closesocket(lpwfs->sndSocket);
2366 if (lpwfs->lstnSocket != -1)
2367 closesocket(lpwfs->lstnSocket);
2369 if (lpwfs->pasvSocket != -1)
2370 closesocket(lpwfs->pasvSocket);
2372 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2373 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2376 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2378 switch(option) {
2379 case INTERNET_OPTION_HANDLE_TYPE:
2380 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2382 if (*size < sizeof(ULONG))
2383 return ERROR_INSUFFICIENT_BUFFER;
2385 *size = sizeof(DWORD);
2386 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2387 return ERROR_SUCCESS;
2390 return INET_QueryOption(hdr, option, buffer, size, unicode);
2393 static const object_vtbl_t FTPSESSIONVtbl = {
2394 FTPSESSION_Destroy,
2395 FTPSESSION_CloseConnection,
2396 FTPSESSION_QueryOption,
2397 INET_SetOption,
2398 NULL,
2399 NULL,
2400 NULL,
2401 NULL
2405 /***********************************************************************
2406 * FTP_Connect (internal)
2408 * Connect to a ftp server
2410 * RETURNS
2411 * HINTERNET a session handle on success
2412 * NULL on failure
2414 * NOTES:
2416 * Windows uses 'anonymous' as the username, when given a NULL username
2417 * and a NULL password. The password is first looked up in:
2419 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2421 * If this entry is not present it uses the current username as the password.
2425 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2426 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2427 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2428 DWORD dwInternalFlags)
2430 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2431 'M','i','c','r','o','s','o','f','t','\\',
2432 'W','i','n','d','o','w','s','\\',
2433 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2434 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2435 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2436 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2437 static const WCHAR szEmpty[] = {'\0'};
2438 struct sockaddr_in socketAddr;
2439 INT nsocket = -1;
2440 socklen_t sock_namelen;
2441 BOOL bSuccess = FALSE;
2442 ftp_session_t *lpwfs = NULL;
2443 char szaddr[INET6_ADDRSTRLEN];
2445 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2446 hIC, debugstr_w(lpszServerName),
2447 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2449 assert( hIC->hdr.htype == WH_HINIT );
2451 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2453 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2454 return NULL;
2457 lpwfs = alloc_object(&hIC->hdr, &FTPSESSIONVtbl, sizeof(ftp_session_t));
2458 if (NULL == lpwfs)
2460 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2461 return NULL;
2464 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2465 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2466 else
2467 lpwfs->serverport = nServerPort;
2469 lpwfs->hdr.htype = WH_HFTPSESSION;
2470 lpwfs->hdr.dwFlags = dwFlags;
2471 lpwfs->hdr.dwContext = dwContext;
2472 lpwfs->hdr.dwInternalFlags |= dwInternalFlags;
2473 lpwfs->download_in_progress = NULL;
2474 lpwfs->sndSocket = -1;
2475 lpwfs->lstnSocket = -1;
2476 lpwfs->pasvSocket = -1;
2478 WININET_AddRef( &hIC->hdr );
2479 lpwfs->lpAppInfo = hIC;
2480 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2482 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
2483 if(strchrW(hIC->proxy, ' '))
2484 FIXME("Several proxies not implemented.\n");
2485 if(hIC->proxyBypass)
2486 FIXME("Proxy bypass is ignored.\n");
2488 if (!lpszUserName || !lpszUserName[0]) {
2489 HKEY key;
2490 WCHAR szPassword[MAX_PATH];
2491 DWORD len = sizeof(szPassword);
2493 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2495 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2496 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2497 /* Nothing in the registry, get the username and use that as the password */
2498 if (!GetUserNameW(szPassword, &len)) {
2499 /* Should never get here, but use an empty password as failsafe */
2500 strcpyW(szPassword, szEmpty);
2503 RegCloseKey(key);
2505 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2506 lpwfs->lpszPassword = heap_strdupW(szPassword);
2508 else {
2509 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2510 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2512 lpwfs->servername = heap_strdupW(lpszServerName);
2514 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2515 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2517 INTERNET_ASYNC_RESULT iar;
2519 iar.dwResult = (DWORD_PTR)lpwfs->hdr.hInternet;
2520 iar.dwError = ERROR_SUCCESS;
2522 INTERNET_SendCallback(&hIC->hdr, dwContext,
2523 INTERNET_STATUS_HANDLE_CREATED, &iar,
2524 sizeof(INTERNET_ASYNC_RESULT));
2527 INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2528 (LPWSTR) lpszServerName, (strlenW(lpszServerName)+1) * sizeof(WCHAR));
2530 sock_namelen = sizeof(socketAddr);
2531 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen, szaddr))
2533 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2534 goto lerror;
2537 if (socketAddr.sin_family != AF_INET)
2539 WARN("unsupported address family %d\n", socketAddr.sin_family);
2540 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2541 goto lerror;
2544 INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2545 szaddr, strlen(szaddr)+1);
2547 init_winsock();
2548 nsocket = socket(AF_INET,SOCK_STREAM,0);
2549 if (nsocket == -1)
2551 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2552 goto lerror;
2555 INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2556 szaddr, strlen(szaddr)+1);
2558 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2560 ERR("Unable to connect (%d)\n", WSAGetLastError());
2561 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2562 closesocket(nsocket);
2564 else
2566 TRACE("Connected to server\n");
2567 lpwfs->sndSocket = nsocket;
2568 INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2569 szaddr, strlen(szaddr)+1);
2571 sock_namelen = sizeof(lpwfs->socketAddress);
2572 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2574 if (FTP_ConnectToHost(lpwfs))
2576 TRACE("Successfully logged into server\n");
2577 bSuccess = TRUE;
2581 lerror:
2582 if (!bSuccess)
2584 if(lpwfs)
2585 WININET_Release( &lpwfs->hdr );
2586 return NULL;
2589 return lpwfs->hdr.hInternet;
2593 /***********************************************************************
2594 * FTP_ConnectToHost (internal)
2596 * Connect to a ftp server
2598 * RETURNS
2599 * TRUE on success
2600 * NULL on failure
2603 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2605 INT nResCode;
2606 BOOL bSuccess = FALSE;
2608 TRACE("\n");
2609 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2611 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2612 goto lend;
2614 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2615 if (nResCode)
2617 /* Login successful... */
2618 if (nResCode == 230)
2619 bSuccess = TRUE;
2620 /* User name okay, need password... */
2621 else if (nResCode == 331)
2622 bSuccess = FTP_SendPassword(lpwfs);
2623 /* Need account for login... */
2624 else if (nResCode == 332)
2625 bSuccess = FTP_SendAccount(lpwfs);
2626 else
2627 FTP_SetResponseError(nResCode);
2630 TRACE("Returning %d\n", bSuccess);
2631 lend:
2632 return bSuccess;
2635 /***********************************************************************
2636 * FTP_GetNextLine (internal)
2638 * Parse next line in directory string listing
2640 * RETURNS
2641 * Pointer to beginning of next line
2642 * NULL on failure
2646 static LPSTR FTP_GetNextLine(INT nSocket, LPDWORD dwLen)
2648 struct timeval tv = {RESPONSE_TIMEOUT,0};
2649 FD_SET set;
2650 INT nRecv = 0;
2651 LPSTR lpszBuffer = INTERNET_GetResponseBuffer();
2653 TRACE("\n");
2655 FD_ZERO(&set);
2656 FD_SET(nSocket, &set);
2658 while (nRecv < MAX_REPLY_LEN)
2660 if (select(nSocket+1, &set, NULL, NULL, &tv) > 0)
2662 if (sock_recv(nSocket, &lpszBuffer[nRecv], 1, 0) <= 0)
2664 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2665 return NULL;
2668 if (lpszBuffer[nRecv] == '\n')
2670 lpszBuffer[nRecv] = '\0';
2671 *dwLen = nRecv - 1;
2672 TRACE(":%d %s\n", nRecv, lpszBuffer);
2673 return lpszBuffer;
2675 if (lpszBuffer[nRecv] != '\r')
2676 nRecv++;
2678 else
2680 INTERNET_SetLastError(ERROR_INTERNET_TIMEOUT);
2681 return NULL;
2685 return NULL;
2688 /***********************************************************************
2689 * FTP_SendCommandA (internal)
2691 * Send command to server
2693 * RETURNS
2694 * TRUE on success
2695 * NULL on failure
2698 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2699 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2701 DWORD len;
2702 CHAR *buf;
2703 DWORD nBytesSent = 0;
2704 int nRC = 0;
2705 DWORD dwParamLen;
2707 TRACE("%d: (%s) %d\n", ftpCmd, debugstr_a(lpszParam), nSocket);
2709 if (lpfnStatusCB)
2711 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2714 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2715 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2716 if (NULL == (buf = heap_alloc(len+1)))
2718 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2719 return FALSE;
2721 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2722 dwParamLen ? lpszParam : "", szCRLF);
2724 TRACE("Sending (%s) len(%d)\n", debugstr_a(buf), len);
2725 while((nBytesSent < len) && (nRC != -1))
2727 nRC = sock_send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2728 nBytesSent += nRC;
2730 heap_free(buf);
2732 if (lpfnStatusCB)
2734 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2735 &nBytesSent, sizeof(DWORD));
2738 TRACE("Sent %d bytes\n", nBytesSent);
2739 return (nRC != -1);
2742 /***********************************************************************
2743 * FTP_SendCommand (internal)
2745 * Send command to server
2747 * RETURNS
2748 * TRUE on success
2749 * NULL on failure
2752 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2753 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2755 BOOL ret;
2756 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2757 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2758 heap_free(lpszParamA);
2759 return ret;
2762 /***********************************************************************
2763 * FTP_ReceiveResponse (internal)
2765 * Receive response from server
2767 * RETURNS
2768 * Reply code on success
2769 * 0 on failure
2772 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2774 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2775 DWORD nRecv;
2776 INT rc = 0;
2777 char firstprefix[5];
2778 BOOL multiline = FALSE;
2780 TRACE("socket(%d)\n", lpwfs->sndSocket);
2782 INTERNET_SendCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2784 while(1)
2786 if (!FTP_GetNextLine(lpwfs->sndSocket, &nRecv))
2787 goto lerror;
2789 if (nRecv >= 3)
2791 if(!multiline)
2793 if(lpszResponse[3] != '-')
2794 break;
2795 else
2796 { /* Start of multiline response. Loop until we get "nnn " */
2797 multiline = TRUE;
2798 memcpy(firstprefix, lpszResponse, 3);
2799 firstprefix[3] = ' ';
2800 firstprefix[4] = '\0';
2803 else
2805 if(!memcmp(firstprefix, lpszResponse, 4))
2806 break;
2811 if (nRecv >= 3)
2813 rc = atoi(lpszResponse);
2815 INTERNET_SendCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2816 &nRecv, sizeof(DWORD));
2819 lerror:
2820 TRACE("return %d\n", rc);
2821 return rc;
2825 /***********************************************************************
2826 * FTP_SendPassword (internal)
2828 * Send password to ftp server
2830 * RETURNS
2831 * TRUE on success
2832 * NULL on failure
2835 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2837 INT nResCode;
2838 BOOL bSuccess = FALSE;
2840 TRACE("\n");
2841 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2842 goto lend;
2844 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2845 if (nResCode)
2847 TRACE("Received reply code %d\n", nResCode);
2848 /* Login successful... */
2849 if (nResCode == 230)
2850 bSuccess = TRUE;
2851 /* Command not implemented, superfluous at the server site... */
2852 /* Need account for login... */
2853 else if (nResCode == 332)
2854 bSuccess = FTP_SendAccount(lpwfs);
2855 else
2856 FTP_SetResponseError(nResCode);
2859 lend:
2860 TRACE("Returning %d\n", bSuccess);
2861 return bSuccess;
2865 /***********************************************************************
2866 * FTP_SendAccount (internal)
2870 * RETURNS
2871 * TRUE on success
2872 * FALSE on failure
2875 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2877 INT nResCode;
2878 BOOL bSuccess = FALSE;
2880 TRACE("\n");
2881 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2882 goto lend;
2884 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2885 if (nResCode)
2886 bSuccess = TRUE;
2887 else
2888 FTP_SetResponseError(nResCode);
2890 lend:
2891 return bSuccess;
2895 /***********************************************************************
2896 * FTP_SendStore (internal)
2898 * Send request to upload file to ftp server
2900 * RETURNS
2901 * TRUE on success
2902 * FALSE on failure
2905 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2907 INT nResCode;
2908 BOOL bSuccess = FALSE;
2910 TRACE("\n");
2911 if (!FTP_InitListenSocket(lpwfs))
2912 goto lend;
2914 if (!FTP_SendType(lpwfs, dwType))
2915 goto lend;
2917 if (!FTP_SendPortOrPasv(lpwfs))
2918 goto lend;
2920 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2921 goto lend;
2922 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2923 if (nResCode)
2925 if (nResCode == 150 || nResCode == 125)
2926 bSuccess = TRUE;
2927 else
2928 FTP_SetResponseError(nResCode);
2931 lend:
2932 if (!bSuccess && lpwfs->lstnSocket != -1)
2934 closesocket(lpwfs->lstnSocket);
2935 lpwfs->lstnSocket = -1;
2938 return bSuccess;
2942 /***********************************************************************
2943 * FTP_InitListenSocket (internal)
2945 * Create a socket to listen for server response
2947 * RETURNS
2948 * TRUE on success
2949 * FALSE on failure
2952 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2954 BOOL bSuccess = FALSE;
2955 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2957 TRACE("\n");
2959 init_winsock();
2960 lpwfs->lstnSocket = socket(AF_INET, SOCK_STREAM, 0);
2961 if (lpwfs->lstnSocket == -1)
2963 TRACE("Unable to create listening socket\n");
2964 goto lend;
2967 /* We obtain our ip addr from the name of the command channel socket */
2968 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2970 /* and get the system to assign us a port */
2971 lpwfs->lstnSocketAddress.sin_port = htons(0);
2973 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2975 TRACE("Unable to bind socket\n");
2976 goto lend;
2979 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2981 TRACE("listen failed\n");
2982 goto lend;
2985 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2986 bSuccess = TRUE;
2988 lend:
2989 if (!bSuccess && lpwfs->lstnSocket != -1)
2991 closesocket(lpwfs->lstnSocket);
2992 lpwfs->lstnSocket = -1;
2995 return bSuccess;
2999 /***********************************************************************
3000 * FTP_SendType (internal)
3002 * Tell server type of data being transferred
3004 * RETURNS
3005 * TRUE on success
3006 * FALSE on failure
3008 * W98SE doesn't cache the type that's currently set
3009 * (i.e. it sends it always),
3010 * so we probably don't want to do that either.
3012 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
3014 INT nResCode;
3015 WCHAR type[] = { 'I','\0' };
3016 BOOL bSuccess = FALSE;
3018 TRACE("\n");
3019 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
3020 type[0] = 'A';
3022 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
3023 goto lend;
3025 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
3026 if (nResCode)
3028 if (nResCode == 2)
3029 bSuccess = TRUE;
3030 else
3031 FTP_SetResponseError(nResCode);
3034 lend:
3035 return bSuccess;
3039 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
3040 /***********************************************************************
3041 * FTP_GetFileSize (internal)
3043 * Retrieves from the server the size of the given file
3045 * RETURNS
3046 * TRUE on success
3047 * FALSE on failure
3050 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
3052 INT nResCode;
3053 BOOL bSuccess = FALSE;
3055 TRACE("\n");
3057 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
3058 goto lend;
3060 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3061 if (nResCode)
3063 if (nResCode == 213) {
3064 /* Now parses the output to get the actual file size */
3065 int i;
3066 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3068 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3069 if (lpszResponseBuffer[i] == '\0') return FALSE;
3070 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3072 bSuccess = TRUE;
3073 } else {
3074 FTP_SetResponseError(nResCode);
3078 lend:
3079 return bSuccess;
3081 #endif
3084 /***********************************************************************
3085 * FTP_SendPort (internal)
3087 * Tell server which port to use
3089 * RETURNS
3090 * TRUE on success
3091 * FALSE on failure
3094 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3096 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3097 INT nResCode;
3098 WCHAR szIPAddress[64];
3099 BOOL bSuccess = FALSE;
3100 TRACE("\n");
3102 sprintfW(szIPAddress, szIPFormat,
3103 lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x000000FF,
3104 (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x0000FF00)>>8,
3105 (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x00FF0000)>>16,
3106 (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0xFF000000)>>24,
3107 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3108 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3110 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3111 goto lend;
3113 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3114 if (nResCode)
3116 if (nResCode == 200)
3117 bSuccess = TRUE;
3118 else
3119 FTP_SetResponseError(nResCode);
3122 lend:
3123 return bSuccess;
3127 /***********************************************************************
3128 * FTP_DoPassive (internal)
3130 * Tell server that we want to do passive transfers
3131 * and connect data socket
3133 * RETURNS
3134 * TRUE on success
3135 * FALSE on failure
3138 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3140 INT nResCode;
3141 BOOL bSuccess = FALSE;
3143 TRACE("\n");
3144 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3145 goto lend;
3147 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3148 if (nResCode)
3150 if (nResCode == 227)
3152 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3153 LPSTR p;
3154 int f[6];
3155 int i;
3156 char *pAddr, *pPort;
3157 INT nsocket = -1;
3158 struct sockaddr_in dataSocketAddress;
3160 p = lpszResponseBuffer+4; /* skip status code */
3161 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3163 if (*p == '\0')
3165 ERR("no address found in response, aborting\n");
3166 goto lend;
3169 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3170 &f[4], &f[5]) != 6)
3172 ERR("unknown response address format '%s', aborting\n", p);
3173 goto lend;
3175 for (i=0; i < 6; i++)
3176 f[i] = f[i] & 0xff;
3178 dataSocketAddress = lpwfs->socketAddress;
3179 pAddr = (char *)&(dataSocketAddress.sin_addr.S_un.S_addr);
3180 pPort = (char *)&(dataSocketAddress.sin_port);
3181 pAddr[0] = f[0];
3182 pAddr[1] = f[1];
3183 pAddr[2] = f[2];
3184 pAddr[3] = f[3];
3185 pPort[0] = f[4];
3186 pPort[1] = f[5];
3188 nsocket = socket(AF_INET,SOCK_STREAM,0);
3189 if (nsocket == -1)
3190 goto lend;
3192 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3194 ERR("can't connect passive FTP data port.\n");
3195 closesocket(nsocket);
3196 goto lend;
3198 lpwfs->pasvSocket = nsocket;
3199 bSuccess = TRUE;
3201 else
3202 FTP_SetResponseError(nResCode);
3205 lend:
3206 return bSuccess;
3210 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3212 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3214 if (!FTP_DoPassive(lpwfs))
3215 return FALSE;
3217 else
3219 if (!FTP_SendPort(lpwfs))
3220 return FALSE;
3222 return TRUE;
3226 /***********************************************************************
3227 * FTP_GetDataSocket (internal)
3229 * Either accepts an incoming data socket connection from the server
3230 * or just returns the already opened socket after a PASV command
3231 * in case of passive FTP.
3234 * RETURNS
3235 * TRUE on success
3236 * FALSE on failure
3239 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3241 struct sockaddr_in saddr;
3242 socklen_t addrlen = sizeof(saddr);
3244 TRACE("\n");
3245 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3247 *nDataSocket = lpwfs->pasvSocket;
3248 lpwfs->pasvSocket = -1;
3250 else
3252 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3253 closesocket(lpwfs->lstnSocket);
3254 lpwfs->lstnSocket = -1;
3256 return *nDataSocket != -1;
3260 /***********************************************************************
3261 * FTP_SendData (internal)
3263 * Send data to the server
3265 * RETURNS
3266 * TRUE on success
3267 * FALSE on failure
3270 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3272 BY_HANDLE_FILE_INFORMATION fi;
3273 DWORD nBytesRead = 0;
3274 DWORD nBytesSent = 0;
3275 DWORD nTotalSent = 0;
3276 DWORD nBytesToSend, nLen;
3277 int nRC = 1;
3278 time_t s_long_time, e_long_time;
3279 LONG nSeconds;
3280 CHAR *lpszBuffer;
3282 TRACE("\n");
3283 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3285 /* Get the size of the file. */
3286 GetFileInformationByHandle(hFile, &fi);
3287 time(&s_long_time);
3291 nBytesToSend = nBytesRead - nBytesSent;
3293 if (nBytesToSend <= 0)
3295 /* Read data from file. */
3296 nBytesSent = 0;
3297 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3298 ERR("Failed reading from file\n");
3300 if (nBytesRead > 0)
3301 nBytesToSend = nBytesRead;
3302 else
3303 break;
3306 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3307 DATA_PACKET_SIZE : nBytesToSend;
3308 nRC = sock_send(nDataSocket, lpszBuffer, nLen, 0);
3310 if (nRC != -1)
3312 nBytesSent += nRC;
3313 nTotalSent += nRC;
3316 /* Do some computation to display the status. */
3317 time(&e_long_time);
3318 nSeconds = e_long_time - s_long_time;
3319 if( nSeconds / 60 > 0 )
3321 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3322 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3323 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3325 else
3327 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3328 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3329 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3331 } while (nRC != -1);
3333 TRACE("file transfer complete!\n");
3335 heap_free(lpszBuffer);
3336 return nTotalSent;
3340 /***********************************************************************
3341 * FTP_SendRetrieve (internal)
3343 * Send request to retrieve a file
3345 * RETURNS
3346 * Number of bytes to be received on success
3347 * 0 on failure
3350 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3352 INT nResCode;
3353 BOOL ret;
3355 TRACE("\n");
3356 if (!(ret = FTP_InitListenSocket(lpwfs)))
3357 goto lend;
3359 if (!(ret = FTP_SendType(lpwfs, dwType)))
3360 goto lend;
3362 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3363 goto lend;
3365 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3366 goto lend;
3368 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3369 if ((nResCode != 125) && (nResCode != 150)) {
3370 /* That means that we got an error getting the file. */
3371 FTP_SetResponseError(nResCode);
3372 ret = FALSE;
3375 lend:
3376 if (!ret && lpwfs->lstnSocket != -1)
3378 closesocket(lpwfs->lstnSocket);
3379 lpwfs->lstnSocket = -1;
3382 return ret;
3386 /***********************************************************************
3387 * FTP_RetrieveData (internal)
3389 * Retrieve data from server
3391 * RETURNS
3392 * TRUE on success
3393 * FALSE on failure
3396 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3398 DWORD nBytesWritten;
3399 INT nRC = 0;
3400 CHAR *lpszBuffer;
3402 TRACE("\n");
3404 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3405 if (NULL == lpszBuffer)
3407 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3408 return FALSE;
3411 while (nRC != -1)
3413 nRC = sock_recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3414 if (nRC != -1)
3416 /* other side closed socket. */
3417 if (nRC == 0)
3418 goto recv_end;
3419 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3423 TRACE("Data transfer complete\n");
3425 recv_end:
3426 heap_free(lpszBuffer);
3427 return (nRC != -1);
3430 /***********************************************************************
3431 * FTPFINDNEXT_Destroy (internal)
3433 * Deallocate session handle
3435 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3437 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3438 DWORD i;
3440 TRACE("\n");
3442 WININET_Release(&lpwfn->lpFtpSession->hdr);
3444 for (i = 0; i < lpwfn->size; i++)
3446 heap_free(lpwfn->lpafp[i].lpszName);
3448 heap_free(lpwfn->lpafp);
3451 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3453 WIN32_FIND_DATAW *find_data = data;
3454 DWORD res = ERROR_SUCCESS;
3456 TRACE("index(%d) size(%d)\n", find->index, find->size);
3458 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3460 if (find->index < find->size) {
3461 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3462 find->index++;
3464 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3465 }else {
3466 res = ERROR_NO_MORE_FILES;
3469 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3471 INTERNET_ASYNC_RESULT iar;
3473 iar.dwResult = (res == ERROR_SUCCESS);
3474 iar.dwError = res;
3476 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3477 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3478 sizeof(INTERNET_ASYNC_RESULT));
3481 return res;
3484 typedef struct {
3485 task_header_t hdr;
3486 WIN32_FIND_DATAW *find_data;
3487 } find_next_task_t;
3489 static void FTPFINDNEXT_AsyncFindNextFileProc(task_header_t *hdr)
3491 find_next_task_t *task = (find_next_task_t*)hdr;
3493 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)task->hdr.hdr, task->find_data);
3496 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3498 switch(option) {
3499 case INTERNET_OPTION_HANDLE_TYPE:
3500 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3502 if (*size < sizeof(ULONG))
3503 return ERROR_INSUFFICIENT_BUFFER;
3505 *size = sizeof(DWORD);
3506 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3507 return ERROR_SUCCESS;
3510 return INET_QueryOption(hdr, option, buffer, size, unicode);
3513 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3515 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3517 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3519 find_next_task_t *task;
3521 task = alloc_async_task(&find->hdr, FTPFINDNEXT_AsyncFindNextFileProc, sizeof(*task));
3522 task->find_data = data;
3524 INTERNET_AsyncCall(&task->hdr);
3525 return ERROR_SUCCESS;
3528 return FTPFINDNEXT_FindNextFileProc(find, data);
3531 static const object_vtbl_t FTPFINDNEXTVtbl = {
3532 FTPFINDNEXT_Destroy,
3533 NULL,
3534 FTPFINDNEXT_QueryOption,
3535 INET_SetOption,
3536 NULL,
3537 NULL,
3538 NULL,
3539 FTPFINDNEXT_FindNextFileW
3542 /***********************************************************************
3543 * FTP_ReceiveFileList (internal)
3545 * Read file list from server
3547 * RETURNS
3548 * Handle to file list on success
3549 * NULL on failure
3552 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3553 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3555 DWORD dwSize = 0;
3556 LPFILEPROPERTIESW lpafp = NULL;
3557 LPWININETFTPFINDNEXTW lpwfn = NULL;
3559 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3561 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3563 if(lpFindFileData)
3564 FTP_ConvertFileProp(lpafp, lpFindFileData);
3566 lpwfn = alloc_object(&lpwfs->hdr, &FTPFINDNEXTVtbl, sizeof(WININETFTPFINDNEXTW));
3567 if (lpwfn)
3569 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3570 lpwfn->hdr.dwContext = dwContext;
3571 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3572 lpwfn->size = dwSize;
3573 lpwfn->lpafp = lpafp;
3575 WININET_AddRef( &lpwfs->hdr );
3576 lpwfn->lpFtpSession = lpwfs;
3577 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3581 TRACE("Matched %d files\n", dwSize);
3582 return lpwfn ? lpwfn->hdr.hInternet : NULL;
3586 /***********************************************************************
3587 * FTP_ConvertFileProp (internal)
3589 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3591 * RETURNS
3592 * TRUE on success
3593 * FALSE on failure
3596 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3598 BOOL bSuccess = FALSE;
3600 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3602 if (lpafp)
3604 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3605 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3606 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3608 /* Not all fields are filled in */
3609 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3610 lpFindFileData->nFileSizeLow = lpafp->nSize;
3612 if (lpafp->bIsDirectory)
3613 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3615 if (lpafp->lpszName)
3616 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3618 bSuccess = TRUE;
3621 return bSuccess;
3624 /***********************************************************************
3625 * FTP_ParseNextFile (internal)
3627 * Parse the next line in file listing
3629 * RETURNS
3630 * TRUE on success
3631 * FALSE on failure
3633 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3635 static const char szSpace[] = " \t";
3636 DWORD nBufLen;
3637 char *pszLine;
3638 char *pszToken;
3639 char *pszTmp;
3640 BOOL found = FALSE;
3641 int i;
3643 lpfp->lpszName = NULL;
3644 do {
3645 if(!(pszLine = FTP_GetNextLine(nSocket, &nBufLen)))
3646 return FALSE;
3648 pszToken = strtok(pszLine, szSpace);
3649 /* ls format
3650 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3652 * For instance:
3653 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3655 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3656 if(!FTP_ParsePermission(pszToken, lpfp))
3657 lpfp->bIsDirectory = FALSE;
3658 for(i=0; i<=3; i++) {
3659 if(!(pszToken = strtok(NULL, szSpace)))
3660 break;
3662 if(!pszToken) continue;
3663 if(lpfp->bIsDirectory) {
3664 TRACE("Is directory\n");
3665 lpfp->nSize = 0;
3667 else {
3668 TRACE("Size: %s\n", pszToken);
3669 lpfp->nSize = atol(pszToken);
3672 lpfp->tmLastModified.wSecond = 0;
3673 lpfp->tmLastModified.wMinute = 0;
3674 lpfp->tmLastModified.wHour = 0;
3675 lpfp->tmLastModified.wDay = 0;
3676 lpfp->tmLastModified.wMonth = 0;
3677 lpfp->tmLastModified.wYear = 0;
3679 /* Determine month */
3680 pszToken = strtok(NULL, szSpace);
3681 if(!pszToken) continue;
3682 if(strlen(pszToken) >= 3) {
3683 pszToken[3] = 0;
3684 if((pszTmp = StrStrIA(szMonths, pszToken)))
3685 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3687 /* Determine day */
3688 pszToken = strtok(NULL, szSpace);
3689 if(!pszToken) continue;
3690 lpfp->tmLastModified.wDay = atoi(pszToken);
3691 /* Determine time or year */
3692 pszToken = strtok(NULL, szSpace);
3693 if(!pszToken) continue;
3694 if((pszTmp = strchr(pszToken, ':'))) {
3695 SYSTEMTIME curr_time;
3696 *pszTmp = 0;
3697 pszTmp++;
3698 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3699 lpfp->tmLastModified.wHour = atoi(pszToken);
3700 GetLocalTime( &curr_time );
3701 lpfp->tmLastModified.wYear = curr_time.wYear;
3703 else {
3704 lpfp->tmLastModified.wYear = atoi(pszToken);
3705 lpfp->tmLastModified.wHour = 12;
3707 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3708 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3709 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3711 pszToken = strtok(NULL, szSpace);
3712 if(!pszToken) continue;
3713 lpfp->lpszName = heap_strdupAtoW(pszToken);
3714 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3716 /* NT way of parsing ... :
3718 07-13-03 08:55PM <DIR> sakpatch
3719 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3721 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3722 int mon, mday, year, hour, min;
3723 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3725 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3726 lpfp->tmLastModified.wDay = mday;
3727 lpfp->tmLastModified.wMonth = mon;
3728 lpfp->tmLastModified.wYear = year;
3730 /* Hacky and bad Y2K protection :-) */
3731 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3733 pszToken = strtok(NULL, szSpace);
3734 if(!pszToken) continue;
3735 sscanf(pszToken, "%d:%d", &hour, &min);
3736 lpfp->tmLastModified.wHour = hour;
3737 lpfp->tmLastModified.wMinute = min;
3738 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3739 lpfp->tmLastModified.wHour += 12;
3741 lpfp->tmLastModified.wSecond = 0;
3743 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3744 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3745 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3747 pszToken = strtok(NULL, szSpace);
3748 if(!pszToken) continue;
3749 if(!strcasecmp(pszToken, "<DIR>")) {
3750 lpfp->bIsDirectory = TRUE;
3751 lpfp->nSize = 0;
3752 TRACE("Is directory\n");
3754 else {
3755 lpfp->bIsDirectory = FALSE;
3756 lpfp->nSize = atol(pszToken);
3757 TRACE("Size: %d\n", lpfp->nSize);
3760 pszToken = strtok(NULL, szSpace);
3761 if(!pszToken) continue;
3762 lpfp->lpszName = heap_strdupAtoW(pszToken);
3763 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3765 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3766 else if(pszToken[0] == '+') {
3767 FIXME("EPLF Format not implemented\n");
3770 if(lpfp->lpszName) {
3771 if((lpszSearchFile == NULL) ||
3772 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3773 found = TRUE;
3774 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3776 else {
3777 heap_free(lpfp->lpszName);
3778 lpfp->lpszName = NULL;
3781 } while(!found);
3782 return TRUE;
3785 /***********************************************************************
3786 * FTP_ParseDirectory (internal)
3788 * Parse string of directory information
3790 * RETURNS
3791 * TRUE on success
3792 * FALSE on failure
3794 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3795 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3797 BOOL bSuccess = TRUE;
3798 INT sizeFilePropArray = 500;/*20; */
3799 INT indexFilePropArray = -1;
3801 TRACE("\n");
3803 /* Allocate initial file properties array */
3804 *lpafp = heap_alloc_zero(sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3805 if (!*lpafp)
3806 return FALSE;
3808 do {
3809 if (indexFilePropArray+1 >= sizeFilePropArray)
3811 LPFILEPROPERTIESW tmpafp;
3813 sizeFilePropArray *= 2;
3814 tmpafp = heap_realloc_zero(*lpafp, sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3815 if (NULL == tmpafp)
3817 bSuccess = FALSE;
3818 break;
3821 *lpafp = tmpafp;
3823 indexFilePropArray++;
3824 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3826 if (bSuccess && indexFilePropArray)
3828 if (indexFilePropArray < sizeFilePropArray - 1)
3830 LPFILEPROPERTIESW tmpafp;
3832 tmpafp = heap_realloc(*lpafp, sizeof(FILEPROPERTIESW)*indexFilePropArray);
3833 if (NULL != tmpafp)
3834 *lpafp = tmpafp;
3836 *dwfp = indexFilePropArray;
3838 else
3840 heap_free(*lpafp);
3841 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3842 bSuccess = FALSE;
3845 return bSuccess;
3849 /***********************************************************************
3850 * FTP_ParsePermission (internal)
3852 * Parse permission string of directory information
3854 * RETURNS
3855 * TRUE on success
3856 * FALSE on failure
3859 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3861 BOOL bSuccess = TRUE;
3862 unsigned short nPermission = 0;
3863 INT nPos = 1;
3864 INT nLast = 9;
3866 TRACE("\n");
3867 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3869 bSuccess = FALSE;
3870 return bSuccess;
3873 lpfp->bIsDirectory = (*lpszPermission == 'd');
3876 switch (nPos)
3878 case 1:
3879 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3880 break;
3881 case 2:
3882 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3883 break;
3884 case 3:
3885 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3886 break;
3887 case 4:
3888 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3889 break;
3890 case 5:
3891 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3892 break;
3893 case 6:
3894 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3895 break;
3896 case 7:
3897 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3898 break;
3899 case 8:
3900 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3901 break;
3902 case 9:
3903 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3904 break;
3906 nPos++;
3907 }while (nPos <= nLast);
3909 lpfp->permissions = nPermission;
3910 return bSuccess;
3914 /***********************************************************************
3915 * FTP_SetResponseError (internal)
3917 * Set the appropriate error code for a given response from the server
3919 * RETURNS
3922 static DWORD FTP_SetResponseError(DWORD dwResponse)
3924 DWORD dwCode = 0;
3926 switch(dwResponse)
3928 case 425: /* Cannot open data connection. */
3929 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3930 break;
3932 case 426: /* Connection closed, transer aborted. */
3933 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3934 break;
3936 case 530: /* Not logged in. Login incorrect. */
3937 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3938 break;
3940 case 421: /* Service not available - Server may be shutting down. */
3941 case 450: /* File action not taken. File may be busy. */
3942 case 451: /* Action aborted. Server error. */
3943 case 452: /* Action not taken. Insufficient storage space on server. */
3944 case 500: /* Syntax error. Command unrecognized. */
3945 case 501: /* Syntax error. Error in parameters or arguments. */
3946 case 502: /* Command not implemented. */
3947 case 503: /* Bad sequence of commands. */
3948 case 504: /* Command not implemented for that parameter. */
3949 case 532: /* Need account for storing files */
3950 case 550: /* File action not taken. File not found or no access. */
3951 case 551: /* Requested action aborted. Page type unknown */
3952 case 552: /* Action aborted. Exceeded storage allocation */
3953 case 553: /* Action not taken. File name not allowed. */
3955 default:
3956 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3957 break;
3960 INTERNET_SetLastError(dwCode);
3961 return dwCode;