qasf/tests: Fix a couple of spelling errors in ok() messages.
[wine/zf.git] / dlls / wininet / ftp.c
blob94491689df9f971bf82e05a567cdfcf1561e470c
1 /*
2 * WININET - Ftp implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2004 Mike McCormack for CodeWeavers
6 * Copyright 2004 Kevin Koltzau
7 * Copyright 2007 Hans Leidekker
9 * Ulrich Czekalla
10 * Noureddine Jemmali
12 * Copyright 2000 Andreas Mohr
13 * Copyright 2002 Jaco Greeff
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "ws2tcpip.h"
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <time.h>
37 #include <assert.h>
39 #include "windef.h"
40 #include "winbase.h"
41 #include "wingdi.h"
42 #include "winuser.h"
43 #include "wininet.h"
44 #include "winnls.h"
45 #include "winerror.h"
46 #include "winreg.h"
47 #include "winternl.h"
48 #include "shlwapi.h"
50 #include "wine/debug.h"
51 #include "internet.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
55 #define RESPONSE_TIMEOUT 30
57 typedef struct _ftp_session_t ftp_session_t;
59 typedef struct
61 object_header_t hdr;
62 ftp_session_t *lpFtpSession;
63 BOOL session_deleted;
64 int nDataSocket;
65 WCHAR *cache_file;
66 HANDLE cache_file_handle;
67 } ftp_file_t;
69 struct _ftp_session_t
71 object_header_t hdr;
72 appinfo_t *lpAppInfo;
73 int sndSocket;
74 int lstnSocket;
75 int pasvSocket; /* data socket connected by us in case of passive FTP */
76 ftp_file_t *download_in_progress;
77 struct sockaddr_in socketAddress;
78 struct sockaddr_in lstnSocketAddress;
79 LPWSTR servername;
80 INTERNET_PORT serverport;
81 LPWSTR lpszPassword;
82 LPWSTR lpszUserName;
85 typedef struct
87 BOOL bIsDirectory;
88 LPWSTR lpszName;
89 DWORD nSize;
90 SYSTEMTIME tmLastModified;
91 unsigned short permissions;
92 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
94 typedef struct
96 object_header_t hdr;
97 ftp_session_t *lpFtpSession;
98 DWORD index;
99 DWORD size;
100 LPFILEPROPERTIESW lpafp;
101 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
103 #define DATA_PACKET_SIZE 0x2000
104 #define szCRLF "\r\n"
105 #define MAX_BACKLOG 5
107 /* Testing shows that Windows only accepts dwFlags where the last
108 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
110 #define FTP_CONDITION_MASK 0x0007
112 typedef enum {
113 /* FTP commands with arguments. */
114 FTP_CMD_ACCT,
115 FTP_CMD_CWD,
116 FTP_CMD_DELE,
117 FTP_CMD_MKD,
118 FTP_CMD_PASS,
119 FTP_CMD_PORT,
120 FTP_CMD_RETR,
121 FTP_CMD_RMD,
122 FTP_CMD_RNFR,
123 FTP_CMD_RNTO,
124 FTP_CMD_STOR,
125 FTP_CMD_TYPE,
126 FTP_CMD_USER,
127 FTP_CMD_SIZE,
129 /* FTP commands without arguments. */
130 FTP_CMD_ABOR,
131 FTP_CMD_LIST,
132 FTP_CMD_NLST,
133 FTP_CMD_PASV,
134 FTP_CMD_PWD,
135 FTP_CMD_QUIT,
136 } FTP_COMMAND;
138 static const CHAR *const szFtpCommands[] = {
139 "ACCT",
140 "CWD",
141 "DELE",
142 "MKD",
143 "PASS",
144 "PORT",
145 "RETR",
146 "RMD",
147 "RNFR",
148 "RNTO",
149 "STOR",
150 "TYPE",
151 "USER",
152 "SIZE",
153 "ABOR",
154 "LIST",
155 "NLST",
156 "PASV",
157 "PWD",
158 "QUIT",
161 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
162 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
164 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
165 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext);
166 static BOOL FTP_SendStore(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
167 static BOOL FTP_GetDataSocket(ftp_session_t*, LPINT nDataSocket);
168 static BOOL FTP_SendData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
169 static INT FTP_ReceiveResponse(ftp_session_t*, DWORD_PTR dwContext);
170 static BOOL FTP_SendRetrieve(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
171 static BOOL FTP_RetrieveFileData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
172 static BOOL FTP_InitListenSocket(ftp_session_t*);
173 static BOOL FTP_ConnectToHost(ftp_session_t*);
174 static BOOL FTP_SendPassword(ftp_session_t*);
175 static BOOL FTP_SendAccount(ftp_session_t*);
176 static BOOL FTP_SendType(ftp_session_t*, DWORD dwType);
177 static BOOL FTP_SendPort(ftp_session_t*);
178 static BOOL FTP_DoPassive(ftp_session_t*);
179 static BOOL FTP_SendPortOrPasv(ftp_session_t*);
180 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
181 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
182 static BOOL FTP_ParseDirectory(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
183 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
184 static HINTERNET FTP_ReceiveFileList(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
185 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
186 static DWORD FTP_SetResponseError(DWORD dwResponse);
187 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
188 static BOOL FTP_FtpPutFileW(ftp_session_t*, LPCWSTR lpszLocalFile,
189 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
190 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
191 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
192 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t*,
193 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
194 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t*, LPWSTR lpszCurrentDirectory,
195 LPDWORD lpdwCurrentDirectory);
196 static BOOL FTP_FtpRenameFileW(ftp_session_t*, LPCWSTR lpszSrc, LPCWSTR lpszDest);
197 static BOOL FTP_FtpRemoveDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
198 static BOOL FTP_FtpDeleteFileW(ftp_session_t*, LPCWSTR lpszFileName);
199 static BOOL FTP_FtpGetFileW(ftp_session_t*, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
200 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
201 DWORD_PTR dwContext);
203 /* A temporary helper until we get rid of INTERNET_GetLastError calls */
204 static BOOL res_to_le(DWORD res)
206 if(res != ERROR_SUCCESS)
207 INTERNET_SetLastError(res);
208 return res == ERROR_SUCCESS;
211 /***********************************************************************
212 * FtpPutFileA (WININET.@)
214 * Uploads a file to the FTP server
216 * RETURNS
217 * TRUE on success
218 * FALSE on failure
221 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
222 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
224 LPWSTR lpwzLocalFile;
225 LPWSTR lpwzNewRemoteFile;
226 BOOL ret;
228 lpwzLocalFile = heap_strdupAtoW(lpszLocalFile);
229 lpwzNewRemoteFile = heap_strdupAtoW(lpszNewRemoteFile);
230 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
231 dwFlags, dwContext);
232 heap_free(lpwzLocalFile);
233 heap_free(lpwzNewRemoteFile);
234 return ret;
237 typedef struct {
238 task_header_t hdr;
239 WCHAR *local_file;
240 WCHAR *remote_file;
241 DWORD flags;
242 DWORD_PTR context;
243 } put_file_task_t;
245 static void AsyncFtpPutFileProc(task_header_t *hdr)
247 put_file_task_t *task = (put_file_task_t*)hdr;
248 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
250 TRACE("%p\n", session);
252 FTP_FtpPutFileW(session, task->local_file, task->remote_file,
253 task->flags, task->context);
255 heap_free(task->local_file);
256 heap_free(task->remote_file);
259 /***********************************************************************
260 * FtpPutFileW (WININET.@)
262 * Uploads a file to the FTP server
264 * RETURNS
265 * TRUE on success
266 * FALSE on failure
269 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
270 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
272 ftp_session_t *lpwfs;
273 appinfo_t *hIC = NULL;
274 BOOL r = FALSE;
276 if (!lpszLocalFile || !lpszNewRemoteFile)
278 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
279 return FALSE;
282 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
283 if (!lpwfs)
285 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
286 return FALSE;
289 if (WH_HFTPSESSION != lpwfs->hdr.htype)
291 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
292 goto lend;
295 if (lpwfs->download_in_progress != NULL)
297 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
298 goto lend;
301 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
303 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
304 goto lend;
307 hIC = lpwfs->lpAppInfo;
308 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
310 put_file_task_t *task = alloc_async_task(&lpwfs->hdr, AsyncFtpPutFileProc, sizeof(*task));
312 task->local_file = heap_strdupW(lpszLocalFile);
313 task->remote_file = heap_strdupW(lpszNewRemoteFile);
314 task->flags = dwFlags;
315 task->context = dwContext;
317 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
319 else
321 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
322 lpszNewRemoteFile, dwFlags, dwContext);
325 lend:
326 WININET_Release( &lpwfs->hdr );
328 return r;
331 /***********************************************************************
332 * FTP_FtpPutFileW (Internal)
334 * Uploads a file to the FTP server
336 * RETURNS
337 * TRUE on success
338 * FALSE on failure
341 static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
342 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
344 HANDLE hFile;
345 BOOL bSuccess = FALSE;
346 appinfo_t *hIC = NULL;
347 INT nResCode;
349 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
351 /* Clear any error information */
352 INTERNET_SetLastError(0);
354 /* Open file to be uploaded */
355 if (INVALID_HANDLE_VALUE ==
356 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
357 /* Let CreateFile set the appropriate error */
358 return FALSE;
360 hIC = lpwfs->lpAppInfo;
362 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
364 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
366 INT nDataSocket;
368 /* Get data socket to server */
369 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
371 FTP_SendData(lpwfs, nDataSocket, hFile);
372 closesocket(nDataSocket);
373 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
374 if (nResCode)
376 if (nResCode == 226)
377 bSuccess = TRUE;
378 else
379 FTP_SetResponseError(nResCode);
384 if (lpwfs->lstnSocket != -1)
386 closesocket(lpwfs->lstnSocket);
387 lpwfs->lstnSocket = -1;
390 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
392 INTERNET_ASYNC_RESULT iar;
394 iar.dwResult = (DWORD)bSuccess;
395 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
396 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
397 &iar, sizeof(INTERNET_ASYNC_RESULT));
400 CloseHandle(hFile);
402 return bSuccess;
406 /***********************************************************************
407 * FtpSetCurrentDirectoryA (WININET.@)
409 * Change the working directory on the FTP server
411 * RETURNS
412 * TRUE on success
413 * FALSE on failure
416 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
418 LPWSTR lpwzDirectory;
419 BOOL ret;
421 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
422 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
423 heap_free(lpwzDirectory);
424 return ret;
427 typedef struct {
428 task_header_t hdr;
429 WCHAR *directory;
430 } directory_task_t;
432 static void AsyncFtpSetCurrentDirectoryProc(task_header_t *hdr)
434 directory_task_t *task = (directory_task_t*)hdr;
435 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
437 TRACE("%p\n", session);
439 FTP_FtpSetCurrentDirectoryW(session, task->directory);
440 heap_free(task->directory);
443 /***********************************************************************
444 * FtpSetCurrentDirectoryW (WININET.@)
446 * Change the working directory on the FTP server
448 * RETURNS
449 * TRUE on success
450 * FALSE on failure
453 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
455 ftp_session_t *lpwfs = NULL;
456 appinfo_t *hIC = NULL;
457 BOOL r = FALSE;
459 if (!lpszDirectory)
461 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
462 goto lend;
465 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
466 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
468 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
469 goto lend;
472 if (lpwfs->download_in_progress != NULL)
474 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
475 goto lend;
478 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
480 hIC = lpwfs->lpAppInfo;
481 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
483 directory_task_t *task;
485 task = alloc_async_task(&lpwfs->hdr, AsyncFtpSetCurrentDirectoryProc, sizeof(*task));
486 task->directory = heap_strdupW(lpszDirectory);
488 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
490 else
492 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
495 lend:
496 if( lpwfs )
497 WININET_Release( &lpwfs->hdr );
499 return r;
503 /***********************************************************************
504 * FTP_FtpSetCurrentDirectoryW (Internal)
506 * Change the working directory on the FTP server
508 * RETURNS
509 * TRUE on success
510 * FALSE on failure
513 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
515 INT nResCode;
516 appinfo_t *hIC = NULL;
517 BOOL bSuccess = FALSE;
519 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
521 /* Clear any error information */
522 INTERNET_SetLastError(0);
524 hIC = lpwfs->lpAppInfo;
525 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
526 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
527 goto lend;
529 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
531 if (nResCode)
533 if (nResCode == 250)
534 bSuccess = TRUE;
535 else
536 FTP_SetResponseError(nResCode);
539 lend:
540 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
542 INTERNET_ASYNC_RESULT iar;
544 iar.dwResult = bSuccess;
545 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
546 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
547 &iar, sizeof(INTERNET_ASYNC_RESULT));
549 return bSuccess;
553 /***********************************************************************
554 * FtpCreateDirectoryA (WININET.@)
556 * Create new directory on the FTP server
558 * RETURNS
559 * TRUE on success
560 * FALSE on failure
563 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
565 LPWSTR lpwzDirectory;
566 BOOL ret;
568 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
569 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
570 heap_free(lpwzDirectory);
571 return ret;
575 static void AsyncFtpCreateDirectoryProc(task_header_t *hdr)
577 directory_task_t *task = (directory_task_t*)hdr;
578 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
580 TRACE(" %p\n", session);
582 FTP_FtpCreateDirectoryW(session, task->directory);
583 heap_free(task->directory);
586 /***********************************************************************
587 * FtpCreateDirectoryW (WININET.@)
589 * Create new directory on the FTP server
591 * RETURNS
592 * TRUE on success
593 * FALSE on failure
596 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
598 ftp_session_t *lpwfs;
599 appinfo_t *hIC = NULL;
600 BOOL r = FALSE;
602 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
603 if (!lpwfs)
605 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
606 return FALSE;
609 if (WH_HFTPSESSION != lpwfs->hdr.htype)
611 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
612 goto lend;
615 if (lpwfs->download_in_progress != NULL)
617 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
618 goto lend;
621 if (!lpszDirectory)
623 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
624 goto lend;
627 hIC = lpwfs->lpAppInfo;
628 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
630 directory_task_t *task;
632 task = alloc_async_task(&lpwfs->hdr, AsyncFtpCreateDirectoryProc, sizeof(*task));
633 task->directory = heap_strdupW(lpszDirectory);
635 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
637 else
639 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
641 lend:
642 WININET_Release( &lpwfs->hdr );
644 return r;
648 /***********************************************************************
649 * FTP_FtpCreateDirectoryW (Internal)
651 * Create new directory on the FTP server
653 * RETURNS
654 * TRUE on success
655 * FALSE on failure
658 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
660 INT nResCode;
661 BOOL bSuccess = FALSE;
662 appinfo_t *hIC = NULL;
664 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
666 /* Clear any error information */
667 INTERNET_SetLastError(0);
669 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
670 goto lend;
672 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
673 if (nResCode)
675 if (nResCode == 257)
676 bSuccess = TRUE;
677 else
678 FTP_SetResponseError(nResCode);
681 lend:
682 hIC = lpwfs->lpAppInfo;
683 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
685 INTERNET_ASYNC_RESULT iar;
687 iar.dwResult = (DWORD)bSuccess;
688 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
689 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
690 &iar, sizeof(INTERNET_ASYNC_RESULT));
693 return bSuccess;
696 /***********************************************************************
697 * FtpFindFirstFileA (WININET.@)
699 * Search the specified directory
701 * RETURNS
702 * HINTERNET on success
703 * NULL on failure
706 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
707 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
709 LPWSTR lpwzSearchFile;
710 WIN32_FIND_DATAW wfd;
711 LPWIN32_FIND_DATAW lpFindFileDataW;
712 HINTERNET ret;
714 lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
715 lpFindFileDataW = lpFindFileData?&wfd:NULL;
716 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
717 heap_free(lpwzSearchFile);
719 if (ret && lpFindFileData)
720 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
722 return ret;
725 typedef struct {
726 task_header_t hdr;
727 WCHAR *search_file;
728 WIN32_FIND_DATAW *find_file_data;
729 DWORD flags;
730 DWORD_PTR context;
731 } find_first_file_task_t;
733 static void AsyncFtpFindFirstFileProc(task_header_t *hdr)
735 find_first_file_task_t *task = (find_first_file_task_t*)hdr;
736 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
738 TRACE("%p\n", session);
740 FTP_FtpFindFirstFileW(session, task->search_file, task->find_file_data, task->flags, task->context);
741 heap_free(task->search_file);
744 /***********************************************************************
745 * FtpFindFirstFileW (WININET.@)
747 * Search the specified directory
749 * RETURNS
750 * HINTERNET on success
751 * NULL on failure
754 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
755 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
757 ftp_session_t *lpwfs;
758 appinfo_t *hIC = NULL;
759 HINTERNET r = NULL;
761 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
762 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
764 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
765 goto lend;
768 if (lpwfs->download_in_progress != NULL)
770 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
771 goto lend;
774 hIC = lpwfs->lpAppInfo;
775 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
777 find_first_file_task_t *task;
779 task = alloc_async_task(&lpwfs->hdr, AsyncFtpFindFirstFileProc, sizeof(*task));
780 task->search_file = heap_strdupW(lpszSearchFile);
781 task->find_file_data = lpFindFileData;
782 task->flags = dwFlags;
783 task->context = dwContext;
785 INTERNET_AsyncCall(&task->hdr);
786 r = NULL;
788 else
790 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
791 dwFlags, dwContext);
793 lend:
794 if( lpwfs )
795 WININET_Release( &lpwfs->hdr );
797 return r;
801 /***********************************************************************
802 * FTP_FtpFindFirstFileW (Internal)
804 * Search the specified directory
806 * RETURNS
807 * HINTERNET on success
808 * NULL on failure
811 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
812 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
814 INT nResCode;
815 appinfo_t *hIC = NULL;
816 HINTERNET hFindNext = NULL;
817 LPWSTR lpszSearchPath = NULL;
819 TRACE("\n");
821 /* Clear any error information */
822 INTERNET_SetLastError(0);
824 if (!FTP_InitListenSocket(lpwfs))
825 goto lend;
827 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
828 goto lend;
830 if (!FTP_SendPortOrPasv(lpwfs))
831 goto lend;
833 /* split search path into file and path */
834 if (lpszSearchFile)
836 LPCWSTR name = lpszSearchFile, p;
837 if ((p = wcsrchr( name, '\\' ))) name = p + 1;
838 if ((p = wcsrchr( name, '/' ))) name = p + 1;
839 if (name != lpszSearchFile)
841 lpszSearchPath = heap_strndupW(lpszSearchFile, name - lpszSearchFile);
842 lpszSearchFile = name;
846 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, lpszSearchPath,
847 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
848 goto lend;
850 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
851 if (nResCode)
853 if (nResCode == 125 || nResCode == 150)
855 INT nDataSocket;
857 /* Get data socket to server */
858 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
860 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
861 closesocket(nDataSocket);
862 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
863 if (nResCode != 226 && nResCode != 250)
864 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
867 else
868 FTP_SetResponseError(nResCode);
871 lend:
872 heap_free(lpszSearchPath);
874 if (lpwfs->lstnSocket != -1)
876 closesocket(lpwfs->lstnSocket);
877 lpwfs->lstnSocket = -1;
880 hIC = lpwfs->lpAppInfo;
881 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
883 INTERNET_ASYNC_RESULT iar;
885 if (hFindNext)
887 iar.dwResult = (DWORD_PTR)hFindNext;
888 iar.dwError = ERROR_SUCCESS;
889 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
890 &iar, sizeof(INTERNET_ASYNC_RESULT));
893 iar.dwResult = (DWORD_PTR)hFindNext;
894 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
895 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
896 &iar, sizeof(INTERNET_ASYNC_RESULT));
899 return hFindNext;
903 /***********************************************************************
904 * FtpGetCurrentDirectoryA (WININET.@)
906 * Retrieves the current directory
908 * RETURNS
909 * TRUE on success
910 * FALSE on failure
913 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
914 LPDWORD lpdwCurrentDirectory)
916 WCHAR *dir = NULL;
917 DWORD len;
918 BOOL ret;
920 if(lpdwCurrentDirectory) {
921 len = *lpdwCurrentDirectory;
922 if(lpszCurrentDirectory)
924 dir = heap_alloc(len * sizeof(WCHAR));
925 if (NULL == dir)
927 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
928 return FALSE;
932 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
934 if (ret && lpszCurrentDirectory)
935 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
937 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
938 heap_free(dir);
939 return ret;
942 typedef struct {
943 task_header_t hdr;
944 WCHAR *directory;
945 DWORD *directory_len;
946 } get_current_dir_task_t;
948 static void AsyncFtpGetCurrentDirectoryProc(task_header_t *hdr)
950 get_current_dir_task_t *task = (get_current_dir_task_t*)hdr;
951 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
953 TRACE("%p\n", session);
955 FTP_FtpGetCurrentDirectoryW(session, task->directory, task->directory_len);
958 /***********************************************************************
959 * FtpGetCurrentDirectoryW (WININET.@)
961 * Retrieves the current directory
963 * RETURNS
964 * TRUE on success
965 * FALSE on failure
968 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
969 LPDWORD lpdwCurrentDirectory)
971 ftp_session_t *lpwfs;
972 appinfo_t *hIC = NULL;
973 BOOL r = FALSE;
975 TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
977 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
978 if (NULL == lpwfs)
980 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
981 goto lend;
984 if (WH_HFTPSESSION != lpwfs->hdr.htype)
986 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
987 goto lend;
990 if (!lpdwCurrentDirectory)
992 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
993 goto lend;
996 if (lpszCurrentDirectory == NULL)
998 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
999 goto lend;
1002 if (lpwfs->download_in_progress != NULL)
1004 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1005 goto lend;
1008 hIC = lpwfs->lpAppInfo;
1009 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1011 get_current_dir_task_t *task;
1013 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetCurrentDirectoryProc, sizeof(*task));
1014 task->directory = lpszCurrentDirectory;
1015 task->directory_len = lpdwCurrentDirectory;
1017 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1019 else
1021 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
1022 lpdwCurrentDirectory);
1025 lend:
1026 if( lpwfs )
1027 WININET_Release( &lpwfs->hdr );
1029 return r;
1033 /***********************************************************************
1034 * FTP_FtpGetCurrentDirectoryW (Internal)
1036 * Retrieves the current directory
1038 * RETURNS
1039 * TRUE on success
1040 * FALSE on failure
1043 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
1044 LPDWORD lpdwCurrentDirectory)
1046 INT nResCode;
1047 appinfo_t *hIC = NULL;
1048 BOOL bSuccess = FALSE;
1050 /* Clear any error information */
1051 INTERNET_SetLastError(0);
1053 hIC = lpwfs->lpAppInfo;
1054 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1055 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1056 goto lend;
1058 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1059 if (nResCode)
1061 if (nResCode == 257) /* Extract directory name */
1063 DWORD firstpos, lastpos, len;
1064 LPWSTR lpszResponseBuffer = heap_strdupAtoW(INTERNET_GetResponseBuffer());
1066 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1068 if ('"' == lpszResponseBuffer[lastpos])
1070 if (!firstpos)
1071 firstpos = lastpos;
1072 else
1073 break;
1076 len = lastpos - firstpos;
1077 if (*lpdwCurrentDirectory >= len)
1079 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1080 lpszCurrentDirectory[len - 1] = 0;
1081 *lpdwCurrentDirectory = len;
1082 bSuccess = TRUE;
1084 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1086 heap_free(lpszResponseBuffer);
1088 else
1089 FTP_SetResponseError(nResCode);
1092 lend:
1093 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1095 INTERNET_ASYNC_RESULT iar;
1097 iar.dwResult = bSuccess;
1098 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1099 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1100 &iar, sizeof(INTERNET_ASYNC_RESULT));
1103 return bSuccess;
1107 /***********************************************************************
1108 * FTPFILE_Destroy(internal)
1110 * Closes the file transfer handle. This also 'cleans' the data queue of
1111 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1114 static void FTPFILE_Destroy(object_header_t *hdr)
1116 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1117 ftp_session_t *lpwfs = lpwh->lpFtpSession;
1118 INT nResCode;
1120 TRACE("\n");
1122 if (lpwh->cache_file_handle != INVALID_HANDLE_VALUE)
1123 CloseHandle(lpwh->cache_file_handle);
1125 heap_free(lpwh->cache_file);
1127 if (!lpwh->session_deleted)
1128 lpwfs->download_in_progress = NULL;
1130 if (lpwh->nDataSocket != -1)
1131 closesocket(lpwh->nDataSocket);
1133 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1134 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1136 WININET_Release(&lpwh->lpFtpSession->hdr);
1139 static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1141 switch(option) {
1142 case INTERNET_OPTION_HANDLE_TYPE:
1143 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1145 if (*size < sizeof(ULONG))
1146 return ERROR_INSUFFICIENT_BUFFER;
1148 *size = sizeof(DWORD);
1149 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1150 return ERROR_SUCCESS;
1151 case INTERNET_OPTION_DATAFILE_NAME:
1153 DWORD required;
1154 ftp_file_t *file = (ftp_file_t *)hdr;
1156 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1158 if (!file->cache_file)
1160 *size = 0;
1161 return ERROR_INTERNET_ITEM_NOT_FOUND;
1163 if (unicode)
1165 required = (lstrlenW(file->cache_file) + 1) * sizeof(WCHAR);
1166 if (*size < required)
1167 return ERROR_INSUFFICIENT_BUFFER;
1169 *size = required;
1170 memcpy(buffer, file->cache_file, *size);
1171 return ERROR_SUCCESS;
1173 else
1175 required = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, NULL, 0, NULL, NULL);
1176 if (required > *size)
1177 return ERROR_INSUFFICIENT_BUFFER;
1179 *size = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, buffer, *size, NULL, NULL);
1180 return ERROR_SUCCESS;
1184 return INET_QueryOption(hdr, option, buffer, size, unicode);
1187 static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read,
1188 DWORD flags, DWORD_PTR context)
1190 ftp_file_t *file = (ftp_file_t*)hdr;
1191 int res;
1192 DWORD error;
1194 if (file->nDataSocket == -1)
1195 return ERROR_INTERNET_DISCONNECTED;
1197 /* FIXME: FTP should use NETCON_ stuff */
1198 res = sock_recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1199 *read = res>0 ? res : 0;
1201 error = res >= 0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME */
1202 if (error == ERROR_SUCCESS && file->cache_file)
1204 DWORD bytes_written;
1206 if (!WriteFile(file->cache_file_handle, buffer, *read, &bytes_written, NULL))
1207 WARN("WriteFile failed: %u\n", GetLastError());
1209 return error;
1212 static DWORD FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1214 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1215 int res;
1217 res = sock_send(lpwh->nDataSocket, buffer, size, 0);
1219 *written = res>0 ? res : 0;
1220 return res >= 0 ? ERROR_SUCCESS : WSAGetLastError();
1223 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
1225 INTERNET_ASYNC_RESULT iar;
1226 BYTE buffer[4096];
1227 int available;
1229 TRACE("%p\n", file);
1231 available = sock_recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1233 if(available != -1) {
1234 iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1235 iar.dwError = first_notif ? 0 : available;
1236 }else {
1237 iar.dwResult = 0;
1238 iar.dwError = INTERNET_GetLastError();
1241 INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1242 sizeof(INTERNET_ASYNC_RESULT));
1245 static void FTPFILE_AsyncQueryDataAvailableProc(task_header_t *task)
1247 ftp_file_t *file = (ftp_file_t*)task->hdr;
1249 FTP_ReceiveRequestData(file, FALSE);
1252 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1254 ftp_file_t *file = (ftp_file_t*) hdr;
1255 ULONG unread = 0;
1256 int retval;
1258 TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
1260 retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
1261 if (!retval)
1262 TRACE("%d bytes of queued, but unread data\n", unread);
1264 *available = unread;
1266 if(!unread) {
1267 BYTE byte;
1269 *available = 0;
1271 retval = sock_recv(file->nDataSocket, &byte, 1, MSG_PEEK);
1272 if(retval > 0) {
1273 task_header_t *task;
1275 task = alloc_async_task(&file->hdr, FTPFILE_AsyncQueryDataAvailableProc, sizeof(*task));
1276 INTERNET_AsyncCall(task);
1278 return ERROR_IO_PENDING;
1282 return ERROR_SUCCESS;
1285 static DWORD FTPFILE_LockRequestFile(object_header_t *hdr, req_file_t **ret)
1287 ftp_file_t *file = (ftp_file_t*)hdr;
1288 FIXME("%p\n", file);
1289 return ERROR_NOT_SUPPORTED;
1292 static const object_vtbl_t FTPFILEVtbl = {
1293 FTPFILE_Destroy,
1294 NULL,
1295 FTPFILE_QueryOption,
1296 INET_SetOption,
1297 FTPFILE_ReadFile,
1298 FTPFILE_WriteFile,
1299 FTPFILE_QueryDataAvailable,
1300 NULL,
1301 FTPFILE_LockRequestFile
1304 /***********************************************************************
1305 * FTP_FtpOpenFileW (Internal)
1307 * Open a remote file for writing or reading
1309 * RETURNS
1310 * HINTERNET handle on success
1311 * NULL on failure
1314 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1315 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1316 DWORD_PTR dwContext)
1318 INT nDataSocket;
1319 BOOL bSuccess = FALSE;
1320 ftp_file_t *lpwh = NULL;
1321 appinfo_t *hIC = NULL;
1323 TRACE("\n");
1325 /* Clear any error information */
1326 INTERNET_SetLastError(0);
1328 if (GENERIC_READ == fdwAccess)
1330 /* Set up socket to retrieve data */
1331 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1333 else if (GENERIC_WRITE == fdwAccess)
1335 /* Set up socket to send data */
1336 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1339 /* Get data socket to server */
1340 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1342 lpwh = alloc_object(&lpwfs->hdr, &FTPFILEVtbl, sizeof(ftp_file_t));
1343 lpwh->hdr.htype = WH_HFILE;
1344 lpwh->hdr.dwFlags = dwFlags;
1345 lpwh->hdr.dwContext = dwContext;
1346 lpwh->nDataSocket = nDataSocket;
1347 lpwh->cache_file = NULL;
1348 lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1349 lpwh->session_deleted = FALSE;
1351 WININET_AddRef( &lpwfs->hdr );
1352 lpwh->lpFtpSession = lpwfs;
1353 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1355 /* Indicate that a download is currently in progress */
1356 lpwfs->download_in_progress = lpwh;
1359 if (lpwfs->lstnSocket != -1)
1361 closesocket(lpwfs->lstnSocket);
1362 lpwfs->lstnSocket = -1;
1365 if (bSuccess && fdwAccess == GENERIC_READ)
1367 WCHAR filename[MAX_PATH + 1];
1368 URL_COMPONENTSW uc;
1369 DWORD len;
1371 memset(&uc, 0, sizeof(uc));
1372 uc.dwStructSize = sizeof(uc);
1373 uc.nScheme = INTERNET_SCHEME_FTP;
1374 uc.lpszHostName = lpwfs->servername;
1375 uc.nPort = lpwfs->serverport;
1376 uc.lpszUserName = lpwfs->lpszUserName;
1377 uc.lpszUrlPath = heap_strdupW(lpszFileName);
1379 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1381 WCHAR *url = heap_alloc(len * sizeof(WCHAR));
1383 if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1385 lpwh->cache_file = heap_strdupW(filename);
1386 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1387 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1388 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1390 WARN("Could not create cache file: %u\n", GetLastError());
1391 heap_free(lpwh->cache_file);
1392 lpwh->cache_file = NULL;
1395 heap_free(url);
1397 heap_free(uc.lpszUrlPath);
1400 hIC = lpwfs->lpAppInfo;
1401 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1403 INTERNET_ASYNC_RESULT iar;
1405 if (lpwh)
1407 iar.dwResult = (DWORD_PTR)lpwh->hdr.hInternet;
1408 iar.dwError = ERROR_SUCCESS;
1409 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1410 &iar, sizeof(INTERNET_ASYNC_RESULT));
1413 if(bSuccess) {
1414 FTP_ReceiveRequestData(lpwh, TRUE);
1415 }else {
1416 iar.dwResult = 0;
1417 iar.dwError = INTERNET_GetLastError();
1418 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1419 &iar, sizeof(INTERNET_ASYNC_RESULT));
1423 if(!bSuccess)
1424 return FALSE;
1426 return lpwh->hdr.hInternet;
1430 /***********************************************************************
1431 * FtpOpenFileA (WININET.@)
1433 * Open a remote file for writing or reading
1435 * RETURNS
1436 * HINTERNET handle on success
1437 * NULL on failure
1440 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1441 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1442 DWORD_PTR dwContext)
1444 LPWSTR lpwzFileName;
1445 HINTERNET ret;
1447 lpwzFileName = heap_strdupAtoW(lpszFileName);
1448 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1449 heap_free(lpwzFileName);
1450 return ret;
1453 typedef struct {
1454 task_header_t hdr;
1455 WCHAR *file_name;
1456 DWORD access;
1457 DWORD flags;
1458 DWORD_PTR context;
1459 } open_file_task_t;
1461 static void AsyncFtpOpenFileProc(task_header_t *hdr)
1463 open_file_task_t *task = (open_file_task_t*)hdr;
1464 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1466 TRACE("%p\n", session);
1468 FTP_FtpOpenFileW(session, task->file_name, task->access, task->flags, task->context);
1469 heap_free(task->file_name);
1472 /***********************************************************************
1473 * FtpOpenFileW (WININET.@)
1475 * Open a remote file for writing or reading
1477 * RETURNS
1478 * HINTERNET handle on success
1479 * NULL on failure
1482 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1483 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1484 DWORD_PTR dwContext)
1486 ftp_session_t *lpwfs;
1487 appinfo_t *hIC = NULL;
1488 HINTERNET r = NULL;
1490 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1491 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1493 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1494 if (!lpwfs)
1496 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1497 return FALSE;
1500 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1502 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1503 goto lend;
1506 if ((!lpszFileName) ||
1507 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1508 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1510 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1511 goto lend;
1514 if (lpwfs->download_in_progress != NULL)
1516 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1517 goto lend;
1520 hIC = lpwfs->lpAppInfo;
1521 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1523 open_file_task_t *task;
1525 task = alloc_async_task(&lpwfs->hdr, AsyncFtpOpenFileProc, sizeof(*task));
1526 task->file_name = heap_strdupW(lpszFileName);
1527 task->access = fdwAccess;
1528 task->flags = dwFlags;
1529 task->context = dwContext;
1531 INTERNET_AsyncCall(&task->hdr);
1532 r = NULL;
1534 else
1536 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1539 lend:
1540 WININET_Release( &lpwfs->hdr );
1542 return r;
1546 /***********************************************************************
1547 * FtpGetFileA (WININET.@)
1549 * Retrieve file from the FTP server
1551 * RETURNS
1552 * TRUE on success
1553 * FALSE on failure
1556 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1557 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1558 DWORD_PTR dwContext)
1560 LPWSTR lpwzRemoteFile;
1561 LPWSTR lpwzNewFile;
1562 BOOL ret;
1564 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1565 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1566 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1567 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1568 heap_free(lpwzRemoteFile);
1569 heap_free(lpwzNewFile);
1570 return ret;
1573 typedef struct {
1574 task_header_t hdr;
1575 WCHAR *remote_file;
1576 WCHAR *new_file;
1577 BOOL fail_if_exists;
1578 DWORD local_attr;
1579 DWORD flags;
1580 DWORD_PTR context;
1581 } get_file_task_t;
1583 static void AsyncFtpGetFileProc(task_header_t *hdr)
1585 get_file_task_t *task = (get_file_task_t*)hdr;
1586 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1588 TRACE("%p\n", session);
1590 FTP_FtpGetFileW(session, task->remote_file, task->new_file, task->fail_if_exists,
1591 task->local_attr, task->flags, task->context);
1592 heap_free(task->remote_file);
1593 heap_free(task->new_file);
1597 /***********************************************************************
1598 * FtpGetFileW (WININET.@)
1600 * Retrieve file from the FTP server
1602 * RETURNS
1603 * TRUE on success
1604 * FALSE on failure
1607 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1608 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1609 DWORD_PTR dwContext)
1611 ftp_session_t *lpwfs;
1612 appinfo_t *hIC = NULL;
1613 BOOL r = FALSE;
1615 if (!lpszRemoteFile || !lpszNewFile)
1617 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1618 return FALSE;
1621 lpwfs = (ftp_session_t*) get_handle_object( hInternet );
1622 if (!lpwfs)
1624 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1625 return FALSE;
1628 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1630 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1631 goto lend;
1634 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1636 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1637 goto lend;
1640 if (lpwfs->download_in_progress != NULL)
1642 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1643 goto lend;
1646 hIC = lpwfs->lpAppInfo;
1647 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1649 get_file_task_t *task;
1651 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetFileProc, sizeof(*task));
1652 task->remote_file = heap_strdupW(lpszRemoteFile);
1653 task->new_file = heap_strdupW(lpszNewFile);
1654 task->local_attr = dwLocalFlagsAttribute;
1655 task->fail_if_exists = fFailIfExists;
1656 task->flags = dwInternetFlags;
1657 task->context = dwContext;
1659 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1661 else
1663 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1664 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1667 lend:
1668 WININET_Release( &lpwfs->hdr );
1670 return r;
1674 /***********************************************************************
1675 * FTP_FtpGetFileW (Internal)
1677 * Retrieve file from the FTP server
1679 * RETURNS
1680 * TRUE on success
1681 * FALSE on failure
1684 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1685 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1686 DWORD_PTR dwContext)
1688 BOOL bSuccess = FALSE;
1689 HANDLE hFile;
1690 appinfo_t *hIC = NULL;
1692 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1694 /* Clear any error information */
1695 INTERNET_SetLastError(0);
1697 /* Ensure we can write to lpszNewfile by opening it */
1698 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1699 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1700 if (INVALID_HANDLE_VALUE == hFile)
1701 return FALSE;
1703 /* Set up socket to retrieve data */
1704 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1706 INT nDataSocket;
1708 /* Get data socket to server */
1709 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1711 INT nResCode;
1713 /* Receive data */
1714 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1715 closesocket(nDataSocket);
1717 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1718 if (nResCode)
1720 if (nResCode == 226)
1721 bSuccess = TRUE;
1722 else
1723 FTP_SetResponseError(nResCode);
1728 if (lpwfs->lstnSocket != -1)
1730 closesocket(lpwfs->lstnSocket);
1731 lpwfs->lstnSocket = -1;
1734 CloseHandle(hFile);
1736 hIC = lpwfs->lpAppInfo;
1737 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1739 INTERNET_ASYNC_RESULT iar;
1741 iar.dwResult = (DWORD)bSuccess;
1742 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1743 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1744 &iar, sizeof(INTERNET_ASYNC_RESULT));
1747 if (!bSuccess) DeleteFileW(lpszNewFile);
1748 return bSuccess;
1751 /***********************************************************************
1752 * FtpGetFileSize (WININET.@)
1754 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1756 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1758 if (lpdwFileSizeHigh)
1759 *lpdwFileSizeHigh = 0;
1761 return 0;
1764 /***********************************************************************
1765 * FtpDeleteFileA (WININET.@)
1767 * Delete a file on the ftp server
1769 * RETURNS
1770 * TRUE on success
1771 * FALSE on failure
1774 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1776 LPWSTR lpwzFileName;
1777 BOOL ret;
1779 lpwzFileName = heap_strdupAtoW(lpszFileName);
1780 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1781 heap_free(lpwzFileName);
1782 return ret;
1785 typedef struct {
1786 task_header_t hdr;
1787 WCHAR *file_name;
1788 } delete_file_task_t;
1790 static void AsyncFtpDeleteFileProc(task_header_t *hdr)
1792 delete_file_task_t *task = (delete_file_task_t*)hdr;
1793 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1795 TRACE("%p\n", session);
1797 FTP_FtpDeleteFileW(session, task->file_name);
1798 heap_free(task->file_name);
1801 /***********************************************************************
1802 * FtpDeleteFileW (WININET.@)
1804 * Delete a file on the ftp server
1806 * RETURNS
1807 * TRUE on success
1808 * FALSE on failure
1811 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1813 ftp_session_t *lpwfs;
1814 appinfo_t *hIC = NULL;
1815 BOOL r = FALSE;
1817 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1818 if (!lpwfs)
1820 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1821 return FALSE;
1824 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1826 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1827 goto lend;
1830 if (lpwfs->download_in_progress != NULL)
1832 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1833 goto lend;
1836 if (!lpszFileName)
1838 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1839 goto lend;
1842 hIC = lpwfs->lpAppInfo;
1843 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1845 delete_file_task_t *task;
1847 task = alloc_async_task(&lpwfs->hdr, AsyncFtpDeleteFileProc, sizeof(*task));
1848 task->file_name = heap_strdupW(lpszFileName);
1850 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1852 else
1854 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1857 lend:
1858 WININET_Release( &lpwfs->hdr );
1860 return r;
1863 /***********************************************************************
1864 * FTP_FtpDeleteFileW (Internal)
1866 * Delete a file on the ftp server
1868 * RETURNS
1869 * TRUE on success
1870 * FALSE on failure
1873 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1875 INT nResCode;
1876 BOOL bSuccess = FALSE;
1877 appinfo_t *hIC = NULL;
1879 TRACE("%p\n", lpwfs);
1881 /* Clear any error information */
1882 INTERNET_SetLastError(0);
1884 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1885 goto lend;
1887 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1888 if (nResCode)
1890 if (nResCode == 250)
1891 bSuccess = TRUE;
1892 else
1893 FTP_SetResponseError(nResCode);
1895 lend:
1896 hIC = lpwfs->lpAppInfo;
1897 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1899 INTERNET_ASYNC_RESULT iar;
1901 iar.dwResult = (DWORD)bSuccess;
1902 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1903 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1904 &iar, sizeof(INTERNET_ASYNC_RESULT));
1907 return bSuccess;
1911 /***********************************************************************
1912 * FtpRemoveDirectoryA (WININET.@)
1914 * Remove a directory on the ftp server
1916 * RETURNS
1917 * TRUE on success
1918 * FALSE on failure
1921 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1923 LPWSTR lpwzDirectory;
1924 BOOL ret;
1926 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1927 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1928 heap_free(lpwzDirectory);
1929 return ret;
1932 static void AsyncFtpRemoveDirectoryProc(task_header_t *hdr)
1934 directory_task_t *task = (directory_task_t*)hdr;
1935 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1937 TRACE("%p\n", session);
1939 FTP_FtpRemoveDirectoryW(session, task->directory);
1940 heap_free(task->directory);
1943 /***********************************************************************
1944 * FtpRemoveDirectoryW (WININET.@)
1946 * Remove a directory on the ftp server
1948 * RETURNS
1949 * TRUE on success
1950 * FALSE on failure
1953 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1955 ftp_session_t *lpwfs;
1956 appinfo_t *hIC = NULL;
1957 BOOL r = FALSE;
1959 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1960 if (!lpwfs)
1962 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1963 return FALSE;
1966 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1968 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1969 goto lend;
1972 if (lpwfs->download_in_progress != NULL)
1974 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1975 goto lend;
1978 if (!lpszDirectory)
1980 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1981 goto lend;
1984 hIC = lpwfs->lpAppInfo;
1985 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1987 directory_task_t *task;
1989 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRemoveDirectoryProc, sizeof(*task));
1990 task->directory = heap_strdupW(lpszDirectory);
1992 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1994 else
1996 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1999 lend:
2000 WININET_Release( &lpwfs->hdr );
2002 return r;
2005 /***********************************************************************
2006 * FTP_FtpRemoveDirectoryW (Internal)
2008 * Remove a directory on the ftp server
2010 * RETURNS
2011 * TRUE on success
2012 * FALSE on failure
2015 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
2017 INT nResCode;
2018 BOOL bSuccess = FALSE;
2019 appinfo_t *hIC = NULL;
2021 TRACE("\n");
2023 /* Clear any error information */
2024 INTERNET_SetLastError(0);
2026 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2027 goto lend;
2029 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2030 if (nResCode)
2032 if (nResCode == 250)
2033 bSuccess = TRUE;
2034 else
2035 FTP_SetResponseError(nResCode);
2038 lend:
2039 hIC = lpwfs->lpAppInfo;
2040 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2042 INTERNET_ASYNC_RESULT iar;
2044 iar.dwResult = (DWORD)bSuccess;
2045 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2046 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2047 &iar, sizeof(INTERNET_ASYNC_RESULT));
2050 return bSuccess;
2054 /***********************************************************************
2055 * FtpRenameFileA (WININET.@)
2057 * Rename a file on the ftp server
2059 * RETURNS
2060 * TRUE on success
2061 * FALSE on failure
2064 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2066 LPWSTR lpwzSrc;
2067 LPWSTR lpwzDest;
2068 BOOL ret;
2070 lpwzSrc = heap_strdupAtoW(lpszSrc);
2071 lpwzDest = heap_strdupAtoW(lpszDest);
2072 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2073 heap_free(lpwzSrc);
2074 heap_free(lpwzDest);
2075 return ret;
2078 typedef struct {
2079 task_header_t hdr;
2080 WCHAR *src_file;
2081 WCHAR *dst_file;
2082 } rename_file_task_t;
2084 static void AsyncFtpRenameFileProc(task_header_t *hdr)
2086 rename_file_task_t *task = (rename_file_task_t*)hdr;
2087 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
2089 TRACE("%p\n", session);
2091 FTP_FtpRenameFileW(session, task->src_file, task->dst_file);
2092 heap_free(task->src_file);
2093 heap_free(task->dst_file);
2096 /***********************************************************************
2097 * FtpRenameFileW (WININET.@)
2099 * Rename a file on the ftp server
2101 * RETURNS
2102 * TRUE on success
2103 * FALSE on failure
2106 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2108 ftp_session_t *lpwfs;
2109 appinfo_t *hIC = NULL;
2110 BOOL r = FALSE;
2112 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
2113 if (!lpwfs)
2115 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2116 return FALSE;
2119 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2121 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2122 goto lend;
2125 if (lpwfs->download_in_progress != NULL)
2127 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2128 goto lend;
2131 if (!lpszSrc || !lpszDest)
2133 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2134 goto lend;
2137 hIC = lpwfs->lpAppInfo;
2138 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2140 rename_file_task_t *task;
2142 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRenameFileProc, sizeof(*task));
2143 task->src_file = heap_strdupW(lpszSrc);
2144 task->dst_file = heap_strdupW(lpszDest);
2146 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
2148 else
2150 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2153 lend:
2154 WININET_Release( &lpwfs->hdr );
2156 return r;
2159 /***********************************************************************
2160 * FTP_FtpRenameFileW (Internal)
2162 * Rename a file on the ftp server
2164 * RETURNS
2165 * TRUE on success
2166 * FALSE on failure
2169 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2171 INT nResCode;
2172 BOOL bSuccess = FALSE;
2173 appinfo_t *hIC = NULL;
2175 TRACE("\n");
2177 /* Clear any error information */
2178 INTERNET_SetLastError(0);
2180 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2181 goto lend;
2183 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2184 if (nResCode == 350)
2186 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2187 goto lend;
2189 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2192 if (nResCode == 250)
2193 bSuccess = TRUE;
2194 else
2195 FTP_SetResponseError(nResCode);
2197 lend:
2198 hIC = lpwfs->lpAppInfo;
2199 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2201 INTERNET_ASYNC_RESULT iar;
2203 iar.dwResult = (DWORD)bSuccess;
2204 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2205 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2206 &iar, sizeof(INTERNET_ASYNC_RESULT));
2209 return bSuccess;
2212 /***********************************************************************
2213 * FtpCommandA (WININET.@)
2215 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2216 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2218 BOOL r;
2219 WCHAR *cmdW;
2221 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2222 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2224 if (fExpectResponse)
2226 FIXME("data connection not supported\n");
2227 return FALSE;
2230 if (!lpszCommand || !lpszCommand[0])
2232 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2233 return FALSE;
2236 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2238 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2239 return FALSE;
2242 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2244 heap_free(cmdW);
2245 return r;
2248 /***********************************************************************
2249 * FtpCommandW (WININET.@)
2251 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2252 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2254 BOOL r = FALSE;
2255 ftp_session_t *lpwfs;
2256 LPSTR cmd = NULL;
2257 DWORD len, nBytesSent= 0;
2258 INT nResCode, nRC = 0;
2260 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2261 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2263 if (!lpszCommand || !lpszCommand[0])
2265 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2266 return FALSE;
2269 if (fExpectResponse)
2271 FIXME("data connection not supported\n");
2272 return FALSE;
2275 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
2276 if (!lpwfs)
2278 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2279 return FALSE;
2282 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2284 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2285 goto lend;
2288 if (lpwfs->download_in_progress != NULL)
2290 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2291 goto lend;
2294 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2295 if ((cmd = heap_alloc(len)))
2296 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2297 else
2299 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2300 goto lend;
2303 strcat(cmd, szCRLF);
2304 len--;
2306 TRACE("Sending (%s) len(%d)\n", debugstr_a(cmd), len);
2307 while ((nBytesSent < len) && (nRC != -1))
2309 nRC = sock_send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2310 if (nRC != -1)
2312 nBytesSent += nRC;
2313 TRACE("Sent %d bytes\n", nRC);
2317 if (nBytesSent)
2319 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2320 if (nResCode > 0 && nResCode < 400)
2321 r = TRUE;
2322 else
2323 FTP_SetResponseError(nResCode);
2326 lend:
2327 WININET_Release( &lpwfs->hdr );
2328 heap_free( cmd );
2329 return r;
2333 /***********************************************************************
2334 * FTPSESSION_Destroy (internal)
2336 * Deallocate session handle
2338 static void FTPSESSION_Destroy(object_header_t *hdr)
2340 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2342 TRACE("\n");
2344 WININET_Release(&lpwfs->lpAppInfo->hdr);
2346 heap_free(lpwfs->lpszPassword);
2347 heap_free(lpwfs->lpszUserName);
2348 heap_free(lpwfs->servername);
2351 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2353 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2355 TRACE("\n");
2357 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2358 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2360 if (lpwfs->download_in_progress != NULL)
2361 lpwfs->download_in_progress->session_deleted = TRUE;
2363 if (lpwfs->sndSocket != -1)
2364 closesocket(lpwfs->sndSocket);
2366 if (lpwfs->lstnSocket != -1)
2367 closesocket(lpwfs->lstnSocket);
2369 if (lpwfs->pasvSocket != -1)
2370 closesocket(lpwfs->pasvSocket);
2372 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2373 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2376 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2378 switch(option) {
2379 case INTERNET_OPTION_HANDLE_TYPE:
2380 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2382 if (*size < sizeof(ULONG))
2383 return ERROR_INSUFFICIENT_BUFFER;
2385 *size = sizeof(DWORD);
2386 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2387 return ERROR_SUCCESS;
2390 return INET_QueryOption(hdr, option, buffer, size, unicode);
2393 static const object_vtbl_t FTPSESSIONVtbl = {
2394 FTPSESSION_Destroy,
2395 FTPSESSION_CloseConnection,
2396 FTPSESSION_QueryOption,
2397 INET_SetOption,
2398 NULL,
2399 NULL,
2400 NULL,
2401 NULL
2405 /***********************************************************************
2406 * FTP_Connect (internal)
2408 * Connect to a ftp server
2410 * RETURNS
2411 * HINTERNET a session handle on success
2412 * NULL on failure
2414 * NOTES:
2416 * Windows uses 'anonymous' as the username, when given a NULL username
2417 * and a NULL password. The password is first looked up in:
2419 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2421 * If this entry is not present it uses the current username as the password.
2425 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2426 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2427 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2428 DWORD dwInternalFlags)
2430 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2431 'M','i','c','r','o','s','o','f','t','\\',
2432 'W','i','n','d','o','w','s','\\',
2433 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2434 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2435 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2436 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2437 static const WCHAR szEmpty[] = {'\0'};
2438 struct sockaddr_in socketAddr;
2439 INT nsocket = -1;
2440 socklen_t sock_namelen;
2441 BOOL bSuccess = FALSE;
2442 ftp_session_t *lpwfs = NULL;
2443 char szaddr[INET6_ADDRSTRLEN];
2445 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2446 hIC, debugstr_w(lpszServerName),
2447 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2449 assert( hIC->hdr.htype == WH_HINIT );
2451 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2453 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2454 return NULL;
2457 lpwfs = alloc_object(&hIC->hdr, &FTPSESSIONVtbl, sizeof(ftp_session_t));
2458 if (NULL == lpwfs)
2460 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2461 return NULL;
2464 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2465 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2466 else
2467 lpwfs->serverport = nServerPort;
2469 lpwfs->hdr.htype = WH_HFTPSESSION;
2470 lpwfs->hdr.dwFlags = dwFlags;
2471 lpwfs->hdr.dwContext = dwContext;
2472 lpwfs->hdr.dwInternalFlags |= dwInternalFlags;
2473 lpwfs->download_in_progress = NULL;
2474 lpwfs->sndSocket = -1;
2475 lpwfs->lstnSocket = -1;
2476 lpwfs->pasvSocket = -1;
2478 WININET_AddRef( &hIC->hdr );
2479 lpwfs->lpAppInfo = hIC;
2480 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2482 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
2483 if(wcschr(hIC->proxy, ' '))
2484 FIXME("Several proxies not implemented.\n");
2485 if(hIC->proxyBypass)
2486 FIXME("Proxy bypass is ignored.\n");
2488 if (!lpszUserName || !lpszUserName[0]) {
2489 HKEY key;
2490 WCHAR szPassword[MAX_PATH];
2491 DWORD len = sizeof(szPassword);
2493 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2495 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2496 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2497 /* Nothing in the registry, get the username and use that as the password */
2498 if (!GetUserNameW(szPassword, &len)) {
2499 /* Should never get here, but use an empty password as failsafe */
2500 lstrcpyW(szPassword, szEmpty);
2503 RegCloseKey(key);
2505 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2506 lpwfs->lpszPassword = heap_strdupW(szPassword);
2508 else {
2509 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2510 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2512 lpwfs->servername = heap_strdupW(lpszServerName);
2514 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2515 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2517 INTERNET_ASYNC_RESULT iar;
2519 iar.dwResult = (DWORD_PTR)lpwfs->hdr.hInternet;
2520 iar.dwError = ERROR_SUCCESS;
2522 INTERNET_SendCallback(&hIC->hdr, dwContext,
2523 INTERNET_STATUS_HANDLE_CREATED, &iar,
2524 sizeof(INTERNET_ASYNC_RESULT));
2527 INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2528 (LPWSTR) lpszServerName, (lstrlenW(lpszServerName)+1) * sizeof(WCHAR));
2530 sock_namelen = sizeof(socketAddr);
2531 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen, szaddr))
2533 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2534 goto lerror;
2537 if (socketAddr.sin_family != AF_INET)
2539 WARN("unsupported address family %d\n", socketAddr.sin_family);
2540 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2541 goto lerror;
2544 INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2545 szaddr, strlen(szaddr)+1);
2547 init_winsock();
2548 nsocket = socket(AF_INET,SOCK_STREAM,0);
2549 if (nsocket == -1)
2551 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2552 goto lerror;
2555 INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2556 szaddr, strlen(szaddr)+1);
2558 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2560 ERR("Unable to connect (%d)\n", WSAGetLastError());
2561 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2562 closesocket(nsocket);
2564 else
2566 TRACE("Connected to server\n");
2567 lpwfs->sndSocket = nsocket;
2568 INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2569 szaddr, strlen(szaddr)+1);
2571 sock_namelen = sizeof(lpwfs->socketAddress);
2572 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2574 if (FTP_ConnectToHost(lpwfs))
2576 TRACE("Successfully logged into server\n");
2577 bSuccess = TRUE;
2581 lerror:
2582 if (!bSuccess)
2584 WININET_Release(&lpwfs->hdr);
2585 return NULL;
2588 return lpwfs->hdr.hInternet;
2592 /***********************************************************************
2593 * FTP_ConnectToHost (internal)
2595 * Connect to a ftp server
2597 * RETURNS
2598 * TRUE on success
2599 * NULL on failure
2602 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2604 INT nResCode;
2605 BOOL bSuccess = FALSE;
2607 TRACE("\n");
2608 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2610 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2611 goto lend;
2613 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2614 if (nResCode)
2616 /* Login successful... */
2617 if (nResCode == 230)
2618 bSuccess = TRUE;
2619 /* User name okay, need password... */
2620 else if (nResCode == 331)
2621 bSuccess = FTP_SendPassword(lpwfs);
2622 /* Need account for login... */
2623 else if (nResCode == 332)
2624 bSuccess = FTP_SendAccount(lpwfs);
2625 else
2626 FTP_SetResponseError(nResCode);
2629 TRACE("Returning %d\n", bSuccess);
2630 lend:
2631 return bSuccess;
2634 /***********************************************************************
2635 * FTP_GetNextLine (internal)
2637 * Parse next line in directory string listing
2639 * RETURNS
2640 * Pointer to beginning of next line
2641 * NULL on failure
2645 static LPSTR FTP_GetNextLine(INT nSocket, LPDWORD dwLen)
2647 struct timeval tv = {RESPONSE_TIMEOUT,0};
2648 FD_SET set;
2649 INT nRecv = 0;
2650 LPSTR lpszBuffer = INTERNET_GetResponseBuffer();
2652 TRACE("\n");
2654 FD_ZERO(&set);
2655 FD_SET(nSocket, &set);
2657 while (nRecv < MAX_REPLY_LEN)
2659 if (select(nSocket+1, &set, NULL, NULL, &tv) > 0)
2661 if (sock_recv(nSocket, &lpszBuffer[nRecv], 1, 0) <= 0)
2663 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2664 return NULL;
2667 if (lpszBuffer[nRecv] == '\n')
2669 lpszBuffer[nRecv] = '\0';
2670 *dwLen = nRecv - 1;
2671 TRACE(":%d %s\n", nRecv, lpszBuffer);
2672 return lpszBuffer;
2674 if (lpszBuffer[nRecv] != '\r')
2675 nRecv++;
2677 else
2679 INTERNET_SetLastError(ERROR_INTERNET_TIMEOUT);
2680 return NULL;
2684 return NULL;
2687 /***********************************************************************
2688 * FTP_SendCommandA (internal)
2690 * Send command to server
2692 * RETURNS
2693 * TRUE on success
2694 * NULL on failure
2697 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2698 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2700 DWORD len;
2701 CHAR *buf;
2702 DWORD nBytesSent = 0;
2703 int nRC = 0;
2704 DWORD dwParamLen;
2706 TRACE("%d: (%s) %d\n", ftpCmd, debugstr_a(lpszParam), nSocket);
2708 if (lpfnStatusCB)
2710 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2713 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2714 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2715 if (NULL == (buf = heap_alloc(len+1)))
2717 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2718 return FALSE;
2720 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2721 dwParamLen ? lpszParam : "", szCRLF);
2723 TRACE("Sending (%s) len(%d)\n", debugstr_a(buf), len);
2724 while((nBytesSent < len) && (nRC != -1))
2726 nRC = sock_send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2727 nBytesSent += nRC;
2729 heap_free(buf);
2731 if (lpfnStatusCB)
2733 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2734 &nBytesSent, sizeof(DWORD));
2737 TRACE("Sent %d bytes\n", nBytesSent);
2738 return (nRC != -1);
2741 /***********************************************************************
2742 * FTP_SendCommand (internal)
2744 * Send command to server
2746 * RETURNS
2747 * TRUE on success
2748 * NULL on failure
2751 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2752 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2754 BOOL ret;
2755 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2756 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2757 heap_free(lpszParamA);
2758 return ret;
2761 /***********************************************************************
2762 * FTP_ReceiveResponse (internal)
2764 * Receive response from server
2766 * RETURNS
2767 * Reply code on success
2768 * 0 on failure
2771 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2773 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2774 DWORD nRecv;
2775 INT rc = 0;
2776 char firstprefix[5];
2777 BOOL multiline = FALSE;
2779 TRACE("socket(%d)\n", lpwfs->sndSocket);
2781 INTERNET_SendCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2783 while(1)
2785 if (!FTP_GetNextLine(lpwfs->sndSocket, &nRecv))
2786 goto lerror;
2788 if (nRecv >= 3)
2790 if(!multiline)
2792 if(lpszResponse[3] != '-')
2793 break;
2794 else
2795 { /* Start of multiline response. Loop until we get "nnn " */
2796 multiline = TRUE;
2797 memcpy(firstprefix, lpszResponse, 3);
2798 firstprefix[3] = ' ';
2799 firstprefix[4] = '\0';
2802 else
2804 if(!memcmp(firstprefix, lpszResponse, 4))
2805 break;
2810 if (nRecv >= 3)
2812 rc = atoi(lpszResponse);
2814 INTERNET_SendCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2815 &nRecv, sizeof(DWORD));
2818 lerror:
2819 TRACE("return %d\n", rc);
2820 return rc;
2824 /***********************************************************************
2825 * FTP_SendPassword (internal)
2827 * Send password to ftp server
2829 * RETURNS
2830 * TRUE on success
2831 * NULL on failure
2834 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2836 INT nResCode;
2837 BOOL bSuccess = FALSE;
2839 TRACE("\n");
2840 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2841 goto lend;
2843 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2844 if (nResCode)
2846 TRACE("Received reply code %d\n", nResCode);
2847 /* Login successful... */
2848 if (nResCode == 230)
2849 bSuccess = TRUE;
2850 /* Command not implemented, superfluous at the server site... */
2851 /* Need account for login... */
2852 else if (nResCode == 332)
2853 bSuccess = FTP_SendAccount(lpwfs);
2854 else
2855 FTP_SetResponseError(nResCode);
2858 lend:
2859 TRACE("Returning %d\n", bSuccess);
2860 return bSuccess;
2864 /***********************************************************************
2865 * FTP_SendAccount (internal)
2869 * RETURNS
2870 * TRUE on success
2871 * FALSE on failure
2874 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2876 INT nResCode;
2877 BOOL bSuccess = FALSE;
2879 TRACE("\n");
2880 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2881 goto lend;
2883 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2884 if (nResCode)
2885 bSuccess = TRUE;
2886 else
2887 FTP_SetResponseError(nResCode);
2889 lend:
2890 return bSuccess;
2894 /***********************************************************************
2895 * FTP_SendStore (internal)
2897 * Send request to upload file to ftp server
2899 * RETURNS
2900 * TRUE on success
2901 * FALSE on failure
2904 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2906 INT nResCode;
2907 BOOL bSuccess = FALSE;
2909 TRACE("\n");
2910 if (!FTP_InitListenSocket(lpwfs))
2911 goto lend;
2913 if (!FTP_SendType(lpwfs, dwType))
2914 goto lend;
2916 if (!FTP_SendPortOrPasv(lpwfs))
2917 goto lend;
2919 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2920 goto lend;
2921 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2922 if (nResCode)
2924 if (nResCode == 150 || nResCode == 125)
2925 bSuccess = TRUE;
2926 else
2927 FTP_SetResponseError(nResCode);
2930 lend:
2931 if (!bSuccess && lpwfs->lstnSocket != -1)
2933 closesocket(lpwfs->lstnSocket);
2934 lpwfs->lstnSocket = -1;
2937 return bSuccess;
2941 /***********************************************************************
2942 * FTP_InitListenSocket (internal)
2944 * Create a socket to listen for server response
2946 * RETURNS
2947 * TRUE on success
2948 * FALSE on failure
2951 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2953 BOOL bSuccess = FALSE;
2954 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2956 TRACE("\n");
2958 init_winsock();
2959 lpwfs->lstnSocket = socket(AF_INET, SOCK_STREAM, 0);
2960 if (lpwfs->lstnSocket == -1)
2962 TRACE("Unable to create listening socket\n");
2963 goto lend;
2966 /* We obtain our ip addr from the name of the command channel socket */
2967 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2969 /* and get the system to assign us a port */
2970 lpwfs->lstnSocketAddress.sin_port = htons(0);
2972 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2974 TRACE("Unable to bind socket\n");
2975 goto lend;
2978 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2980 TRACE("listen failed\n");
2981 goto lend;
2984 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2985 bSuccess = TRUE;
2987 lend:
2988 if (!bSuccess && lpwfs->lstnSocket != -1)
2990 closesocket(lpwfs->lstnSocket);
2991 lpwfs->lstnSocket = -1;
2994 return bSuccess;
2998 /***********************************************************************
2999 * FTP_SendType (internal)
3001 * Tell server type of data being transferred
3003 * RETURNS
3004 * TRUE on success
3005 * FALSE on failure
3007 * W98SE doesn't cache the type that's currently set
3008 * (i.e. it sends it always),
3009 * so we probably don't want to do that either.
3011 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
3013 INT nResCode;
3014 WCHAR type[] = { 'I','\0' };
3015 BOOL bSuccess = FALSE;
3017 TRACE("\n");
3018 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
3019 type[0] = 'A';
3021 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
3022 goto lend;
3024 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
3025 if (nResCode)
3027 if (nResCode == 2)
3028 bSuccess = TRUE;
3029 else
3030 FTP_SetResponseError(nResCode);
3033 lend:
3034 return bSuccess;
3038 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
3039 /***********************************************************************
3040 * FTP_GetFileSize (internal)
3042 * Retrieves from the server the size of the given file
3044 * RETURNS
3045 * TRUE on success
3046 * FALSE on failure
3049 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
3051 INT nResCode;
3052 BOOL bSuccess = FALSE;
3054 TRACE("\n");
3056 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
3057 goto lend;
3059 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3060 if (nResCode)
3062 if (nResCode == 213) {
3063 /* Now parses the output to get the actual file size */
3064 int i;
3065 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3067 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3068 if (lpszResponseBuffer[i] == '\0') return FALSE;
3069 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3071 bSuccess = TRUE;
3072 } else {
3073 FTP_SetResponseError(nResCode);
3077 lend:
3078 return bSuccess;
3080 #endif
3083 /***********************************************************************
3084 * FTP_SendPort (internal)
3086 * Tell server which port to use
3088 * RETURNS
3089 * TRUE on success
3090 * FALSE on failure
3093 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3095 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3096 INT nResCode;
3097 WCHAR szIPAddress[64];
3098 BOOL bSuccess = FALSE;
3099 TRACE("\n");
3101 swprintf(szIPAddress, ARRAY_SIZE(szIPAddress), szIPFormat,
3102 lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x000000FF,
3103 (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x0000FF00)>>8,
3104 (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x00FF0000)>>16,
3105 (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0xFF000000)>>24,
3106 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3107 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3109 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3110 goto lend;
3112 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3113 if (nResCode)
3115 if (nResCode == 200)
3116 bSuccess = TRUE;
3117 else
3118 FTP_SetResponseError(nResCode);
3121 lend:
3122 return bSuccess;
3126 /***********************************************************************
3127 * FTP_DoPassive (internal)
3129 * Tell server that we want to do passive transfers
3130 * and connect data socket
3132 * RETURNS
3133 * TRUE on success
3134 * FALSE on failure
3137 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3139 INT nResCode;
3140 BOOL bSuccess = FALSE;
3142 TRACE("\n");
3143 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3144 goto lend;
3146 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3147 if (nResCode)
3149 if (nResCode == 227)
3151 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3152 LPSTR p;
3153 int f[6];
3154 int i;
3155 char *pAddr, *pPort;
3156 INT nsocket = -1;
3157 struct sockaddr_in dataSocketAddress;
3159 p = lpszResponseBuffer+4; /* skip status code */
3160 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3162 if (*p == '\0')
3164 ERR("no address found in response, aborting\n");
3165 goto lend;
3168 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3169 &f[4], &f[5]) != 6)
3171 ERR("unknown response address format '%s', aborting\n", p);
3172 goto lend;
3174 for (i=0; i < 6; i++)
3175 f[i] = f[i] & 0xff;
3177 dataSocketAddress = lpwfs->socketAddress;
3178 pAddr = (char *)&(dataSocketAddress.sin_addr.S_un.S_addr);
3179 pPort = (char *)&(dataSocketAddress.sin_port);
3180 pAddr[0] = f[0];
3181 pAddr[1] = f[1];
3182 pAddr[2] = f[2];
3183 pAddr[3] = f[3];
3184 pPort[0] = f[4];
3185 pPort[1] = f[5];
3187 nsocket = socket(AF_INET,SOCK_STREAM,0);
3188 if (nsocket == -1)
3189 goto lend;
3191 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3193 ERR("can't connect passive FTP data port.\n");
3194 closesocket(nsocket);
3195 goto lend;
3197 lpwfs->pasvSocket = nsocket;
3198 bSuccess = TRUE;
3200 else
3201 FTP_SetResponseError(nResCode);
3204 lend:
3205 return bSuccess;
3209 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3211 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3213 if (!FTP_DoPassive(lpwfs))
3214 return FALSE;
3216 else
3218 if (!FTP_SendPort(lpwfs))
3219 return FALSE;
3221 return TRUE;
3225 /***********************************************************************
3226 * FTP_GetDataSocket (internal)
3228 * Either accepts an incoming data socket connection from the server
3229 * or just returns the already opened socket after a PASV command
3230 * in case of passive FTP.
3233 * RETURNS
3234 * TRUE on success
3235 * FALSE on failure
3238 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3240 struct sockaddr_in saddr;
3241 socklen_t addrlen = sizeof(saddr);
3243 TRACE("\n");
3244 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3246 *nDataSocket = lpwfs->pasvSocket;
3247 lpwfs->pasvSocket = -1;
3249 else
3251 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3252 closesocket(lpwfs->lstnSocket);
3253 lpwfs->lstnSocket = -1;
3255 return *nDataSocket != -1;
3259 /***********************************************************************
3260 * FTP_SendData (internal)
3262 * Send data to the server
3264 * RETURNS
3265 * TRUE on success
3266 * FALSE on failure
3269 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3271 BY_HANDLE_FILE_INFORMATION fi;
3272 DWORD nBytesRead = 0;
3273 DWORD nBytesSent = 0;
3274 DWORD nTotalSent = 0;
3275 DWORD nBytesToSend, nLen;
3276 int nRC = 1;
3277 time_t s_long_time, e_long_time;
3278 LONG nSeconds;
3279 CHAR *lpszBuffer;
3281 TRACE("\n");
3282 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3284 /* Get the size of the file. */
3285 GetFileInformationByHandle(hFile, &fi);
3286 time(&s_long_time);
3290 nBytesToSend = nBytesRead - nBytesSent;
3292 if (nBytesToSend <= 0)
3294 /* Read data from file. */
3295 nBytesSent = 0;
3296 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3297 ERR("Failed reading from file\n");
3299 if (nBytesRead > 0)
3300 nBytesToSend = nBytesRead;
3301 else
3302 break;
3305 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3306 DATA_PACKET_SIZE : nBytesToSend;
3307 nRC = sock_send(nDataSocket, lpszBuffer, nLen, 0);
3309 if (nRC != -1)
3311 nBytesSent += nRC;
3312 nTotalSent += nRC;
3315 /* Do some computation to display the status. */
3316 time(&e_long_time);
3317 nSeconds = e_long_time - s_long_time;
3318 if( nSeconds / 60 > 0 )
3320 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3321 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3322 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3324 else
3326 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3327 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3328 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3330 } while (nRC != -1);
3332 TRACE("file transfer complete!\n");
3334 heap_free(lpszBuffer);
3335 return nTotalSent;
3339 /***********************************************************************
3340 * FTP_SendRetrieve (internal)
3342 * Send request to retrieve a file
3344 * RETURNS
3345 * Number of bytes to be received on success
3346 * 0 on failure
3349 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3351 INT nResCode;
3352 BOOL ret;
3354 TRACE("\n");
3355 if (!(ret = FTP_InitListenSocket(lpwfs)))
3356 goto lend;
3358 if (!(ret = FTP_SendType(lpwfs, dwType)))
3359 goto lend;
3361 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3362 goto lend;
3364 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3365 goto lend;
3367 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3368 if ((nResCode != 125) && (nResCode != 150)) {
3369 /* That means that we got an error getting the file. */
3370 FTP_SetResponseError(nResCode);
3371 ret = FALSE;
3374 lend:
3375 if (!ret && lpwfs->lstnSocket != -1)
3377 closesocket(lpwfs->lstnSocket);
3378 lpwfs->lstnSocket = -1;
3381 return ret;
3385 /***********************************************************************
3386 * FTP_RetrieveData (internal)
3388 * Retrieve data from server
3390 * RETURNS
3391 * TRUE on success
3392 * FALSE on failure
3395 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3397 DWORD nBytesWritten;
3398 INT nRC = 0;
3399 CHAR *lpszBuffer;
3401 TRACE("\n");
3403 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3404 if (NULL == lpszBuffer)
3406 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3407 return FALSE;
3410 while (nRC != -1)
3412 nRC = sock_recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3413 if (nRC != -1)
3415 /* other side closed socket. */
3416 if (nRC == 0)
3417 goto recv_end;
3418 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3422 TRACE("Data transfer complete\n");
3424 recv_end:
3425 heap_free(lpszBuffer);
3426 return (nRC != -1);
3429 /***********************************************************************
3430 * FTPFINDNEXT_Destroy (internal)
3432 * Deallocate session handle
3434 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3436 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3437 DWORD i;
3439 TRACE("\n");
3441 WININET_Release(&lpwfn->lpFtpSession->hdr);
3443 for (i = 0; i < lpwfn->size; i++)
3445 heap_free(lpwfn->lpafp[i].lpszName);
3447 heap_free(lpwfn->lpafp);
3450 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3452 WIN32_FIND_DATAW *find_data = data;
3453 DWORD res = ERROR_SUCCESS;
3455 TRACE("index(%d) size(%d)\n", find->index, find->size);
3457 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3459 if (find->index < find->size) {
3460 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3461 find->index++;
3463 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3464 }else {
3465 res = ERROR_NO_MORE_FILES;
3468 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3470 INTERNET_ASYNC_RESULT iar;
3472 iar.dwResult = (res == ERROR_SUCCESS);
3473 iar.dwError = res;
3475 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3476 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3477 sizeof(INTERNET_ASYNC_RESULT));
3480 return res;
3483 typedef struct {
3484 task_header_t hdr;
3485 WIN32_FIND_DATAW *find_data;
3486 } find_next_task_t;
3488 static void FTPFINDNEXT_AsyncFindNextFileProc(task_header_t *hdr)
3490 find_next_task_t *task = (find_next_task_t*)hdr;
3492 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)task->hdr.hdr, task->find_data);
3495 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3497 switch(option) {
3498 case INTERNET_OPTION_HANDLE_TYPE:
3499 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3501 if (*size < sizeof(ULONG))
3502 return ERROR_INSUFFICIENT_BUFFER;
3504 *size = sizeof(DWORD);
3505 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3506 return ERROR_SUCCESS;
3509 return INET_QueryOption(hdr, option, buffer, size, unicode);
3512 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3514 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3516 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3518 find_next_task_t *task;
3520 task = alloc_async_task(&find->hdr, FTPFINDNEXT_AsyncFindNextFileProc, sizeof(*task));
3521 task->find_data = data;
3523 INTERNET_AsyncCall(&task->hdr);
3524 return ERROR_SUCCESS;
3527 return FTPFINDNEXT_FindNextFileProc(find, data);
3530 static const object_vtbl_t FTPFINDNEXTVtbl = {
3531 FTPFINDNEXT_Destroy,
3532 NULL,
3533 FTPFINDNEXT_QueryOption,
3534 INET_SetOption,
3535 NULL,
3536 NULL,
3537 NULL,
3538 FTPFINDNEXT_FindNextFileW
3541 /***********************************************************************
3542 * FTP_ReceiveFileList (internal)
3544 * Read file list from server
3546 * RETURNS
3547 * Handle to file list on success
3548 * NULL on failure
3551 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3552 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3554 DWORD dwSize = 0;
3555 LPFILEPROPERTIESW lpafp = NULL;
3556 LPWININETFTPFINDNEXTW lpwfn = NULL;
3558 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3560 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3562 if(lpFindFileData)
3563 FTP_ConvertFileProp(lpafp, lpFindFileData);
3565 lpwfn = alloc_object(&lpwfs->hdr, &FTPFINDNEXTVtbl, sizeof(WININETFTPFINDNEXTW));
3566 if (lpwfn)
3568 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3569 lpwfn->hdr.dwContext = dwContext;
3570 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3571 lpwfn->size = dwSize;
3572 lpwfn->lpafp = lpafp;
3574 WININET_AddRef( &lpwfs->hdr );
3575 lpwfn->lpFtpSession = lpwfs;
3576 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3580 TRACE("Matched %d files\n", dwSize);
3581 return lpwfn ? lpwfn->hdr.hInternet : NULL;
3585 /***********************************************************************
3586 * FTP_ConvertFileProp (internal)
3588 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3590 * RETURNS
3591 * TRUE on success
3592 * FALSE on failure
3595 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3597 BOOL bSuccess = FALSE;
3599 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3601 if (lpafp)
3603 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3604 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3605 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3607 /* Not all fields are filled in */
3608 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3609 lpFindFileData->nFileSizeLow = lpafp->nSize;
3611 if (lpafp->bIsDirectory)
3612 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3614 if (lpafp->lpszName)
3615 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3617 bSuccess = TRUE;
3620 return bSuccess;
3623 /***********************************************************************
3624 * FTP_ParseNextFile (internal)
3626 * Parse the next line in file listing
3628 * RETURNS
3629 * TRUE on success
3630 * FALSE on failure
3632 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3634 static const char szSpace[] = " \t";
3635 DWORD nBufLen;
3636 char *pszLine;
3637 char *pszToken;
3638 char *pszTmp;
3639 BOOL found = FALSE;
3640 int i;
3642 lpfp->lpszName = NULL;
3643 do {
3644 if(!(pszLine = FTP_GetNextLine(nSocket, &nBufLen)))
3645 return FALSE;
3647 pszToken = strtok(pszLine, szSpace);
3648 /* ls format
3649 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3651 * For instance:
3652 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3654 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3655 if(!FTP_ParsePermission(pszToken, lpfp))
3656 lpfp->bIsDirectory = FALSE;
3657 for(i=0; i<=3; i++) {
3658 if(!(pszToken = strtok(NULL, szSpace)))
3659 break;
3661 if(!pszToken) continue;
3662 if(lpfp->bIsDirectory) {
3663 TRACE("Is directory\n");
3664 lpfp->nSize = 0;
3666 else {
3667 TRACE("Size: %s\n", pszToken);
3668 lpfp->nSize = atol(pszToken);
3671 lpfp->tmLastModified.wSecond = 0;
3672 lpfp->tmLastModified.wMinute = 0;
3673 lpfp->tmLastModified.wHour = 0;
3674 lpfp->tmLastModified.wDay = 0;
3675 lpfp->tmLastModified.wMonth = 0;
3676 lpfp->tmLastModified.wYear = 0;
3678 /* Determine month */
3679 pszToken = strtok(NULL, szSpace);
3680 if(!pszToken) continue;
3681 if(strlen(pszToken) >= 3) {
3682 pszToken[3] = 0;
3683 if((pszTmp = StrStrIA(szMonths, pszToken)))
3684 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3686 /* Determine day */
3687 pszToken = strtok(NULL, szSpace);
3688 if(!pszToken) continue;
3689 lpfp->tmLastModified.wDay = atoi(pszToken);
3690 /* Determine time or year */
3691 pszToken = strtok(NULL, szSpace);
3692 if(!pszToken) continue;
3693 if((pszTmp = strchr(pszToken, ':'))) {
3694 SYSTEMTIME curr_time;
3695 *pszTmp = 0;
3696 pszTmp++;
3697 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3698 lpfp->tmLastModified.wHour = atoi(pszToken);
3699 GetLocalTime( &curr_time );
3700 lpfp->tmLastModified.wYear = curr_time.wYear;
3702 else {
3703 lpfp->tmLastModified.wYear = atoi(pszToken);
3704 lpfp->tmLastModified.wHour = 12;
3706 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3707 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3708 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3710 pszToken = strtok(NULL, szSpace);
3711 if(!pszToken) continue;
3712 lpfp->lpszName = heap_strdupAtoW(pszToken);
3713 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3715 /* NT way of parsing ... :
3717 07-13-03 08:55PM <DIR> sakpatch
3718 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3720 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3721 int mon, mday, year, hour, min;
3722 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3724 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3725 lpfp->tmLastModified.wDay = mday;
3726 lpfp->tmLastModified.wMonth = mon;
3727 lpfp->tmLastModified.wYear = year;
3729 /* Hacky and bad Y2K protection :-) */
3730 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3732 pszToken = strtok(NULL, szSpace);
3733 if(!pszToken) continue;
3734 sscanf(pszToken, "%d:%d", &hour, &min);
3735 lpfp->tmLastModified.wHour = hour;
3736 lpfp->tmLastModified.wMinute = min;
3737 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3738 lpfp->tmLastModified.wHour += 12;
3740 lpfp->tmLastModified.wSecond = 0;
3742 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3743 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3744 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3746 pszToken = strtok(NULL, szSpace);
3747 if(!pszToken) continue;
3748 if(!_strnicmp(pszToken, "<DIR>", -1)) {
3749 lpfp->bIsDirectory = TRUE;
3750 lpfp->nSize = 0;
3751 TRACE("Is directory\n");
3753 else {
3754 lpfp->bIsDirectory = FALSE;
3755 lpfp->nSize = atol(pszToken);
3756 TRACE("Size: %d\n", lpfp->nSize);
3759 pszToken = strtok(NULL, szSpace);
3760 if(!pszToken) continue;
3761 lpfp->lpszName = heap_strdupAtoW(pszToken);
3762 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3764 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3765 else if(pszToken[0] == '+') {
3766 FIXME("EPLF Format not implemented\n");
3769 if(lpfp->lpszName) {
3770 if((lpszSearchFile == NULL) ||
3771 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3772 found = TRUE;
3773 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3775 else {
3776 heap_free(lpfp->lpszName);
3777 lpfp->lpszName = NULL;
3780 } while(!found);
3781 return TRUE;
3784 /***********************************************************************
3785 * FTP_ParseDirectory (internal)
3787 * Parse string of directory information
3789 * RETURNS
3790 * TRUE on success
3791 * FALSE on failure
3793 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3794 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3796 BOOL bSuccess = TRUE;
3797 INT sizeFilePropArray = 500;/*20; */
3798 INT indexFilePropArray = -1;
3800 TRACE("\n");
3802 /* Allocate initial file properties array */
3803 *lpafp = heap_alloc_zero(sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3804 if (!*lpafp)
3805 return FALSE;
3807 do {
3808 if (indexFilePropArray+1 >= sizeFilePropArray)
3810 LPFILEPROPERTIESW tmpafp;
3812 sizeFilePropArray *= 2;
3813 tmpafp = heap_realloc_zero(*lpafp, sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3814 if (NULL == tmpafp)
3816 bSuccess = FALSE;
3817 break;
3820 *lpafp = tmpafp;
3822 indexFilePropArray++;
3823 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3825 if (bSuccess && indexFilePropArray)
3827 if (indexFilePropArray < sizeFilePropArray - 1)
3829 LPFILEPROPERTIESW tmpafp;
3831 tmpafp = heap_realloc(*lpafp, sizeof(FILEPROPERTIESW)*indexFilePropArray);
3832 if (NULL != tmpafp)
3833 *lpafp = tmpafp;
3835 *dwfp = indexFilePropArray;
3837 else
3839 heap_free(*lpafp);
3840 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3841 bSuccess = FALSE;
3844 return bSuccess;
3848 /***********************************************************************
3849 * FTP_ParsePermission (internal)
3851 * Parse permission string of directory information
3853 * RETURNS
3854 * TRUE on success
3855 * FALSE on failure
3858 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3860 BOOL bSuccess = TRUE;
3861 unsigned short nPermission = 0;
3862 INT nPos = 1;
3863 INT nLast = 9;
3865 TRACE("\n");
3866 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3868 bSuccess = FALSE;
3869 return bSuccess;
3872 lpfp->bIsDirectory = (*lpszPermission == 'd');
3875 switch (nPos)
3877 case 1:
3878 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3879 break;
3880 case 2:
3881 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3882 break;
3883 case 3:
3884 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3885 break;
3886 case 4:
3887 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3888 break;
3889 case 5:
3890 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3891 break;
3892 case 6:
3893 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3894 break;
3895 case 7:
3896 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3897 break;
3898 case 8:
3899 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3900 break;
3901 case 9:
3902 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3903 break;
3905 nPos++;
3906 }while (nPos <= nLast);
3908 lpfp->permissions = nPermission;
3909 return bSuccess;
3913 /***********************************************************************
3914 * FTP_SetResponseError (internal)
3916 * Set the appropriate error code for a given response from the server
3918 * RETURNS
3921 static DWORD FTP_SetResponseError(DWORD dwResponse)
3923 DWORD dwCode = 0;
3925 switch(dwResponse)
3927 case 425: /* Cannot open data connection. */
3928 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3929 break;
3931 case 426: /* Connection closed, transfer aborted. */
3932 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3933 break;
3935 case 530: /* Not logged in. Login incorrect. */
3936 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3937 break;
3939 case 421: /* Service not available - Server may be shutting down. */
3940 case 450: /* File action not taken. File may be busy. */
3941 case 451: /* Action aborted. Server error. */
3942 case 452: /* Action not taken. Insufficient storage space on server. */
3943 case 500: /* Syntax error. Command unrecognized. */
3944 case 501: /* Syntax error. Error in parameters or arguments. */
3945 case 502: /* Command not implemented. */
3946 case 503: /* Bad sequence of commands. */
3947 case 504: /* Command not implemented for that parameter. */
3948 case 532: /* Need account for storing files */
3949 case 550: /* File action not taken. File not found or no access. */
3950 case 551: /* Requested action aborted. Page type unknown */
3951 case 552: /* Action aborted. Exceeded storage allocation */
3952 case 553: /* Action not taken. File name not allowed. */
3954 default:
3955 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3956 break;
3959 INTERNET_SetLastError(dwCode);
3960 return dwCode;