msvcp: Add _Exp implementation.
[wine/multimedia.git] / dlls / wininet / ftp.c
blob8944c5c10f74ac3a7e6fea5e23277507fa67f500
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 SendAsyncCallback(&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 SendAsyncCallback(&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 SendAsyncCallback(&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 SendAsyncCallback(&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 SendAsyncCallback(&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 SendAsyncCallback(&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 SendAsyncCallback(&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)
1189 ftp_file_t *file = (ftp_file_t*)hdr;
1190 int res;
1191 DWORD error;
1193 if (file->nDataSocket == -1)
1194 return ERROR_INTERNET_DISCONNECTED;
1196 /* FIXME: FTP should use NETCON_ stuff */
1197 res = sock_recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1198 *read = res>0 ? res : 0;
1200 error = res >= 0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME */
1201 if (error == ERROR_SUCCESS && file->cache_file)
1203 DWORD bytes_written;
1205 if (!WriteFile(file->cache_file_handle, buffer, *read, &bytes_written, NULL))
1206 WARN("WriteFile failed: %u\n", GetLastError());
1208 return error;
1211 static DWORD FTPFILE_ReadFileEx(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_size,
1212 DWORD flags, DWORD_PTR context)
1214 return FTPFILE_ReadFile(hdr, buf, size, ret_size);
1217 static DWORD FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1219 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1220 int res;
1222 res = sock_send(lpwh->nDataSocket, buffer, size, 0);
1224 *written = res>0 ? res : 0;
1225 return res >= 0 ? ERROR_SUCCESS : WSAGetLastError();
1228 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
1230 INTERNET_ASYNC_RESULT iar;
1231 BYTE buffer[4096];
1232 int available;
1234 TRACE("%p\n", file);
1236 available = sock_recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1238 if(available != -1) {
1239 iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1240 iar.dwError = first_notif ? 0 : available;
1241 }else {
1242 iar.dwResult = 0;
1243 iar.dwError = INTERNET_GetLastError();
1246 INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1247 sizeof(INTERNET_ASYNC_RESULT));
1250 static void FTPFILE_AsyncQueryDataAvailableProc(task_header_t *task)
1252 ftp_file_t *file = (ftp_file_t*)task->hdr;
1254 FTP_ReceiveRequestData(file, FALSE);
1257 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1259 ftp_file_t *file = (ftp_file_t*) hdr;
1260 ULONG unread = 0;
1261 int retval;
1263 TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
1265 retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
1266 if (!retval)
1267 TRACE("%d bytes of queued, but unread data\n", unread);
1269 *available = unread;
1271 if(!unread) {
1272 BYTE byte;
1274 *available = 0;
1276 retval = sock_recv(file->nDataSocket, &byte, 1, MSG_PEEK);
1277 if(retval > 0) {
1278 task_header_t *task;
1280 task = alloc_async_task(&file->hdr, FTPFILE_AsyncQueryDataAvailableProc, sizeof(*task));
1281 INTERNET_AsyncCall(task);
1283 return ERROR_IO_PENDING;
1287 return ERROR_SUCCESS;
1290 static DWORD FTPFILE_LockRequestFile(object_header_t *hdr, req_file_t **ret)
1292 ftp_file_t *file = (ftp_file_t*)hdr;
1293 FIXME("%p\n", file);
1294 return ERROR_NOT_SUPPORTED;
1297 static const object_vtbl_t FTPFILEVtbl = {
1298 FTPFILE_Destroy,
1299 NULL,
1300 FTPFILE_QueryOption,
1301 INET_SetOption,
1302 FTPFILE_ReadFile,
1303 FTPFILE_ReadFileEx,
1304 FTPFILE_WriteFile,
1305 FTPFILE_QueryDataAvailable,
1306 NULL,
1307 FTPFILE_LockRequestFile
1310 /***********************************************************************
1311 * FTP_FtpOpenFileW (Internal)
1313 * Open a remote file for writing or reading
1315 * RETURNS
1316 * HINTERNET handle on success
1317 * NULL on failure
1320 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1321 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1322 DWORD_PTR dwContext)
1324 INT nDataSocket;
1325 BOOL bSuccess = FALSE;
1326 ftp_file_t *lpwh = NULL;
1327 appinfo_t *hIC = NULL;
1329 TRACE("\n");
1331 /* Clear any error information */
1332 INTERNET_SetLastError(0);
1334 if (GENERIC_READ == fdwAccess)
1336 /* Set up socket to retrieve data */
1337 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1339 else if (GENERIC_WRITE == fdwAccess)
1341 /* Set up socket to send data */
1342 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1345 /* Get data socket to server */
1346 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1348 lpwh = alloc_object(&lpwfs->hdr, &FTPFILEVtbl, sizeof(ftp_file_t));
1349 lpwh->hdr.htype = WH_HFILE;
1350 lpwh->hdr.dwFlags = dwFlags;
1351 lpwh->hdr.dwContext = dwContext;
1352 lpwh->nDataSocket = nDataSocket;
1353 lpwh->cache_file = NULL;
1354 lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1355 lpwh->session_deleted = FALSE;
1357 WININET_AddRef( &lpwfs->hdr );
1358 lpwh->lpFtpSession = lpwfs;
1359 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1361 /* Indicate that a download is currently in progress */
1362 lpwfs->download_in_progress = lpwh;
1365 if (lpwfs->lstnSocket != -1)
1367 closesocket(lpwfs->lstnSocket);
1368 lpwfs->lstnSocket = -1;
1371 if (bSuccess && fdwAccess == GENERIC_READ)
1373 WCHAR filename[MAX_PATH + 1];
1374 URL_COMPONENTSW uc;
1375 DWORD len;
1377 memset(&uc, 0, sizeof(uc));
1378 uc.dwStructSize = sizeof(uc);
1379 uc.nScheme = INTERNET_SCHEME_FTP;
1380 uc.lpszHostName = lpwfs->servername;
1381 uc.nPort = lpwfs->serverport;
1382 uc.lpszUserName = lpwfs->lpszUserName;
1383 uc.lpszUrlPath = heap_strdupW(lpszFileName);
1385 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1387 WCHAR *url = heap_alloc(len * sizeof(WCHAR));
1389 if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1391 lpwh->cache_file = heap_strdupW(filename);
1392 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1393 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1394 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1396 WARN("Could not create cache file: %u\n", GetLastError());
1397 heap_free(lpwh->cache_file);
1398 lpwh->cache_file = NULL;
1401 heap_free(url);
1403 heap_free(uc.lpszUrlPath);
1406 hIC = lpwfs->lpAppInfo;
1407 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1409 INTERNET_ASYNC_RESULT iar;
1411 if (lpwh)
1413 iar.dwResult = (DWORD_PTR)lpwh->hdr.hInternet;
1414 iar.dwError = ERROR_SUCCESS;
1415 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1416 &iar, sizeof(INTERNET_ASYNC_RESULT));
1419 if(bSuccess) {
1420 FTP_ReceiveRequestData(lpwh, TRUE);
1421 }else {
1422 iar.dwResult = 0;
1423 iar.dwError = INTERNET_GetLastError();
1424 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1425 &iar, sizeof(INTERNET_ASYNC_RESULT));
1429 if(!bSuccess)
1430 return FALSE;
1432 return lpwh->hdr.hInternet;
1436 /***********************************************************************
1437 * FtpOpenFileA (WININET.@)
1439 * Open a remote file for writing or reading
1441 * RETURNS
1442 * HINTERNET handle on success
1443 * NULL on failure
1446 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1447 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1448 DWORD_PTR dwContext)
1450 LPWSTR lpwzFileName;
1451 HINTERNET ret;
1453 lpwzFileName = heap_strdupAtoW(lpszFileName);
1454 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1455 heap_free(lpwzFileName);
1456 return ret;
1459 typedef struct {
1460 task_header_t hdr;
1461 WCHAR *file_name;
1462 DWORD access;
1463 DWORD flags;
1464 DWORD_PTR context;
1465 } open_file_task_t;
1467 static void AsyncFtpOpenFileProc(task_header_t *hdr)
1469 open_file_task_t *task = (open_file_task_t*)hdr;
1470 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1472 TRACE("%p\n", session);
1474 FTP_FtpOpenFileW(session, task->file_name, task->access, task->flags, task->context);
1475 heap_free(task->file_name);
1478 /***********************************************************************
1479 * FtpOpenFileW (WININET.@)
1481 * Open a remote file for writing or reading
1483 * RETURNS
1484 * HINTERNET handle on success
1485 * NULL on failure
1488 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1489 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1490 DWORD_PTR dwContext)
1492 ftp_session_t *lpwfs;
1493 appinfo_t *hIC = NULL;
1494 HINTERNET r = NULL;
1496 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1497 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1499 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1500 if (!lpwfs)
1502 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1503 return FALSE;
1506 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1508 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1509 goto lend;
1512 if ((!lpszFileName) ||
1513 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1514 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1516 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1517 goto lend;
1520 if (lpwfs->download_in_progress != NULL)
1522 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1523 goto lend;
1526 hIC = lpwfs->lpAppInfo;
1527 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1529 open_file_task_t *task;
1531 task = alloc_async_task(&lpwfs->hdr, AsyncFtpOpenFileProc, sizeof(*task));
1532 task->file_name = heap_strdupW(lpszFileName);
1533 task->access = fdwAccess;
1534 task->flags = dwFlags;
1535 task->context = dwContext;
1537 INTERNET_AsyncCall(&task->hdr);
1538 r = NULL;
1540 else
1542 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1545 lend:
1546 WININET_Release( &lpwfs->hdr );
1548 return r;
1552 /***********************************************************************
1553 * FtpGetFileA (WININET.@)
1555 * Retrieve file from the FTP server
1557 * RETURNS
1558 * TRUE on success
1559 * FALSE on failure
1562 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1563 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1564 DWORD_PTR dwContext)
1566 LPWSTR lpwzRemoteFile;
1567 LPWSTR lpwzNewFile;
1568 BOOL ret;
1570 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1571 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1572 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1573 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1574 heap_free(lpwzRemoteFile);
1575 heap_free(lpwzNewFile);
1576 return ret;
1579 typedef struct {
1580 task_header_t hdr;
1581 WCHAR *remote_file;
1582 WCHAR *new_file;
1583 BOOL fail_if_exists;
1584 DWORD local_attr;
1585 DWORD flags;
1586 DWORD_PTR context;
1587 } get_file_task_t;
1589 static void AsyncFtpGetFileProc(task_header_t *hdr)
1591 get_file_task_t *task = (get_file_task_t*)hdr;
1592 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1594 TRACE("%p\n", session);
1596 FTP_FtpGetFileW(session, task->remote_file, task->new_file, task->fail_if_exists,
1597 task->local_attr, task->flags, task->context);
1598 heap_free(task->remote_file);
1599 heap_free(task->new_file);
1603 /***********************************************************************
1604 * FtpGetFileW (WININET.@)
1606 * Retrieve file from the FTP server
1608 * RETURNS
1609 * TRUE on success
1610 * FALSE on failure
1613 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1614 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1615 DWORD_PTR dwContext)
1617 ftp_session_t *lpwfs;
1618 appinfo_t *hIC = NULL;
1619 BOOL r = FALSE;
1621 if (!lpszRemoteFile || !lpszNewFile)
1623 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1624 return FALSE;
1627 lpwfs = (ftp_session_t*) get_handle_object( hInternet );
1628 if (!lpwfs)
1630 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1631 return FALSE;
1634 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1636 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1637 goto lend;
1640 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1642 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1643 goto lend;
1646 if (lpwfs->download_in_progress != NULL)
1648 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1649 goto lend;
1652 hIC = lpwfs->lpAppInfo;
1653 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1655 get_file_task_t *task;
1657 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetFileProc, sizeof(*task));
1658 task->remote_file = heap_strdupW(lpszRemoteFile);
1659 task->new_file = heap_strdupW(lpszNewFile);
1660 task->local_attr = dwLocalFlagsAttribute;
1661 task->fail_if_exists = fFailIfExists;
1662 task->flags = dwInternetFlags;
1663 task->context = dwContext;
1665 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1667 else
1669 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1670 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1673 lend:
1674 WININET_Release( &lpwfs->hdr );
1676 return r;
1680 /***********************************************************************
1681 * FTP_FtpGetFileW (Internal)
1683 * Retrieve file from the FTP server
1685 * RETURNS
1686 * TRUE on success
1687 * FALSE on failure
1690 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1691 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1692 DWORD_PTR dwContext)
1694 BOOL bSuccess = FALSE;
1695 HANDLE hFile;
1696 appinfo_t *hIC = NULL;
1698 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1700 /* Clear any error information */
1701 INTERNET_SetLastError(0);
1703 /* Ensure we can write to lpszNewfile by opening it */
1704 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1705 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1706 if (INVALID_HANDLE_VALUE == hFile)
1707 return FALSE;
1709 /* Set up socket to retrieve data */
1710 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1712 INT nDataSocket;
1714 /* Get data socket to server */
1715 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1717 INT nResCode;
1719 /* Receive data */
1720 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1721 closesocket(nDataSocket);
1723 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1724 if (nResCode)
1726 if (nResCode == 226)
1727 bSuccess = TRUE;
1728 else
1729 FTP_SetResponseError(nResCode);
1734 if (lpwfs->lstnSocket != -1)
1736 closesocket(lpwfs->lstnSocket);
1737 lpwfs->lstnSocket = -1;
1740 CloseHandle(hFile);
1742 hIC = lpwfs->lpAppInfo;
1743 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1745 INTERNET_ASYNC_RESULT iar;
1747 iar.dwResult = (DWORD)bSuccess;
1748 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1749 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1750 &iar, sizeof(INTERNET_ASYNC_RESULT));
1753 if (!bSuccess) DeleteFileW(lpszNewFile);
1754 return bSuccess;
1757 /***********************************************************************
1758 * FtpGetFileSize (WININET.@)
1760 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1762 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1764 if (lpdwFileSizeHigh)
1765 *lpdwFileSizeHigh = 0;
1767 return 0;
1770 /***********************************************************************
1771 * FtpDeleteFileA (WININET.@)
1773 * Delete a file on the ftp server
1775 * RETURNS
1776 * TRUE on success
1777 * FALSE on failure
1780 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1782 LPWSTR lpwzFileName;
1783 BOOL ret;
1785 lpwzFileName = heap_strdupAtoW(lpszFileName);
1786 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1787 heap_free(lpwzFileName);
1788 return ret;
1791 typedef struct {
1792 task_header_t hdr;
1793 WCHAR *file_name;
1794 } delete_file_task_t;
1796 static void AsyncFtpDeleteFileProc(task_header_t *hdr)
1798 delete_file_task_t *task = (delete_file_task_t*)hdr;
1799 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1801 TRACE("%p\n", session);
1803 FTP_FtpDeleteFileW(session, task->file_name);
1804 heap_free(task->file_name);
1807 /***********************************************************************
1808 * FtpDeleteFileW (WININET.@)
1810 * Delete a file on the ftp server
1812 * RETURNS
1813 * TRUE on success
1814 * FALSE on failure
1817 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1819 ftp_session_t *lpwfs;
1820 appinfo_t *hIC = NULL;
1821 BOOL r = FALSE;
1823 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1824 if (!lpwfs)
1826 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1827 return FALSE;
1830 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1832 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1833 goto lend;
1836 if (lpwfs->download_in_progress != NULL)
1838 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1839 goto lend;
1842 if (!lpszFileName)
1844 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1845 goto lend;
1848 hIC = lpwfs->lpAppInfo;
1849 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1851 delete_file_task_t *task;
1853 task = alloc_async_task(&lpwfs->hdr, AsyncFtpDeleteFileProc, sizeof(*task));
1854 task->file_name = heap_strdupW(lpszFileName);
1856 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1858 else
1860 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1863 lend:
1864 WININET_Release( &lpwfs->hdr );
1866 return r;
1869 /***********************************************************************
1870 * FTP_FtpDeleteFileW (Internal)
1872 * Delete a file on the ftp server
1874 * RETURNS
1875 * TRUE on success
1876 * FALSE on failure
1879 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1881 INT nResCode;
1882 BOOL bSuccess = FALSE;
1883 appinfo_t *hIC = NULL;
1885 TRACE("%p\n", lpwfs);
1887 /* Clear any error information */
1888 INTERNET_SetLastError(0);
1890 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1891 goto lend;
1893 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1894 if (nResCode)
1896 if (nResCode == 250)
1897 bSuccess = TRUE;
1898 else
1899 FTP_SetResponseError(nResCode);
1901 lend:
1902 hIC = lpwfs->lpAppInfo;
1903 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1905 INTERNET_ASYNC_RESULT iar;
1907 iar.dwResult = (DWORD)bSuccess;
1908 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1909 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1910 &iar, sizeof(INTERNET_ASYNC_RESULT));
1913 return bSuccess;
1917 /***********************************************************************
1918 * FtpRemoveDirectoryA (WININET.@)
1920 * Remove a directory on the ftp server
1922 * RETURNS
1923 * TRUE on success
1924 * FALSE on failure
1927 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1929 LPWSTR lpwzDirectory;
1930 BOOL ret;
1932 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1933 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1934 heap_free(lpwzDirectory);
1935 return ret;
1938 static void AsyncFtpRemoveDirectoryProc(task_header_t *hdr)
1940 directory_task_t *task = (directory_task_t*)hdr;
1941 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1943 TRACE("%p\n", session);
1945 FTP_FtpRemoveDirectoryW(session, task->directory);
1946 heap_free(task->directory);
1949 /***********************************************************************
1950 * FtpRemoveDirectoryW (WININET.@)
1952 * Remove a directory on the ftp server
1954 * RETURNS
1955 * TRUE on success
1956 * FALSE on failure
1959 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1961 ftp_session_t *lpwfs;
1962 appinfo_t *hIC = NULL;
1963 BOOL r = FALSE;
1965 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1966 if (!lpwfs)
1968 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1969 return FALSE;
1972 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1974 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1975 goto lend;
1978 if (lpwfs->download_in_progress != NULL)
1980 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1981 goto lend;
1984 if (!lpszDirectory)
1986 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1987 goto lend;
1990 hIC = lpwfs->lpAppInfo;
1991 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1993 directory_task_t *task;
1995 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRemoveDirectoryProc, sizeof(*task));
1996 task->directory = heap_strdupW(lpszDirectory);
1998 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
2000 else
2002 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
2005 lend:
2006 WININET_Release( &lpwfs->hdr );
2008 return r;
2011 /***********************************************************************
2012 * FTP_FtpRemoveDirectoryW (Internal)
2014 * Remove a directory on the ftp server
2016 * RETURNS
2017 * TRUE on success
2018 * FALSE on failure
2021 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
2023 INT nResCode;
2024 BOOL bSuccess = FALSE;
2025 appinfo_t *hIC = NULL;
2027 TRACE("\n");
2029 /* Clear any error information */
2030 INTERNET_SetLastError(0);
2032 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2033 goto lend;
2035 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2036 if (nResCode)
2038 if (nResCode == 250)
2039 bSuccess = TRUE;
2040 else
2041 FTP_SetResponseError(nResCode);
2044 lend:
2045 hIC = lpwfs->lpAppInfo;
2046 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2048 INTERNET_ASYNC_RESULT iar;
2050 iar.dwResult = (DWORD)bSuccess;
2051 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2052 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2053 &iar, sizeof(INTERNET_ASYNC_RESULT));
2056 return bSuccess;
2060 /***********************************************************************
2061 * FtpRenameFileA (WININET.@)
2063 * Rename a file on the ftp server
2065 * RETURNS
2066 * TRUE on success
2067 * FALSE on failure
2070 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2072 LPWSTR lpwzSrc;
2073 LPWSTR lpwzDest;
2074 BOOL ret;
2076 lpwzSrc = heap_strdupAtoW(lpszSrc);
2077 lpwzDest = heap_strdupAtoW(lpszDest);
2078 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2079 heap_free(lpwzSrc);
2080 heap_free(lpwzDest);
2081 return ret;
2084 typedef struct {
2085 task_header_t hdr;
2086 WCHAR *src_file;
2087 WCHAR *dst_file;
2088 } rename_file_task_t;
2090 static void AsyncFtpRenameFileProc(task_header_t *hdr)
2092 rename_file_task_t *task = (rename_file_task_t*)hdr;
2093 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
2095 TRACE("%p\n", session);
2097 FTP_FtpRenameFileW(session, task->src_file, task->dst_file);
2098 heap_free(task->src_file);
2099 heap_free(task->dst_file);
2102 /***********************************************************************
2103 * FtpRenameFileW (WININET.@)
2105 * Rename a file on the ftp server
2107 * RETURNS
2108 * TRUE on success
2109 * FALSE on failure
2112 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2114 ftp_session_t *lpwfs;
2115 appinfo_t *hIC = NULL;
2116 BOOL r = FALSE;
2118 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
2119 if (!lpwfs)
2121 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2122 return FALSE;
2125 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2127 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2128 goto lend;
2131 if (lpwfs->download_in_progress != NULL)
2133 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2134 goto lend;
2137 if (!lpszSrc || !lpszDest)
2139 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2140 goto lend;
2143 hIC = lpwfs->lpAppInfo;
2144 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2146 rename_file_task_t *task;
2148 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRenameFileProc, sizeof(*task));
2149 task->src_file = heap_strdupW(lpszSrc);
2150 task->dst_file = heap_strdupW(lpszDest);
2152 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
2154 else
2156 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2159 lend:
2160 WININET_Release( &lpwfs->hdr );
2162 return r;
2165 /***********************************************************************
2166 * FTP_FtpRenameFileW (Internal)
2168 * Rename a file on the ftp server
2170 * RETURNS
2171 * TRUE on success
2172 * FALSE on failure
2175 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2177 INT nResCode;
2178 BOOL bSuccess = FALSE;
2179 appinfo_t *hIC = NULL;
2181 TRACE("\n");
2183 /* Clear any error information */
2184 INTERNET_SetLastError(0);
2186 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2187 goto lend;
2189 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2190 if (nResCode == 350)
2192 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2193 goto lend;
2195 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2198 if (nResCode == 250)
2199 bSuccess = TRUE;
2200 else
2201 FTP_SetResponseError(nResCode);
2203 lend:
2204 hIC = lpwfs->lpAppInfo;
2205 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2207 INTERNET_ASYNC_RESULT iar;
2209 iar.dwResult = (DWORD)bSuccess;
2210 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2211 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2212 &iar, sizeof(INTERNET_ASYNC_RESULT));
2215 return bSuccess;
2218 /***********************************************************************
2219 * FtpCommandA (WININET.@)
2221 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2222 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2224 BOOL r;
2225 WCHAR *cmdW;
2227 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2228 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2230 if (fExpectResponse)
2232 FIXME("data connection not supported\n");
2233 return FALSE;
2236 if (!lpszCommand || !lpszCommand[0])
2238 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2239 return FALSE;
2242 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2244 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2245 return FALSE;
2248 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2250 heap_free(cmdW);
2251 return r;
2254 /***********************************************************************
2255 * FtpCommandW (WININET.@)
2257 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2258 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2260 BOOL r = FALSE;
2261 ftp_session_t *lpwfs;
2262 LPSTR cmd = NULL;
2263 DWORD len, nBytesSent= 0;
2264 INT nResCode, nRC = 0;
2266 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2267 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2269 if (!lpszCommand || !lpszCommand[0])
2271 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2272 return FALSE;
2275 if (fExpectResponse)
2277 FIXME("data connection not supported\n");
2278 return FALSE;
2281 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
2282 if (!lpwfs)
2284 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2285 return FALSE;
2288 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2290 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2291 goto lend;
2294 if (lpwfs->download_in_progress != NULL)
2296 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2297 goto lend;
2300 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2301 if ((cmd = heap_alloc(len)))
2302 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2303 else
2305 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2306 goto lend;
2309 strcat(cmd, szCRLF);
2310 len--;
2312 TRACE("Sending (%s) len(%d)\n", cmd, len);
2313 while ((nBytesSent < len) && (nRC != -1))
2315 nRC = sock_send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2316 if (nRC != -1)
2318 nBytesSent += nRC;
2319 TRACE("Sent %d bytes\n", nRC);
2323 if (nBytesSent)
2325 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2326 if (nResCode > 0 && nResCode < 400)
2327 r = TRUE;
2328 else
2329 FTP_SetResponseError(nResCode);
2332 lend:
2333 WININET_Release( &lpwfs->hdr );
2334 heap_free( cmd );
2335 return r;
2339 /***********************************************************************
2340 * FTPSESSION_Destroy (internal)
2342 * Deallocate session handle
2344 static void FTPSESSION_Destroy(object_header_t *hdr)
2346 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2348 TRACE("\n");
2350 WININET_Release(&lpwfs->lpAppInfo->hdr);
2352 heap_free(lpwfs->lpszPassword);
2353 heap_free(lpwfs->lpszUserName);
2354 heap_free(lpwfs->servername);
2357 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2359 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2361 TRACE("\n");
2363 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2364 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2366 if (lpwfs->download_in_progress != NULL)
2367 lpwfs->download_in_progress->session_deleted = TRUE;
2369 if (lpwfs->sndSocket != -1)
2370 closesocket(lpwfs->sndSocket);
2372 if (lpwfs->lstnSocket != -1)
2373 closesocket(lpwfs->lstnSocket);
2375 if (lpwfs->pasvSocket != -1)
2376 closesocket(lpwfs->pasvSocket);
2378 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2379 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2382 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2384 switch(option) {
2385 case INTERNET_OPTION_HANDLE_TYPE:
2386 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2388 if (*size < sizeof(ULONG))
2389 return ERROR_INSUFFICIENT_BUFFER;
2391 *size = sizeof(DWORD);
2392 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2393 return ERROR_SUCCESS;
2396 return INET_QueryOption(hdr, option, buffer, size, unicode);
2399 static const object_vtbl_t FTPSESSIONVtbl = {
2400 FTPSESSION_Destroy,
2401 FTPSESSION_CloseConnection,
2402 FTPSESSION_QueryOption,
2403 INET_SetOption,
2404 NULL,
2405 NULL,
2406 NULL,
2407 NULL,
2408 NULL
2412 /***********************************************************************
2413 * FTP_Connect (internal)
2415 * Connect to a ftp server
2417 * RETURNS
2418 * HINTERNET a session handle on success
2419 * NULL on failure
2421 * NOTES:
2423 * Windows uses 'anonymous' as the username, when given a NULL username
2424 * and a NULL password. The password is first looked up in:
2426 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2428 * If this entry is not present it uses the current username as the password.
2432 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2433 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2434 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2435 DWORD dwInternalFlags)
2437 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2438 'M','i','c','r','o','s','o','f','t','\\',
2439 'W','i','n','d','o','w','s','\\',
2440 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2441 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2442 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2443 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2444 static const WCHAR szEmpty[] = {'\0'};
2445 struct sockaddr_in socketAddr;
2446 INT nsocket = -1;
2447 socklen_t sock_namelen;
2448 BOOL bSuccess = FALSE;
2449 ftp_session_t *lpwfs = NULL;
2450 char szaddr[INET6_ADDRSTRLEN];
2452 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2453 hIC, debugstr_w(lpszServerName),
2454 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2456 assert( hIC->hdr.htype == WH_HINIT );
2458 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2460 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2461 return NULL;
2464 lpwfs = alloc_object(&hIC->hdr, &FTPSESSIONVtbl, sizeof(ftp_session_t));
2465 if (NULL == lpwfs)
2467 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2468 return NULL;
2471 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2472 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2473 else
2474 lpwfs->serverport = nServerPort;
2476 lpwfs->hdr.htype = WH_HFTPSESSION;
2477 lpwfs->hdr.dwFlags = dwFlags;
2478 lpwfs->hdr.dwContext = dwContext;
2479 lpwfs->hdr.dwInternalFlags |= dwInternalFlags;
2480 lpwfs->download_in_progress = NULL;
2481 lpwfs->sndSocket = -1;
2482 lpwfs->lstnSocket = -1;
2483 lpwfs->pasvSocket = -1;
2485 WININET_AddRef( &hIC->hdr );
2486 lpwfs->lpAppInfo = hIC;
2487 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2489 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
2490 if(strchrW(hIC->proxy, ' '))
2491 FIXME("Several proxies not implemented.\n");
2492 if(hIC->proxyBypass)
2493 FIXME("Proxy bypass is ignored.\n");
2495 if (!lpszUserName || !lpszUserName[0]) {
2496 HKEY key;
2497 WCHAR szPassword[MAX_PATH];
2498 DWORD len = sizeof(szPassword);
2500 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2502 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2503 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2504 /* Nothing in the registry, get the username and use that as the password */
2505 if (!GetUserNameW(szPassword, &len)) {
2506 /* Should never get here, but use an empty password as failsafe */
2507 strcpyW(szPassword, szEmpty);
2510 RegCloseKey(key);
2512 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2513 lpwfs->lpszPassword = heap_strdupW(szPassword);
2515 else {
2516 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2517 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2519 lpwfs->servername = heap_strdupW(lpszServerName);
2521 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2522 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2524 INTERNET_ASYNC_RESULT iar;
2526 iar.dwResult = (DWORD_PTR)lpwfs->hdr.hInternet;
2527 iar.dwError = ERROR_SUCCESS;
2529 SendAsyncCallback(&hIC->hdr, dwContext,
2530 INTERNET_STATUS_HANDLE_CREATED, &iar,
2531 sizeof(INTERNET_ASYNC_RESULT));
2534 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2535 (LPWSTR) lpszServerName, (strlenW(lpszServerName)+1) * sizeof(WCHAR));
2537 sock_namelen = sizeof(socketAddr);
2538 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen, szaddr))
2540 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2541 goto lerror;
2544 if (socketAddr.sin_family != AF_INET)
2546 WARN("unsupported address family %d\n", socketAddr.sin_family);
2547 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2548 goto lerror;
2551 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2552 szaddr, strlen(szaddr)+1);
2554 init_winsock();
2555 nsocket = socket(AF_INET,SOCK_STREAM,0);
2556 if (nsocket == -1)
2558 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2559 goto lerror;
2562 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2563 szaddr, strlen(szaddr)+1);
2565 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2567 ERR("Unable to connect (%d)\n", WSAGetLastError());
2568 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2569 closesocket(nsocket);
2571 else
2573 TRACE("Connected to server\n");
2574 lpwfs->sndSocket = nsocket;
2575 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2576 szaddr, strlen(szaddr)+1);
2578 sock_namelen = sizeof(lpwfs->socketAddress);
2579 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2581 if (FTP_ConnectToHost(lpwfs))
2583 TRACE("Successfully logged into server\n");
2584 bSuccess = TRUE;
2588 lerror:
2589 if (!bSuccess)
2591 if(lpwfs)
2592 WININET_Release( &lpwfs->hdr );
2593 return NULL;
2596 return lpwfs->hdr.hInternet;
2600 /***********************************************************************
2601 * FTP_ConnectToHost (internal)
2603 * Connect to a ftp server
2605 * RETURNS
2606 * TRUE on success
2607 * NULL on failure
2610 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2612 INT nResCode;
2613 BOOL bSuccess = FALSE;
2615 TRACE("\n");
2616 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2618 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2619 goto lend;
2621 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2622 if (nResCode)
2624 /* Login successful... */
2625 if (nResCode == 230)
2626 bSuccess = TRUE;
2627 /* User name okay, need password... */
2628 else if (nResCode == 331)
2629 bSuccess = FTP_SendPassword(lpwfs);
2630 /* Need account for login... */
2631 else if (nResCode == 332)
2632 bSuccess = FTP_SendAccount(lpwfs);
2633 else
2634 FTP_SetResponseError(nResCode);
2637 TRACE("Returning %d\n", bSuccess);
2638 lend:
2639 return bSuccess;
2642 /***********************************************************************
2643 * FTP_GetNextLine (internal)
2645 * Parse next line in directory string listing
2647 * RETURNS
2648 * Pointer to beginning of next line
2649 * NULL on failure
2653 static LPSTR FTP_GetNextLine(INT nSocket, LPDWORD dwLen)
2655 struct timeval tv = {RESPONSE_TIMEOUT,0};
2656 FD_SET set;
2657 INT nRecv = 0;
2658 LPSTR lpszBuffer = INTERNET_GetResponseBuffer();
2660 TRACE("\n");
2662 FD_ZERO(&set);
2663 FD_SET(nSocket, &set);
2665 while (nRecv < MAX_REPLY_LEN)
2667 if (select(nSocket+1, &set, NULL, NULL, &tv) > 0)
2669 if (sock_recv(nSocket, &lpszBuffer[nRecv], 1, 0) <= 0)
2671 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2672 return NULL;
2675 if (lpszBuffer[nRecv] == '\n')
2677 lpszBuffer[nRecv] = '\0';
2678 *dwLen = nRecv - 1;
2679 TRACE(":%d %s\n", nRecv, lpszBuffer);
2680 return lpszBuffer;
2682 if (lpszBuffer[nRecv] != '\r')
2683 nRecv++;
2685 else
2687 INTERNET_SetLastError(ERROR_INTERNET_TIMEOUT);
2688 return NULL;
2692 return NULL;
2695 /***********************************************************************
2696 * FTP_SendCommandA (internal)
2698 * Send command to server
2700 * RETURNS
2701 * TRUE on success
2702 * NULL on failure
2705 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2706 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2708 DWORD len;
2709 CHAR *buf;
2710 DWORD nBytesSent = 0;
2711 int nRC = 0;
2712 DWORD dwParamLen;
2714 TRACE("%d: (%s) %d\n", ftpCmd, debugstr_a(lpszParam), nSocket);
2716 if (lpfnStatusCB)
2718 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2721 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2722 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2723 if (NULL == (buf = heap_alloc(len+1)))
2725 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2726 return FALSE;
2728 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2729 dwParamLen ? lpszParam : "", szCRLF);
2731 TRACE("Sending (%s) len(%d)\n", buf, len);
2732 while((nBytesSent < len) && (nRC != -1))
2734 nRC = sock_send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2735 nBytesSent += nRC;
2737 heap_free(buf);
2739 if (lpfnStatusCB)
2741 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2742 &nBytesSent, sizeof(DWORD));
2745 TRACE("Sent %d bytes\n", nBytesSent);
2746 return (nRC != -1);
2749 /***********************************************************************
2750 * FTP_SendCommand (internal)
2752 * Send command to server
2754 * RETURNS
2755 * TRUE on success
2756 * NULL on failure
2759 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2760 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2762 BOOL ret;
2763 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2764 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2765 heap_free(lpszParamA);
2766 return ret;
2769 /***********************************************************************
2770 * FTP_ReceiveResponse (internal)
2772 * Receive response from server
2774 * RETURNS
2775 * Reply code on success
2776 * 0 on failure
2779 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2781 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2782 DWORD nRecv;
2783 INT rc = 0;
2784 char firstprefix[5];
2785 BOOL multiline = FALSE;
2787 TRACE("socket(%d)\n", lpwfs->sndSocket);
2789 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2791 while(1)
2793 if (!FTP_GetNextLine(lpwfs->sndSocket, &nRecv))
2794 goto lerror;
2796 if (nRecv >= 3)
2798 if(!multiline)
2800 if(lpszResponse[3] != '-')
2801 break;
2802 else
2803 { /* Start of multiline response. Loop until we get "nnn " */
2804 multiline = TRUE;
2805 memcpy(firstprefix, lpszResponse, 3);
2806 firstprefix[3] = ' ';
2807 firstprefix[4] = '\0';
2810 else
2812 if(!memcmp(firstprefix, lpszResponse, 4))
2813 break;
2818 if (nRecv >= 3)
2820 rc = atoi(lpszResponse);
2822 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2823 &nRecv, sizeof(DWORD));
2826 lerror:
2827 TRACE("return %d\n", rc);
2828 return rc;
2832 /***********************************************************************
2833 * FTP_SendPassword (internal)
2835 * Send password to ftp server
2837 * RETURNS
2838 * TRUE on success
2839 * NULL on failure
2842 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2844 INT nResCode;
2845 BOOL bSuccess = FALSE;
2847 TRACE("\n");
2848 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2849 goto lend;
2851 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2852 if (nResCode)
2854 TRACE("Received reply code %d\n", nResCode);
2855 /* Login successful... */
2856 if (nResCode == 230)
2857 bSuccess = TRUE;
2858 /* Command not implemented, superfluous at the server site... */
2859 /* Need account for login... */
2860 else if (nResCode == 332)
2861 bSuccess = FTP_SendAccount(lpwfs);
2862 else
2863 FTP_SetResponseError(nResCode);
2866 lend:
2867 TRACE("Returning %d\n", bSuccess);
2868 return bSuccess;
2872 /***********************************************************************
2873 * FTP_SendAccount (internal)
2877 * RETURNS
2878 * TRUE on success
2879 * FALSE on failure
2882 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2884 INT nResCode;
2885 BOOL bSuccess = FALSE;
2887 TRACE("\n");
2888 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2889 goto lend;
2891 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2892 if (nResCode)
2893 bSuccess = TRUE;
2894 else
2895 FTP_SetResponseError(nResCode);
2897 lend:
2898 return bSuccess;
2902 /***********************************************************************
2903 * FTP_SendStore (internal)
2905 * Send request to upload file to ftp server
2907 * RETURNS
2908 * TRUE on success
2909 * FALSE on failure
2912 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2914 INT nResCode;
2915 BOOL bSuccess = FALSE;
2917 TRACE("\n");
2918 if (!FTP_InitListenSocket(lpwfs))
2919 goto lend;
2921 if (!FTP_SendType(lpwfs, dwType))
2922 goto lend;
2924 if (!FTP_SendPortOrPasv(lpwfs))
2925 goto lend;
2927 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2928 goto lend;
2929 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2930 if (nResCode)
2932 if (nResCode == 150 || nResCode == 125)
2933 bSuccess = TRUE;
2934 else
2935 FTP_SetResponseError(nResCode);
2938 lend:
2939 if (!bSuccess && lpwfs->lstnSocket != -1)
2941 closesocket(lpwfs->lstnSocket);
2942 lpwfs->lstnSocket = -1;
2945 return bSuccess;
2949 /***********************************************************************
2950 * FTP_InitListenSocket (internal)
2952 * Create a socket to listen for server response
2954 * RETURNS
2955 * TRUE on success
2956 * FALSE on failure
2959 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2961 BOOL bSuccess = FALSE;
2962 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2964 TRACE("\n");
2966 init_winsock();
2967 lpwfs->lstnSocket = socket(AF_INET, SOCK_STREAM, 0);
2968 if (lpwfs->lstnSocket == -1)
2970 TRACE("Unable to create listening socket\n");
2971 goto lend;
2974 /* We obtain our ip addr from the name of the command channel socket */
2975 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2977 /* and get the system to assign us a port */
2978 lpwfs->lstnSocketAddress.sin_port = htons(0);
2980 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2982 TRACE("Unable to bind socket\n");
2983 goto lend;
2986 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2988 TRACE("listen failed\n");
2989 goto lend;
2992 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2993 bSuccess = TRUE;
2995 lend:
2996 if (!bSuccess && lpwfs->lstnSocket != -1)
2998 closesocket(lpwfs->lstnSocket);
2999 lpwfs->lstnSocket = -1;
3002 return bSuccess;
3006 /***********************************************************************
3007 * FTP_SendType (internal)
3009 * Tell server type of data being transferred
3011 * RETURNS
3012 * TRUE on success
3013 * FALSE on failure
3015 * W98SE doesn't cache the type that's currently set
3016 * (i.e. it sends it always),
3017 * so we probably don't want to do that either.
3019 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
3021 INT nResCode;
3022 WCHAR type[] = { 'I','\0' };
3023 BOOL bSuccess = FALSE;
3025 TRACE("\n");
3026 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
3027 type[0] = 'A';
3029 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
3030 goto lend;
3032 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
3033 if (nResCode)
3035 if (nResCode == 2)
3036 bSuccess = TRUE;
3037 else
3038 FTP_SetResponseError(nResCode);
3041 lend:
3042 return bSuccess;
3046 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
3047 /***********************************************************************
3048 * FTP_GetFileSize (internal)
3050 * Retrieves from the server the size of the given file
3052 * RETURNS
3053 * TRUE on success
3054 * FALSE on failure
3057 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
3059 INT nResCode;
3060 BOOL bSuccess = FALSE;
3062 TRACE("\n");
3064 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
3065 goto lend;
3067 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3068 if (nResCode)
3070 if (nResCode == 213) {
3071 /* Now parses the output to get the actual file size */
3072 int i;
3073 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3075 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3076 if (lpszResponseBuffer[i] == '\0') return FALSE;
3077 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3079 bSuccess = TRUE;
3080 } else {
3081 FTP_SetResponseError(nResCode);
3085 lend:
3086 return bSuccess;
3088 #endif
3091 /***********************************************************************
3092 * FTP_SendPort (internal)
3094 * Tell server which port to use
3096 * RETURNS
3097 * TRUE on success
3098 * FALSE on failure
3101 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3103 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3104 INT nResCode;
3105 WCHAR szIPAddress[64];
3106 BOOL bSuccess = FALSE;
3107 TRACE("\n");
3109 sprintfW(szIPAddress, szIPFormat,
3110 lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x000000FF,
3111 (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x0000FF00)>>8,
3112 (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x00FF0000)>>16,
3113 (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0xFF000000)>>24,
3114 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3115 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3117 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3118 goto lend;
3120 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3121 if (nResCode)
3123 if (nResCode == 200)
3124 bSuccess = TRUE;
3125 else
3126 FTP_SetResponseError(nResCode);
3129 lend:
3130 return bSuccess;
3134 /***********************************************************************
3135 * FTP_DoPassive (internal)
3137 * Tell server that we want to do passive transfers
3138 * and connect data socket
3140 * RETURNS
3141 * TRUE on success
3142 * FALSE on failure
3145 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3147 INT nResCode;
3148 BOOL bSuccess = FALSE;
3150 TRACE("\n");
3151 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3152 goto lend;
3154 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3155 if (nResCode)
3157 if (nResCode == 227)
3159 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3160 LPSTR p;
3161 int f[6];
3162 int i;
3163 char *pAddr, *pPort;
3164 INT nsocket = -1;
3165 struct sockaddr_in dataSocketAddress;
3167 p = lpszResponseBuffer+4; /* skip status code */
3168 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3170 if (*p == '\0')
3172 ERR("no address found in response, aborting\n");
3173 goto lend;
3176 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3177 &f[4], &f[5]) != 6)
3179 ERR("unknown response address format '%s', aborting\n", p);
3180 goto lend;
3182 for (i=0; i < 6; i++)
3183 f[i] = f[i] & 0xff;
3185 dataSocketAddress = lpwfs->socketAddress;
3186 pAddr = (char *)&(dataSocketAddress.sin_addr.S_un.S_addr);
3187 pPort = (char *)&(dataSocketAddress.sin_port);
3188 pAddr[0] = f[0];
3189 pAddr[1] = f[1];
3190 pAddr[2] = f[2];
3191 pAddr[3] = f[3];
3192 pPort[0] = f[4];
3193 pPort[1] = f[5];
3195 nsocket = socket(AF_INET,SOCK_STREAM,0);
3196 if (nsocket == -1)
3197 goto lend;
3199 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3201 ERR("can't connect passive FTP data port.\n");
3202 closesocket(nsocket);
3203 goto lend;
3205 lpwfs->pasvSocket = nsocket;
3206 bSuccess = TRUE;
3208 else
3209 FTP_SetResponseError(nResCode);
3212 lend:
3213 return bSuccess;
3217 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3219 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3221 if (!FTP_DoPassive(lpwfs))
3222 return FALSE;
3224 else
3226 if (!FTP_SendPort(lpwfs))
3227 return FALSE;
3229 return TRUE;
3233 /***********************************************************************
3234 * FTP_GetDataSocket (internal)
3236 * Either accepts an incoming data socket connection from the server
3237 * or just returns the already opened socket after a PASV command
3238 * in case of passive FTP.
3241 * RETURNS
3242 * TRUE on success
3243 * FALSE on failure
3246 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3248 struct sockaddr_in saddr;
3249 socklen_t addrlen = sizeof(saddr);
3251 TRACE("\n");
3252 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3254 *nDataSocket = lpwfs->pasvSocket;
3255 lpwfs->pasvSocket = -1;
3257 else
3259 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3260 closesocket(lpwfs->lstnSocket);
3261 lpwfs->lstnSocket = -1;
3263 return *nDataSocket != -1;
3267 /***********************************************************************
3268 * FTP_SendData (internal)
3270 * Send data to the server
3272 * RETURNS
3273 * TRUE on success
3274 * FALSE on failure
3277 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3279 BY_HANDLE_FILE_INFORMATION fi;
3280 DWORD nBytesRead = 0;
3281 DWORD nBytesSent = 0;
3282 DWORD nTotalSent = 0;
3283 DWORD nBytesToSend, nLen;
3284 int nRC = 1;
3285 time_t s_long_time, e_long_time;
3286 LONG nSeconds;
3287 CHAR *lpszBuffer;
3289 TRACE("\n");
3290 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3292 /* Get the size of the file. */
3293 GetFileInformationByHandle(hFile, &fi);
3294 time(&s_long_time);
3298 nBytesToSend = nBytesRead - nBytesSent;
3300 if (nBytesToSend <= 0)
3302 /* Read data from file. */
3303 nBytesSent = 0;
3304 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3305 ERR("Failed reading from file\n");
3307 if (nBytesRead > 0)
3308 nBytesToSend = nBytesRead;
3309 else
3310 break;
3313 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3314 DATA_PACKET_SIZE : nBytesToSend;
3315 nRC = sock_send(nDataSocket, lpszBuffer, nLen, 0);
3317 if (nRC != -1)
3319 nBytesSent += nRC;
3320 nTotalSent += nRC;
3323 /* Do some computation to display the status. */
3324 time(&e_long_time);
3325 nSeconds = e_long_time - s_long_time;
3326 if( nSeconds / 60 > 0 )
3328 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3329 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3330 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3332 else
3334 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3335 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3336 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3338 } while (nRC != -1);
3340 TRACE("file transfer complete!\n");
3342 heap_free(lpszBuffer);
3343 return nTotalSent;
3347 /***********************************************************************
3348 * FTP_SendRetrieve (internal)
3350 * Send request to retrieve a file
3352 * RETURNS
3353 * Number of bytes to be received on success
3354 * 0 on failure
3357 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3359 INT nResCode;
3360 BOOL ret;
3362 TRACE("\n");
3363 if (!(ret = FTP_InitListenSocket(lpwfs)))
3364 goto lend;
3366 if (!(ret = FTP_SendType(lpwfs, dwType)))
3367 goto lend;
3369 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3370 goto lend;
3372 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3373 goto lend;
3375 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3376 if ((nResCode != 125) && (nResCode != 150)) {
3377 /* That means that we got an error getting the file. */
3378 FTP_SetResponseError(nResCode);
3379 ret = FALSE;
3382 lend:
3383 if (!ret && lpwfs->lstnSocket != -1)
3385 closesocket(lpwfs->lstnSocket);
3386 lpwfs->lstnSocket = -1;
3389 return ret;
3393 /***********************************************************************
3394 * FTP_RetrieveData (internal)
3396 * Retrieve data from server
3398 * RETURNS
3399 * TRUE on success
3400 * FALSE on failure
3403 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3405 DWORD nBytesWritten;
3406 INT nRC = 0;
3407 CHAR *lpszBuffer;
3409 TRACE("\n");
3411 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3412 if (NULL == lpszBuffer)
3414 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3415 return FALSE;
3418 while (nRC != -1)
3420 nRC = sock_recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3421 if (nRC != -1)
3423 /* other side closed socket. */
3424 if (nRC == 0)
3425 goto recv_end;
3426 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3430 TRACE("Data transfer complete\n");
3432 recv_end:
3433 heap_free(lpszBuffer);
3434 return (nRC != -1);
3437 /***********************************************************************
3438 * FTPFINDNEXT_Destroy (internal)
3440 * Deallocate session handle
3442 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3444 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3445 DWORD i;
3447 TRACE("\n");
3449 WININET_Release(&lpwfn->lpFtpSession->hdr);
3451 for (i = 0; i < lpwfn->size; i++)
3453 heap_free(lpwfn->lpafp[i].lpszName);
3455 heap_free(lpwfn->lpafp);
3458 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3460 WIN32_FIND_DATAW *find_data = data;
3461 DWORD res = ERROR_SUCCESS;
3463 TRACE("index(%d) size(%d)\n", find->index, find->size);
3465 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3467 if (find->index < find->size) {
3468 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3469 find->index++;
3471 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3472 }else {
3473 res = ERROR_NO_MORE_FILES;
3476 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3478 INTERNET_ASYNC_RESULT iar;
3480 iar.dwResult = (res == ERROR_SUCCESS);
3481 iar.dwError = res;
3483 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3484 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3485 sizeof(INTERNET_ASYNC_RESULT));
3488 return res;
3491 typedef struct {
3492 task_header_t hdr;
3493 WIN32_FIND_DATAW *find_data;
3494 } find_next_task_t;
3496 static void FTPFINDNEXT_AsyncFindNextFileProc(task_header_t *hdr)
3498 find_next_task_t *task = (find_next_task_t*)hdr;
3500 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)task->hdr.hdr, task->find_data);
3503 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3505 switch(option) {
3506 case INTERNET_OPTION_HANDLE_TYPE:
3507 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3509 if (*size < sizeof(ULONG))
3510 return ERROR_INSUFFICIENT_BUFFER;
3512 *size = sizeof(DWORD);
3513 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3514 return ERROR_SUCCESS;
3517 return INET_QueryOption(hdr, option, buffer, size, unicode);
3520 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3522 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3524 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3526 find_next_task_t *task;
3528 task = alloc_async_task(&find->hdr, FTPFINDNEXT_AsyncFindNextFileProc, sizeof(*task));
3529 task->find_data = data;
3531 INTERNET_AsyncCall(&task->hdr);
3532 return ERROR_SUCCESS;
3535 return FTPFINDNEXT_FindNextFileProc(find, data);
3538 static const object_vtbl_t FTPFINDNEXTVtbl = {
3539 FTPFINDNEXT_Destroy,
3540 NULL,
3541 FTPFINDNEXT_QueryOption,
3542 INET_SetOption,
3543 NULL,
3544 NULL,
3545 NULL,
3546 NULL,
3547 FTPFINDNEXT_FindNextFileW
3550 /***********************************************************************
3551 * FTP_ReceiveFileList (internal)
3553 * Read file list from server
3555 * RETURNS
3556 * Handle to file list on success
3557 * NULL on failure
3560 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3561 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3563 DWORD dwSize = 0;
3564 LPFILEPROPERTIESW lpafp = NULL;
3565 LPWININETFTPFINDNEXTW lpwfn = NULL;
3567 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3569 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3571 if(lpFindFileData)
3572 FTP_ConvertFileProp(lpafp, lpFindFileData);
3574 lpwfn = alloc_object(&lpwfs->hdr, &FTPFINDNEXTVtbl, sizeof(WININETFTPFINDNEXTW));
3575 if (lpwfn)
3577 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3578 lpwfn->hdr.dwContext = dwContext;
3579 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3580 lpwfn->size = dwSize;
3581 lpwfn->lpafp = lpafp;
3583 WININET_AddRef( &lpwfs->hdr );
3584 lpwfn->lpFtpSession = lpwfs;
3585 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3589 TRACE("Matched %d files\n", dwSize);
3590 return lpwfn ? lpwfn->hdr.hInternet : NULL;
3594 /***********************************************************************
3595 * FTP_ConvertFileProp (internal)
3597 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3599 * RETURNS
3600 * TRUE on success
3601 * FALSE on failure
3604 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3606 BOOL bSuccess = FALSE;
3608 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3610 if (lpafp)
3612 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3613 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3614 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3616 /* Not all fields are filled in */
3617 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3618 lpFindFileData->nFileSizeLow = lpafp->nSize;
3620 if (lpafp->bIsDirectory)
3621 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3623 if (lpafp->lpszName)
3624 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3626 bSuccess = TRUE;
3629 return bSuccess;
3632 /***********************************************************************
3633 * FTP_ParseNextFile (internal)
3635 * Parse the next line in file listing
3637 * RETURNS
3638 * TRUE on success
3639 * FALSE on failure
3641 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3643 static const char szSpace[] = " \t";
3644 DWORD nBufLen;
3645 char *pszLine;
3646 char *pszToken;
3647 char *pszTmp;
3648 BOOL found = FALSE;
3649 int i;
3651 lpfp->lpszName = NULL;
3652 do {
3653 if(!(pszLine = FTP_GetNextLine(nSocket, &nBufLen)))
3654 return FALSE;
3656 pszToken = strtok(pszLine, szSpace);
3657 /* ls format
3658 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3660 * For instance:
3661 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3663 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3664 if(!FTP_ParsePermission(pszToken, lpfp))
3665 lpfp->bIsDirectory = FALSE;
3666 for(i=0; i<=3; i++) {
3667 if(!(pszToken = strtok(NULL, szSpace)))
3668 break;
3670 if(!pszToken) continue;
3671 if(lpfp->bIsDirectory) {
3672 TRACE("Is directory\n");
3673 lpfp->nSize = 0;
3675 else {
3676 TRACE("Size: %s\n", pszToken);
3677 lpfp->nSize = atol(pszToken);
3680 lpfp->tmLastModified.wSecond = 0;
3681 lpfp->tmLastModified.wMinute = 0;
3682 lpfp->tmLastModified.wHour = 0;
3683 lpfp->tmLastModified.wDay = 0;
3684 lpfp->tmLastModified.wMonth = 0;
3685 lpfp->tmLastModified.wYear = 0;
3687 /* Determine month */
3688 pszToken = strtok(NULL, szSpace);
3689 if(!pszToken) continue;
3690 if(strlen(pszToken) >= 3) {
3691 pszToken[3] = 0;
3692 if((pszTmp = StrStrIA(szMonths, pszToken)))
3693 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3695 /* Determine day */
3696 pszToken = strtok(NULL, szSpace);
3697 if(!pszToken) continue;
3698 lpfp->tmLastModified.wDay = atoi(pszToken);
3699 /* Determine time or year */
3700 pszToken = strtok(NULL, szSpace);
3701 if(!pszToken) continue;
3702 if((pszTmp = strchr(pszToken, ':'))) {
3703 SYSTEMTIME curr_time;
3704 *pszTmp = 0;
3705 pszTmp++;
3706 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3707 lpfp->tmLastModified.wHour = atoi(pszToken);
3708 GetLocalTime( &curr_time );
3709 lpfp->tmLastModified.wYear = curr_time.wYear;
3711 else {
3712 lpfp->tmLastModified.wYear = atoi(pszToken);
3713 lpfp->tmLastModified.wHour = 12;
3715 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3716 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3717 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3719 pszToken = strtok(NULL, szSpace);
3720 if(!pszToken) continue;
3721 lpfp->lpszName = heap_strdupAtoW(pszToken);
3722 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3724 /* NT way of parsing ... :
3726 07-13-03 08:55PM <DIR> sakpatch
3727 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3729 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3730 int mon, mday, year, hour, min;
3731 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3733 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3734 lpfp->tmLastModified.wDay = mday;
3735 lpfp->tmLastModified.wMonth = mon;
3736 lpfp->tmLastModified.wYear = year;
3738 /* Hacky and bad Y2K protection :-) */
3739 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3741 pszToken = strtok(NULL, szSpace);
3742 if(!pszToken) continue;
3743 sscanf(pszToken, "%d:%d", &hour, &min);
3744 lpfp->tmLastModified.wHour = hour;
3745 lpfp->tmLastModified.wMinute = min;
3746 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3747 lpfp->tmLastModified.wHour += 12;
3749 lpfp->tmLastModified.wSecond = 0;
3751 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3752 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3753 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3755 pszToken = strtok(NULL, szSpace);
3756 if(!pszToken) continue;
3757 if(!strcasecmp(pszToken, "<DIR>")) {
3758 lpfp->bIsDirectory = TRUE;
3759 lpfp->nSize = 0;
3760 TRACE("Is directory\n");
3762 else {
3763 lpfp->bIsDirectory = FALSE;
3764 lpfp->nSize = atol(pszToken);
3765 TRACE("Size: %d\n", lpfp->nSize);
3768 pszToken = strtok(NULL, szSpace);
3769 if(!pszToken) continue;
3770 lpfp->lpszName = heap_strdupAtoW(pszToken);
3771 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3773 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3774 else if(pszToken[0] == '+') {
3775 FIXME("EPLF Format not implemented\n");
3778 if(lpfp->lpszName) {
3779 if((lpszSearchFile == NULL) ||
3780 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3781 found = TRUE;
3782 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3784 else {
3785 heap_free(lpfp->lpszName);
3786 lpfp->lpszName = NULL;
3789 } while(!found);
3790 return TRUE;
3793 /***********************************************************************
3794 * FTP_ParseDirectory (internal)
3796 * Parse string of directory information
3798 * RETURNS
3799 * TRUE on success
3800 * FALSE on failure
3802 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3803 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3805 BOOL bSuccess = TRUE;
3806 INT sizeFilePropArray = 500;/*20; */
3807 INT indexFilePropArray = -1;
3809 TRACE("\n");
3811 /* Allocate initial file properties array */
3812 *lpafp = heap_alloc_zero(sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3813 if (!*lpafp)
3814 return FALSE;
3816 do {
3817 if (indexFilePropArray+1 >= sizeFilePropArray)
3819 LPFILEPROPERTIESW tmpafp;
3821 sizeFilePropArray *= 2;
3822 tmpafp = heap_realloc_zero(*lpafp, sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3823 if (NULL == tmpafp)
3825 bSuccess = FALSE;
3826 break;
3829 *lpafp = tmpafp;
3831 indexFilePropArray++;
3832 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3834 if (bSuccess && indexFilePropArray)
3836 if (indexFilePropArray < sizeFilePropArray - 1)
3838 LPFILEPROPERTIESW tmpafp;
3840 tmpafp = heap_realloc(*lpafp, sizeof(FILEPROPERTIESW)*indexFilePropArray);
3841 if (NULL != tmpafp)
3842 *lpafp = tmpafp;
3844 *dwfp = indexFilePropArray;
3846 else
3848 heap_free(*lpafp);
3849 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3850 bSuccess = FALSE;
3853 return bSuccess;
3857 /***********************************************************************
3858 * FTP_ParsePermission (internal)
3860 * Parse permission string of directory information
3862 * RETURNS
3863 * TRUE on success
3864 * FALSE on failure
3867 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3869 BOOL bSuccess = TRUE;
3870 unsigned short nPermission = 0;
3871 INT nPos = 1;
3872 INT nLast = 9;
3874 TRACE("\n");
3875 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3877 bSuccess = FALSE;
3878 return bSuccess;
3881 lpfp->bIsDirectory = (*lpszPermission == 'd');
3884 switch (nPos)
3886 case 1:
3887 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3888 break;
3889 case 2:
3890 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3891 break;
3892 case 3:
3893 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3894 break;
3895 case 4:
3896 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3897 break;
3898 case 5:
3899 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3900 break;
3901 case 6:
3902 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3903 break;
3904 case 7:
3905 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3906 break;
3907 case 8:
3908 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3909 break;
3910 case 9:
3911 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3912 break;
3914 nPos++;
3915 }while (nPos <= nLast);
3917 lpfp->permissions = nPermission;
3918 return bSuccess;
3922 /***********************************************************************
3923 * FTP_SetResponseError (internal)
3925 * Set the appropriate error code for a given response from the server
3927 * RETURNS
3930 static DWORD FTP_SetResponseError(DWORD dwResponse)
3932 DWORD dwCode = 0;
3934 switch(dwResponse)
3936 case 425: /* Cannot open data connection. */
3937 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3938 break;
3940 case 426: /* Connection closed, transer aborted. */
3941 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3942 break;
3944 case 530: /* Not logged in. Login incorrect. */
3945 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3946 break;
3948 case 421: /* Service not available - Server may be shutting down. */
3949 case 450: /* File action not taken. File may be busy. */
3950 case 451: /* Action aborted. Server error. */
3951 case 452: /* Action not taken. Insufficient storage space on server. */
3952 case 500: /* Syntax error. Command unrecognized. */
3953 case 501: /* Syntax error. Error in parameters or arguments. */
3954 case 502: /* Command not implemented. */
3955 case 503: /* Bad sequence of commands. */
3956 case 504: /* Command not implemented for that parameter. */
3957 case 532: /* Need account for storing files */
3958 case 550: /* File action not taken. File not found or no access. */
3959 case 551: /* Requested action aborted. Page type unknown */
3960 case 552: /* Action aborted. Exceeded storage allocation */
3961 case 553: /* Action not taken. File name not allowed. */
3963 default:
3964 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3965 break;
3968 INTERNET_SetLastError(dwCode);
3969 return dwCode;