wininet: Moved WORKREQ_FTPDELETEFILEW out of WORKREQUEST.
[wine.git] / dlls / wininet / ftp.c
blob152e336bf32b45f360b879967c73c7e173fe5714
1 /*
2 * WININET - Ftp implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2004 Mike McCormack for CodeWeavers
6 * Copyright 2004 Kevin Koltzau
7 * Copyright 2007 Hans Leidekker
9 * Ulrich Czekalla
10 * Noureddine Jemmali
12 * Copyright 2000 Andreas Mohr
13 * Copyright 2002 Jaco Greeff
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "config.h"
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
34 #include <ws2tcpip.h>
35 #endif
37 #include <errno.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/types.h>
43 #ifdef HAVE_SYS_SOCKET_H
44 # include <sys/socket.h>
45 #endif
46 #ifdef HAVE_ARPA_INET_H
47 # include <arpa/inet.h>
48 #endif
49 #ifdef HAVE_UNISTD_H
50 # include <unistd.h>
51 #endif
52 #ifdef HAVE_SYS_IOCTL_H
53 # include <sys/ioctl.h>
54 #endif
55 #include <time.h>
56 #include <assert.h>
58 #include "windef.h"
59 #include "winbase.h"
60 #include "wingdi.h"
61 #include "winuser.h"
62 #include "wininet.h"
63 #include "winnls.h"
64 #include "winerror.h"
65 #include "winreg.h"
66 #include "winternl.h"
67 #include "shlwapi.h"
69 #include "wine/debug.h"
70 #include "internet.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
74 typedef struct _ftp_session_t ftp_session_t;
76 typedef struct
78 object_header_t hdr;
79 ftp_session_t *lpFtpSession;
80 BOOL session_deleted;
81 int nDataSocket;
82 WCHAR *cache_file;
83 HANDLE cache_file_handle;
84 } ftp_file_t;
86 struct _ftp_session_t
88 object_header_t hdr;
89 appinfo_t *lpAppInfo;
90 int sndSocket;
91 int lstnSocket;
92 int pasvSocket; /* data socket connected by us in case of passive FTP */
93 ftp_file_t *download_in_progress;
94 struct sockaddr_in socketAddress;
95 struct sockaddr_in lstnSocketAddress;
96 LPWSTR servername;
97 INTERNET_PORT serverport;
98 LPWSTR lpszPassword;
99 LPWSTR lpszUserName;
102 typedef struct
104 BOOL bIsDirectory;
105 LPWSTR lpszName;
106 DWORD nSize;
107 SYSTEMTIME tmLastModified;
108 unsigned short permissions;
109 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
111 typedef struct
113 object_header_t hdr;
114 ftp_session_t *lpFtpSession;
115 DWORD index;
116 DWORD size;
117 LPFILEPROPERTIESW lpafp;
118 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
120 #define DATA_PACKET_SIZE 0x2000
121 #define szCRLF "\r\n"
122 #define MAX_BACKLOG 5
124 /* Testing shows that Windows only accepts dwFlags where the last
125 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
127 #define FTP_CONDITION_MASK 0x0007
129 typedef enum {
130 /* FTP commands with arguments. */
131 FTP_CMD_ACCT,
132 FTP_CMD_CWD,
133 FTP_CMD_DELE,
134 FTP_CMD_MKD,
135 FTP_CMD_PASS,
136 FTP_CMD_PORT,
137 FTP_CMD_RETR,
138 FTP_CMD_RMD,
139 FTP_CMD_RNFR,
140 FTP_CMD_RNTO,
141 FTP_CMD_STOR,
142 FTP_CMD_TYPE,
143 FTP_CMD_USER,
144 FTP_CMD_SIZE,
146 /* FTP commands without arguments. */
147 FTP_CMD_ABOR,
148 FTP_CMD_LIST,
149 FTP_CMD_NLST,
150 FTP_CMD_PASV,
151 FTP_CMD_PWD,
152 FTP_CMD_QUIT,
153 } FTP_COMMAND;
155 static const CHAR *const szFtpCommands[] = {
156 "ACCT",
157 "CWD",
158 "DELE",
159 "MKD",
160 "PASS",
161 "PORT",
162 "RETR",
163 "RMD",
164 "RNFR",
165 "RNTO",
166 "STOR",
167 "TYPE",
168 "USER",
169 "SIZE",
170 "ABOR",
171 "LIST",
172 "NLST",
173 "PASV",
174 "PWD",
175 "QUIT",
178 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
179 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
181 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
182 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext);
183 static BOOL FTP_SendStore(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
184 static BOOL FTP_GetDataSocket(ftp_session_t*, LPINT nDataSocket);
185 static BOOL FTP_SendData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
186 static INT FTP_ReceiveResponse(ftp_session_t*, DWORD_PTR dwContext);
187 static BOOL FTP_SendRetrieve(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
188 static BOOL FTP_RetrieveFileData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
189 static BOOL FTP_InitListenSocket(ftp_session_t*);
190 static BOOL FTP_ConnectToHost(ftp_session_t*);
191 static BOOL FTP_SendPassword(ftp_session_t*);
192 static BOOL FTP_SendAccount(ftp_session_t*);
193 static BOOL FTP_SendType(ftp_session_t*, DWORD dwType);
194 static BOOL FTP_SendPort(ftp_session_t*);
195 static BOOL FTP_DoPassive(ftp_session_t*);
196 static BOOL FTP_SendPortOrPasv(ftp_session_t*);
197 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
198 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
199 static BOOL FTP_ParseDirectory(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
200 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
201 static HINTERNET FTP_ReceiveFileList(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
202 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
203 static DWORD FTP_SetResponseError(DWORD dwResponse);
204 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
205 static BOOL FTP_FtpPutFileW(ftp_session_t*, LPCWSTR lpszLocalFile,
206 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
207 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
208 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
209 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t*,
210 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
211 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t*, LPWSTR lpszCurrentDirectory,
212 LPDWORD lpdwCurrentDirectory);
213 static BOOL FTP_FtpRenameFileW(ftp_session_t*, LPCWSTR lpszSrc, LPCWSTR lpszDest);
214 static BOOL FTP_FtpRemoveDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
215 static BOOL FTP_FtpDeleteFileW(ftp_session_t*, LPCWSTR lpszFileName);
216 static BOOL FTP_FtpGetFileW(ftp_session_t*, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
217 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
218 DWORD_PTR dwContext);
220 /* A temporary helper until we get rid of INTERNET_GetLastError calls */
221 static BOOL res_to_le(DWORD res)
223 if(res != ERROR_SUCCESS)
224 INTERNET_SetLastError(res);
225 return res == ERROR_SUCCESS;
228 /***********************************************************************
229 * FtpPutFileA (WININET.@)
231 * Uploads a file to the FTP server
233 * RETURNS
234 * TRUE on success
235 * FALSE on failure
238 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
239 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
241 LPWSTR lpwzLocalFile;
242 LPWSTR lpwzNewRemoteFile;
243 BOOL ret;
245 lpwzLocalFile = heap_strdupAtoW(lpszLocalFile);
246 lpwzNewRemoteFile = heap_strdupAtoW(lpszNewRemoteFile);
247 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
248 dwFlags, dwContext);
249 heap_free(lpwzLocalFile);
250 heap_free(lpwzNewRemoteFile);
251 return ret;
254 typedef struct {
255 task_header_t hdr;
256 WCHAR *local_file;
257 WCHAR *remote_file;
258 DWORD flags;
259 DWORD_PTR context;
260 } put_file_task_t;
262 static void AsyncFtpPutFileProc(task_header_t *hdr)
264 put_file_task_t *task = (put_file_task_t*)hdr;
265 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
267 TRACE("%p\n", session);
269 FTP_FtpPutFileW(session, task->local_file, task->remote_file,
270 task->flags, task->context);
272 heap_free(task->local_file);
273 heap_free(task->remote_file);
276 /***********************************************************************
277 * FtpPutFileW (WININET.@)
279 * Uploads a file to the FTP server
281 * RETURNS
282 * TRUE on success
283 * FALSE on failure
286 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
287 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
289 ftp_session_t *lpwfs;
290 appinfo_t *hIC = NULL;
291 BOOL r = FALSE;
293 if (!lpszLocalFile || !lpszNewRemoteFile)
295 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
296 return FALSE;
299 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
300 if (!lpwfs)
302 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
303 return FALSE;
306 if (WH_HFTPSESSION != lpwfs->hdr.htype)
308 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
309 goto lend;
312 if (lpwfs->download_in_progress != NULL)
314 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
315 goto lend;
318 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
320 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
321 goto lend;
324 hIC = lpwfs->lpAppInfo;
325 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
327 put_file_task_t *task = alloc_async_task(&lpwfs->hdr, AsyncFtpPutFileProc, sizeof(*task));
329 task->local_file = heap_strdupW(lpszLocalFile);
330 task->remote_file = heap_strdupW(lpszNewRemoteFile);
331 task->flags = dwFlags;
332 task->context = dwContext;
334 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
336 else
338 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
339 lpszNewRemoteFile, dwFlags, dwContext);
342 lend:
343 WININET_Release( &lpwfs->hdr );
345 return r;
348 /***********************************************************************
349 * FTP_FtpPutFileW (Internal)
351 * Uploads a file to the FTP server
353 * RETURNS
354 * TRUE on success
355 * FALSE on failure
358 static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
359 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
361 HANDLE hFile;
362 BOOL bSuccess = FALSE;
363 appinfo_t *hIC = NULL;
364 INT nResCode;
366 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
368 /* Clear any error information */
369 INTERNET_SetLastError(0);
371 /* Open file to be uploaded */
372 if (INVALID_HANDLE_VALUE ==
373 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
374 /* Let CreateFile set the appropriate error */
375 return FALSE;
377 hIC = lpwfs->lpAppInfo;
379 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
381 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
383 INT nDataSocket;
385 /* Get data socket to server */
386 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
388 FTP_SendData(lpwfs, nDataSocket, hFile);
389 closesocket(nDataSocket);
390 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
391 if (nResCode)
393 if (nResCode == 226)
394 bSuccess = TRUE;
395 else
396 FTP_SetResponseError(nResCode);
401 if (lpwfs->lstnSocket != -1)
403 closesocket(lpwfs->lstnSocket);
404 lpwfs->lstnSocket = -1;
407 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
409 INTERNET_ASYNC_RESULT iar;
411 iar.dwResult = (DWORD)bSuccess;
412 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
413 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
414 &iar, sizeof(INTERNET_ASYNC_RESULT));
417 CloseHandle(hFile);
419 return bSuccess;
423 /***********************************************************************
424 * FtpSetCurrentDirectoryA (WININET.@)
426 * Change the working directory on the FTP server
428 * RETURNS
429 * TRUE on success
430 * FALSE on failure
433 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
435 LPWSTR lpwzDirectory;
436 BOOL ret;
438 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
439 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
440 heap_free(lpwzDirectory);
441 return ret;
444 typedef struct {
445 task_header_t hdr;
446 WCHAR *directory;
447 } directory_task_t;
449 static void AsyncFtpSetCurrentDirectoryProc(task_header_t *hdr)
451 directory_task_t *task = (directory_task_t*)hdr;
452 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
454 TRACE("%p\n", session);
456 FTP_FtpSetCurrentDirectoryW(session, task->directory);
457 heap_free(task->directory);
460 /***********************************************************************
461 * FtpSetCurrentDirectoryW (WININET.@)
463 * Change the working directory on the FTP server
465 * RETURNS
466 * TRUE on success
467 * FALSE on failure
470 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
472 ftp_session_t *lpwfs = NULL;
473 appinfo_t *hIC = NULL;
474 BOOL r = FALSE;
476 if (!lpszDirectory)
478 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
479 goto lend;
482 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
483 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
485 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
486 goto lend;
489 if (lpwfs->download_in_progress != NULL)
491 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
492 goto lend;
495 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
497 hIC = lpwfs->lpAppInfo;
498 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
500 directory_task_t *task;
502 task = alloc_async_task(&lpwfs->hdr, AsyncFtpSetCurrentDirectoryProc, sizeof(*task));
503 task->directory = heap_strdupW(lpszDirectory);
505 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
507 else
509 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
512 lend:
513 if( lpwfs )
514 WININET_Release( &lpwfs->hdr );
516 return r;
520 /***********************************************************************
521 * FTP_FtpSetCurrentDirectoryW (Internal)
523 * Change the working directory on the FTP server
525 * RETURNS
526 * TRUE on success
527 * FALSE on failure
530 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
532 INT nResCode;
533 appinfo_t *hIC = NULL;
534 DWORD bSuccess = FALSE;
536 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
538 /* Clear any error information */
539 INTERNET_SetLastError(0);
541 hIC = lpwfs->lpAppInfo;
542 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
543 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
544 goto lend;
546 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
548 if (nResCode)
550 if (nResCode == 250)
551 bSuccess = TRUE;
552 else
553 FTP_SetResponseError(nResCode);
556 lend:
557 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
559 INTERNET_ASYNC_RESULT iar;
561 iar.dwResult = bSuccess;
562 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
563 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
564 &iar, sizeof(INTERNET_ASYNC_RESULT));
566 return bSuccess;
570 /***********************************************************************
571 * FtpCreateDirectoryA (WININET.@)
573 * Create new directory on the FTP server
575 * RETURNS
576 * TRUE on success
577 * FALSE on failure
580 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
582 LPWSTR lpwzDirectory;
583 BOOL ret;
585 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
586 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
587 heap_free(lpwzDirectory);
588 return ret;
592 static void AsyncFtpCreateDirectoryProc(task_header_t *hdr)
594 directory_task_t *task = (directory_task_t*)hdr;
595 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
597 TRACE(" %p\n", session);
599 FTP_FtpCreateDirectoryW(session, task->directory);
600 heap_free(task->directory);
603 /***********************************************************************
604 * FtpCreateDirectoryW (WININET.@)
606 * Create new directory on the FTP server
608 * RETURNS
609 * TRUE on success
610 * FALSE on failure
613 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
615 ftp_session_t *lpwfs;
616 appinfo_t *hIC = NULL;
617 BOOL r = FALSE;
619 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
620 if (!lpwfs)
622 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
623 return FALSE;
626 if (WH_HFTPSESSION != lpwfs->hdr.htype)
628 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
629 goto lend;
632 if (lpwfs->download_in_progress != NULL)
634 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
635 goto lend;
638 if (!lpszDirectory)
640 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
641 goto lend;
644 hIC = lpwfs->lpAppInfo;
645 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
647 directory_task_t *task;
649 task = alloc_async_task(&lpwfs->hdr, AsyncFtpCreateDirectoryProc, sizeof(*task));
650 task->directory = heap_strdupW(lpszDirectory);
652 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
654 else
656 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
658 lend:
659 WININET_Release( &lpwfs->hdr );
661 return r;
665 /***********************************************************************
666 * FTP_FtpCreateDirectoryW (Internal)
668 * Create new directory on the FTP server
670 * RETURNS
671 * TRUE on success
672 * FALSE on failure
675 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
677 INT nResCode;
678 BOOL bSuccess = FALSE;
679 appinfo_t *hIC = NULL;
681 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
683 /* Clear any error information */
684 INTERNET_SetLastError(0);
686 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
687 goto lend;
689 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
690 if (nResCode)
692 if (nResCode == 257)
693 bSuccess = TRUE;
694 else
695 FTP_SetResponseError(nResCode);
698 lend:
699 hIC = lpwfs->lpAppInfo;
700 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
702 INTERNET_ASYNC_RESULT iar;
704 iar.dwResult = (DWORD)bSuccess;
705 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
706 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
707 &iar, sizeof(INTERNET_ASYNC_RESULT));
710 return bSuccess;
713 /***********************************************************************
714 * FtpFindFirstFileA (WININET.@)
716 * Search the specified directory
718 * RETURNS
719 * HINTERNET on success
720 * NULL on failure
723 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
724 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
726 LPWSTR lpwzSearchFile;
727 WIN32_FIND_DATAW wfd;
728 LPWIN32_FIND_DATAW lpFindFileDataW;
729 HINTERNET ret;
731 lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
732 lpFindFileDataW = lpFindFileData?&wfd:NULL;
733 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
734 heap_free(lpwzSearchFile);
736 if (ret && lpFindFileData)
737 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
739 return ret;
742 typedef struct {
743 task_header_t hdr;
744 WCHAR *search_file;
745 WIN32_FIND_DATAW *find_file_data;
746 DWORD flags;
747 DWORD_PTR context;
748 } find_first_file_task_t;
750 static void AsyncFtpFindFirstFileProc(task_header_t *hdr)
752 find_first_file_task_t *task = (find_first_file_task_t*)hdr;
753 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
755 TRACE("%p\n", session);
757 FTP_FtpFindFirstFileW(session, task->search_file, task->find_file_data, task->flags, task->context);
758 heap_free(task->search_file);
761 /***********************************************************************
762 * FtpFindFirstFileW (WININET.@)
764 * Search the specified directory
766 * RETURNS
767 * HINTERNET on success
768 * NULL on failure
771 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
772 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
774 ftp_session_t *lpwfs;
775 appinfo_t *hIC = NULL;
776 HINTERNET r = NULL;
778 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
779 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
781 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
782 goto lend;
785 if (lpwfs->download_in_progress != NULL)
787 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
788 goto lend;
791 hIC = lpwfs->lpAppInfo;
792 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
794 find_first_file_task_t *task;
796 task = alloc_async_task(&lpwfs->hdr, AsyncFtpFindFirstFileProc, sizeof(*task));
797 task->search_file = heap_strdupW(lpszSearchFile);
798 task->find_file_data = lpFindFileData;
799 task->flags = dwFlags;
800 task->context = dwContext;
802 INTERNET_AsyncCall(&task->hdr);
803 r = NULL;
805 else
807 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
808 dwFlags, dwContext);
810 lend:
811 if( lpwfs )
812 WININET_Release( &lpwfs->hdr );
814 return r;
818 /***********************************************************************
819 * FTP_FtpFindFirstFileW (Internal)
821 * Search the specified directory
823 * RETURNS
824 * HINTERNET on success
825 * NULL on failure
828 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
829 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
831 INT nResCode;
832 appinfo_t *hIC = NULL;
833 HINTERNET hFindNext = NULL;
835 TRACE("\n");
837 /* Clear any error information */
838 INTERNET_SetLastError(0);
840 if (!FTP_InitListenSocket(lpwfs))
841 goto lend;
843 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
844 goto lend;
846 if (!FTP_SendPortOrPasv(lpwfs))
847 goto lend;
849 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
850 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
851 goto lend;
853 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
854 if (nResCode)
856 if (nResCode == 125 || nResCode == 150)
858 INT nDataSocket;
860 /* Get data socket to server */
861 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
863 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
864 closesocket(nDataSocket);
865 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
866 if (nResCode != 226 && nResCode != 250)
867 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
870 else
871 FTP_SetResponseError(nResCode);
874 lend:
875 if (lpwfs->lstnSocket != -1)
877 closesocket(lpwfs->lstnSocket);
878 lpwfs->lstnSocket = -1;
881 hIC = lpwfs->lpAppInfo;
882 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
884 INTERNET_ASYNC_RESULT iar;
886 if (hFindNext)
888 iar.dwResult = (DWORD_PTR)hFindNext;
889 iar.dwError = ERROR_SUCCESS;
890 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
891 &iar, sizeof(INTERNET_ASYNC_RESULT));
894 iar.dwResult = (DWORD_PTR)hFindNext;
895 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
896 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
897 &iar, sizeof(INTERNET_ASYNC_RESULT));
900 return hFindNext;
904 /***********************************************************************
905 * FtpGetCurrentDirectoryA (WININET.@)
907 * Retrieves the current directory
909 * RETURNS
910 * TRUE on success
911 * FALSE on failure
914 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
915 LPDWORD lpdwCurrentDirectory)
917 WCHAR *dir = NULL;
918 DWORD len;
919 BOOL ret;
921 if(lpdwCurrentDirectory) {
922 len = *lpdwCurrentDirectory;
923 if(lpszCurrentDirectory)
925 dir = heap_alloc(len * sizeof(WCHAR));
926 if (NULL == dir)
928 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
929 return FALSE;
933 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
935 if (ret && lpszCurrentDirectory)
936 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
938 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
939 heap_free(dir);
940 return ret;
943 typedef struct {
944 task_header_t hdr;
945 WCHAR *directory;
946 DWORD *directory_len;
947 } get_current_dir_task_t;
949 static void AsyncFtpGetCurrentDirectoryProc(task_header_t *hdr)
951 get_current_dir_task_t *task = (get_current_dir_task_t*)hdr;
952 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
954 TRACE("%p\n", session);
956 FTP_FtpGetCurrentDirectoryW(session, task->directory, task->directory_len);
959 /***********************************************************************
960 * FtpGetCurrentDirectoryW (WININET.@)
962 * Retrieves the current directory
964 * RETURNS
965 * TRUE on success
966 * FALSE on failure
969 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
970 LPDWORD lpdwCurrentDirectory)
972 ftp_session_t *lpwfs;
973 appinfo_t *hIC = NULL;
974 BOOL r = FALSE;
976 TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
978 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
979 if (NULL == lpwfs)
981 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
982 goto lend;
985 if (WH_HFTPSESSION != lpwfs->hdr.htype)
987 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
988 goto lend;
991 if (!lpdwCurrentDirectory)
993 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
994 goto lend;
997 if (lpszCurrentDirectory == NULL)
999 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1000 goto lend;
1003 if (lpwfs->download_in_progress != NULL)
1005 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1006 goto lend;
1009 hIC = lpwfs->lpAppInfo;
1010 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1012 get_current_dir_task_t *task;
1014 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetCurrentDirectoryProc, sizeof(*task));
1015 task->directory = lpszCurrentDirectory;
1016 task->directory_len = lpdwCurrentDirectory;
1018 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1020 else
1022 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
1023 lpdwCurrentDirectory);
1026 lend:
1027 if( lpwfs )
1028 WININET_Release( &lpwfs->hdr );
1030 return r;
1034 /***********************************************************************
1035 * FTP_FtpGetCurrentDirectoryW (Internal)
1037 * Retrieves the current directory
1039 * RETURNS
1040 * TRUE on success
1041 * FALSE on failure
1044 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
1045 LPDWORD lpdwCurrentDirectory)
1047 INT nResCode;
1048 appinfo_t *hIC = NULL;
1049 DWORD bSuccess = FALSE;
1051 /* Clear any error information */
1052 INTERNET_SetLastError(0);
1054 hIC = lpwfs->lpAppInfo;
1055 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1056 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1057 goto lend;
1059 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1060 if (nResCode)
1062 if (nResCode == 257) /* Extract directory name */
1064 DWORD firstpos, lastpos, len;
1065 LPWSTR lpszResponseBuffer = heap_strdupAtoW(INTERNET_GetResponseBuffer());
1067 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1069 if ('"' == lpszResponseBuffer[lastpos])
1071 if (!firstpos)
1072 firstpos = lastpos;
1073 else
1074 break;
1077 len = lastpos - firstpos;
1078 if (*lpdwCurrentDirectory >= len)
1080 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1081 lpszCurrentDirectory[len - 1] = 0;
1082 *lpdwCurrentDirectory = len;
1083 bSuccess = TRUE;
1085 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1087 heap_free(lpszResponseBuffer);
1089 else
1090 FTP_SetResponseError(nResCode);
1093 lend:
1094 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1096 INTERNET_ASYNC_RESULT iar;
1098 iar.dwResult = bSuccess;
1099 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1100 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1101 &iar, sizeof(INTERNET_ASYNC_RESULT));
1104 return bSuccess;
1108 /***********************************************************************
1109 * FTPFILE_Destroy(internal)
1111 * Closes the file transfer handle. This also 'cleans' the data queue of
1112 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1115 static void FTPFILE_Destroy(object_header_t *hdr)
1117 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1118 ftp_session_t *lpwfs = lpwh->lpFtpSession;
1119 INT nResCode;
1121 TRACE("\n");
1123 if (lpwh->cache_file_handle != INVALID_HANDLE_VALUE)
1124 CloseHandle(lpwh->cache_file_handle);
1126 heap_free(lpwh->cache_file);
1128 if (!lpwh->session_deleted)
1129 lpwfs->download_in_progress = NULL;
1131 if (lpwh->nDataSocket != -1)
1132 closesocket(lpwh->nDataSocket);
1134 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1135 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1137 WININET_Release(&lpwh->lpFtpSession->hdr);
1140 static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1142 switch(option) {
1143 case INTERNET_OPTION_HANDLE_TYPE:
1144 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1146 if (*size < sizeof(ULONG))
1147 return ERROR_INSUFFICIENT_BUFFER;
1149 *size = sizeof(DWORD);
1150 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1151 return ERROR_SUCCESS;
1152 case INTERNET_OPTION_DATAFILE_NAME:
1154 DWORD required;
1155 ftp_file_t *file = (ftp_file_t *)hdr;
1157 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1159 if (!file->cache_file)
1161 *size = 0;
1162 return ERROR_INTERNET_ITEM_NOT_FOUND;
1164 if (unicode)
1166 required = (lstrlenW(file->cache_file) + 1) * sizeof(WCHAR);
1167 if (*size < required)
1168 return ERROR_INSUFFICIENT_BUFFER;
1170 *size = required;
1171 memcpy(buffer, file->cache_file, *size);
1172 return ERROR_SUCCESS;
1174 else
1176 required = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, NULL, 0, NULL, NULL);
1177 if (required > *size)
1178 return ERROR_INSUFFICIENT_BUFFER;
1180 *size = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, buffer, *size, NULL, NULL);
1181 return ERROR_SUCCESS;
1185 return INET_QueryOption(hdr, option, buffer, size, unicode);
1188 static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
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 = 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_ReadFileEx(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_size,
1213 DWORD flags, DWORD_PTR context)
1215 return FTPFILE_ReadFile(hdr, buf, size, ret_size);
1218 static DWORD FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1220 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1221 int res;
1223 res = send(lpwh->nDataSocket, buffer, size, 0);
1225 *written = res>0 ? res : 0;
1226 return res >= 0 ? ERROR_SUCCESS : sock_get_error(errno);
1229 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
1231 INTERNET_ASYNC_RESULT iar;
1232 BYTE buffer[4096];
1233 int available;
1235 TRACE("%p\n", file);
1237 available = recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1239 if(available != -1) {
1240 iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1241 iar.dwError = first_notif ? 0 : available;
1242 }else {
1243 iar.dwResult = 0;
1244 iar.dwError = INTERNET_GetLastError();
1247 INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1248 sizeof(INTERNET_ASYNC_RESULT));
1251 static void FTPFILE_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
1253 ftp_file_t *file = (ftp_file_t*)workRequest->hdr;
1255 FTP_ReceiveRequestData(file, FALSE);
1258 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1260 ftp_file_t *file = (ftp_file_t*) hdr;
1261 int retval, unread = 0;
1263 TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
1265 #ifdef FIONREAD
1266 retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
1267 if (!retval)
1268 TRACE("%d bytes of queued, but unread data\n", unread);
1269 #else
1270 FIXME("FIONREAD not available\n");
1271 #endif
1273 *available = unread;
1275 if(!unread) {
1276 BYTE byte;
1278 *available = 0;
1280 retval = recv(file->nDataSocket, &byte, 1, MSG_PEEK);
1281 if(retval > 0) {
1282 task_header_t *task;
1284 task = alloc_async_task(&file->hdr, FTPFILE_AsyncQueryDataAvailableProc, sizeof(*task));
1285 INTERNET_AsyncCall(task);
1287 *available = 0;
1288 return ERROR_IO_PENDING;
1292 return ERROR_SUCCESS;
1296 static const object_vtbl_t FTPFILEVtbl = {
1297 FTPFILE_Destroy,
1298 NULL,
1299 FTPFILE_QueryOption,
1300 INET_SetOption,
1301 FTPFILE_ReadFile,
1302 FTPFILE_ReadFileEx,
1303 FTPFILE_WriteFile,
1304 FTPFILE_QueryDataAvailable,
1305 NULL
1308 /***********************************************************************
1309 * FTP_FtpOpenFileW (Internal)
1311 * Open a remote file for writing or reading
1313 * RETURNS
1314 * HINTERNET handle on success
1315 * NULL on failure
1318 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1319 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1320 DWORD_PTR dwContext)
1322 INT nDataSocket;
1323 BOOL bSuccess = FALSE;
1324 ftp_file_t *lpwh = NULL;
1325 appinfo_t *hIC = NULL;
1327 TRACE("\n");
1329 /* Clear any error information */
1330 INTERNET_SetLastError(0);
1332 if (GENERIC_READ == fdwAccess)
1334 /* Set up socket to retrieve data */
1335 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1337 else if (GENERIC_WRITE == fdwAccess)
1339 /* Set up socket to send data */
1340 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1343 /* Get data socket to server */
1344 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1346 lpwh = alloc_object(&lpwfs->hdr, &FTPFILEVtbl, sizeof(ftp_file_t));
1347 lpwh->hdr.htype = WH_HFILE;
1348 lpwh->hdr.dwFlags = dwFlags;
1349 lpwh->hdr.dwContext = dwContext;
1350 lpwh->nDataSocket = nDataSocket;
1351 lpwh->cache_file = NULL;
1352 lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1353 lpwh->session_deleted = FALSE;
1355 WININET_AddRef( &lpwfs->hdr );
1356 lpwh->lpFtpSession = lpwfs;
1357 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1359 /* Indicate that a download is currently in progress */
1360 lpwfs->download_in_progress = lpwh;
1363 if (lpwfs->lstnSocket != -1)
1365 closesocket(lpwfs->lstnSocket);
1366 lpwfs->lstnSocket = -1;
1369 if (bSuccess && fdwAccess == GENERIC_READ)
1371 WCHAR filename[MAX_PATH + 1];
1372 URL_COMPONENTSW uc;
1373 DWORD len;
1375 memset(&uc, 0, sizeof(uc));
1376 uc.dwStructSize = sizeof(uc);
1377 uc.nScheme = INTERNET_SCHEME_FTP;
1378 uc.lpszHostName = lpwfs->servername;
1379 uc.nPort = lpwfs->serverport;
1380 uc.lpszUserName = lpwfs->lpszUserName;
1381 uc.lpszUrlPath = heap_strdupW(lpszFileName);
1383 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1385 WCHAR *url = heap_alloc(len * sizeof(WCHAR));
1387 if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1389 lpwh->cache_file = heap_strdupW(filename);
1390 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1391 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1392 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1394 WARN("Could not create cache file: %u\n", GetLastError());
1395 heap_free(lpwh->cache_file);
1396 lpwh->cache_file = NULL;
1399 heap_free(url);
1401 heap_free(uc.lpszUrlPath);
1404 hIC = lpwfs->lpAppInfo;
1405 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1407 INTERNET_ASYNC_RESULT iar;
1409 if (lpwh)
1411 iar.dwResult = (DWORD_PTR)lpwh->hdr.hInternet;
1412 iar.dwError = ERROR_SUCCESS;
1413 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1414 &iar, sizeof(INTERNET_ASYNC_RESULT));
1417 if(bSuccess) {
1418 FTP_ReceiveRequestData(lpwh, TRUE);
1419 }else {
1420 iar.dwResult = 0;
1421 iar.dwError = INTERNET_GetLastError();
1422 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1423 &iar, sizeof(INTERNET_ASYNC_RESULT));
1427 if(!bSuccess)
1428 return FALSE;
1430 return lpwh->hdr.hInternet;
1434 /***********************************************************************
1435 * FtpOpenFileA (WININET.@)
1437 * Open a remote file for writing or reading
1439 * RETURNS
1440 * HINTERNET handle on success
1441 * NULL on failure
1444 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1445 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1446 DWORD_PTR dwContext)
1448 LPWSTR lpwzFileName;
1449 HINTERNET ret;
1451 lpwzFileName = heap_strdupAtoW(lpszFileName);
1452 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1453 heap_free(lpwzFileName);
1454 return ret;
1457 typedef struct {
1458 task_header_t hdr;
1459 WCHAR *file_name;
1460 DWORD access;
1461 DWORD flags;
1462 DWORD_PTR context;
1463 } open_file_task_t;
1465 static void AsyncFtpOpenFileProc(task_header_t *hdr)
1467 open_file_task_t *task = (open_file_task_t*)hdr;
1468 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1470 TRACE("%p\n", session);
1472 FTP_FtpOpenFileW(session, task->file_name, task->access, task->flags, task->context);
1473 heap_free(task->file_name);
1476 /***********************************************************************
1477 * FtpOpenFileW (WININET.@)
1479 * Open a remote file for writing or reading
1481 * RETURNS
1482 * HINTERNET handle on success
1483 * NULL on failure
1486 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1487 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1488 DWORD_PTR dwContext)
1490 ftp_session_t *lpwfs;
1491 appinfo_t *hIC = NULL;
1492 HINTERNET r = NULL;
1494 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1495 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1497 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1498 if (!lpwfs)
1500 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1501 return FALSE;
1504 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1506 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1507 goto lend;
1510 if ((!lpszFileName) ||
1511 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1512 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1514 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1515 goto lend;
1518 if (lpwfs->download_in_progress != NULL)
1520 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1521 goto lend;
1524 hIC = lpwfs->lpAppInfo;
1525 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1527 open_file_task_t *task;
1529 task = alloc_async_task(&lpwfs->hdr, AsyncFtpOpenFileProc, sizeof(*task));
1530 task->file_name = heap_strdupW(lpszFileName);
1531 task->access = fdwAccess;
1532 task->flags = dwFlags;
1533 task->context = dwContext;
1535 INTERNET_AsyncCall(&task->hdr);
1536 r = NULL;
1538 else
1540 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1543 lend:
1544 WININET_Release( &lpwfs->hdr );
1546 return r;
1550 /***********************************************************************
1551 * FtpGetFileA (WININET.@)
1553 * Retrieve file from the FTP server
1555 * RETURNS
1556 * TRUE on success
1557 * FALSE on failure
1560 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1561 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1562 DWORD_PTR dwContext)
1564 LPWSTR lpwzRemoteFile;
1565 LPWSTR lpwzNewFile;
1566 BOOL ret;
1568 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1569 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1570 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1571 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1572 heap_free(lpwzRemoteFile);
1573 heap_free(lpwzNewFile);
1574 return ret;
1577 typedef struct {
1578 task_header_t hdr;
1579 WCHAR *remote_file;
1580 WCHAR *new_file;
1581 BOOL fail_if_exists;
1582 DWORD local_attr;
1583 DWORD flags;
1584 DWORD_PTR context;
1585 } get_file_task_t;
1587 static void AsyncFtpGetFileProc(task_header_t *hdr)
1589 get_file_task_t *task = (get_file_task_t*)hdr;
1590 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1592 TRACE("%p\n", session);
1594 FTP_FtpGetFileW(session, task->remote_file, task->new_file, task->fail_if_exists,
1595 task->local_attr, task->flags, task->context);
1596 heap_free(task->remote_file);
1597 heap_free(task->new_file);
1601 /***********************************************************************
1602 * FtpGetFileW (WININET.@)
1604 * Retrieve file from the FTP server
1606 * RETURNS
1607 * TRUE on success
1608 * FALSE on failure
1611 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1612 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1613 DWORD_PTR dwContext)
1615 ftp_session_t *lpwfs;
1616 appinfo_t *hIC = NULL;
1617 BOOL r = FALSE;
1619 if (!lpszRemoteFile || !lpszNewFile)
1621 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1622 return FALSE;
1625 lpwfs = (ftp_session_t*) get_handle_object( hInternet );
1626 if (!lpwfs)
1628 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1629 return FALSE;
1632 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1634 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1635 goto lend;
1638 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1640 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1641 goto lend;
1644 if (lpwfs->download_in_progress != NULL)
1646 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1647 goto lend;
1650 hIC = lpwfs->lpAppInfo;
1651 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1653 get_file_task_t *task;
1655 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetFileProc, sizeof(*task));
1656 task->remote_file = heap_strdupW(lpszRemoteFile);
1657 task->new_file = heap_strdupW(lpszNewFile);
1658 task->local_attr = dwLocalFlagsAttribute;
1659 task->fail_if_exists = fFailIfExists;
1660 task->flags = dwInternetFlags;
1661 task->context = dwContext;
1663 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1665 else
1667 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1668 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1671 lend:
1672 WININET_Release( &lpwfs->hdr );
1674 return r;
1678 /***********************************************************************
1679 * FTP_FtpGetFileW (Internal)
1681 * Retrieve file from the FTP server
1683 * RETURNS
1684 * TRUE on success
1685 * FALSE on failure
1688 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1689 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1690 DWORD_PTR dwContext)
1692 BOOL bSuccess = FALSE;
1693 HANDLE hFile;
1694 appinfo_t *hIC = NULL;
1696 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1698 /* Clear any error information */
1699 INTERNET_SetLastError(0);
1701 /* Ensure we can write to lpszNewfile by opening it */
1702 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1703 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1704 if (INVALID_HANDLE_VALUE == hFile)
1705 return FALSE;
1707 /* Set up socket to retrieve data */
1708 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1710 INT nDataSocket;
1712 /* Get data socket to server */
1713 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1715 INT nResCode;
1717 /* Receive data */
1718 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1719 closesocket(nDataSocket);
1721 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1722 if (nResCode)
1724 if (nResCode == 226)
1725 bSuccess = TRUE;
1726 else
1727 FTP_SetResponseError(nResCode);
1732 if (lpwfs->lstnSocket != -1)
1734 closesocket(lpwfs->lstnSocket);
1735 lpwfs->lstnSocket = -1;
1738 CloseHandle(hFile);
1740 hIC = lpwfs->lpAppInfo;
1741 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1743 INTERNET_ASYNC_RESULT iar;
1745 iar.dwResult = (DWORD)bSuccess;
1746 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1747 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1748 &iar, sizeof(INTERNET_ASYNC_RESULT));
1751 if (!bSuccess) DeleteFileW(lpszNewFile);
1752 return bSuccess;
1755 /***********************************************************************
1756 * FtpGetFileSize (WININET.@)
1758 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1760 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1762 if (lpdwFileSizeHigh)
1763 *lpdwFileSizeHigh = 0;
1765 return 0;
1768 /***********************************************************************
1769 * FtpDeleteFileA (WININET.@)
1771 * Delete a file on the ftp server
1773 * RETURNS
1774 * TRUE on success
1775 * FALSE on failure
1778 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1780 LPWSTR lpwzFileName;
1781 BOOL ret;
1783 lpwzFileName = heap_strdupAtoW(lpszFileName);
1784 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1785 heap_free(lpwzFileName);
1786 return ret;
1789 typedef struct {
1790 task_header_t hdr;
1791 WCHAR *file_name;
1792 } delete_file_task_t;
1794 static void AsyncFtpDeleteFileProc(task_header_t *hdr)
1796 delete_file_task_t *task = (delete_file_task_t*)hdr;
1797 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1799 TRACE("%p\n", session);
1801 FTP_FtpDeleteFileW(session, task->file_name);
1802 heap_free(task->file_name);
1805 /***********************************************************************
1806 * FtpDeleteFileW (WININET.@)
1808 * Delete a file on the ftp server
1810 * RETURNS
1811 * TRUE on success
1812 * FALSE on failure
1815 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1817 ftp_session_t *lpwfs;
1818 appinfo_t *hIC = NULL;
1819 BOOL r = FALSE;
1821 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1822 if (!lpwfs)
1824 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1825 return FALSE;
1828 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1830 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1831 goto lend;
1834 if (lpwfs->download_in_progress != NULL)
1836 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1837 goto lend;
1840 if (!lpszFileName)
1842 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1843 goto lend;
1846 hIC = lpwfs->lpAppInfo;
1847 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1849 delete_file_task_t *task;
1851 task = alloc_async_task(&lpwfs->hdr, AsyncFtpDeleteFileProc, sizeof(*task));
1852 task->file_name = heap_strdupW(lpszFileName);
1854 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1856 else
1858 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1861 lend:
1862 WININET_Release( &lpwfs->hdr );
1864 return r;
1867 /***********************************************************************
1868 * FTP_FtpDeleteFileW (Internal)
1870 * Delete a file on the ftp server
1872 * RETURNS
1873 * TRUE on success
1874 * FALSE on failure
1877 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1879 INT nResCode;
1880 BOOL bSuccess = FALSE;
1881 appinfo_t *hIC = NULL;
1883 TRACE("%p\n", lpwfs);
1885 /* Clear any error information */
1886 INTERNET_SetLastError(0);
1888 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1889 goto lend;
1891 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1892 if (nResCode)
1894 if (nResCode == 250)
1895 bSuccess = TRUE;
1896 else
1897 FTP_SetResponseError(nResCode);
1899 lend:
1900 hIC = lpwfs->lpAppInfo;
1901 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1903 INTERNET_ASYNC_RESULT iar;
1905 iar.dwResult = (DWORD)bSuccess;
1906 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1907 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1908 &iar, sizeof(INTERNET_ASYNC_RESULT));
1911 return bSuccess;
1915 /***********************************************************************
1916 * FtpRemoveDirectoryA (WININET.@)
1918 * Remove a directory on the ftp server
1920 * RETURNS
1921 * TRUE on success
1922 * FALSE on failure
1925 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1927 LPWSTR lpwzDirectory;
1928 BOOL ret;
1930 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1931 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1932 heap_free(lpwzDirectory);
1933 return ret;
1936 static void AsyncFtpRemoveDirectoryProc(task_header_t *hdr)
1938 directory_task_t *task = (directory_task_t*)hdr;
1939 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1941 TRACE("%p\n", session);
1943 FTP_FtpRemoveDirectoryW(session, task->directory);
1944 heap_free(task->directory);
1947 /***********************************************************************
1948 * FtpRemoveDirectoryW (WININET.@)
1950 * Remove a directory on the ftp server
1952 * RETURNS
1953 * TRUE on success
1954 * FALSE on failure
1957 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1959 ftp_session_t *lpwfs;
1960 appinfo_t *hIC = NULL;
1961 BOOL r = FALSE;
1963 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1964 if (!lpwfs)
1966 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1967 return FALSE;
1970 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1972 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1973 goto lend;
1976 if (lpwfs->download_in_progress != NULL)
1978 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1979 goto lend;
1982 if (!lpszDirectory)
1984 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1985 goto lend;
1988 hIC = lpwfs->lpAppInfo;
1989 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1991 directory_task_t *task;
1993 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRemoveDirectoryProc, sizeof(*task));
1994 task->directory = heap_strdupW(lpszDirectory);
1996 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1998 else
2000 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
2003 lend:
2004 WININET_Release( &lpwfs->hdr );
2006 return r;
2009 /***********************************************************************
2010 * FTP_FtpRemoveDirectoryW (Internal)
2012 * Remove a directory on the ftp server
2014 * RETURNS
2015 * TRUE on success
2016 * FALSE on failure
2019 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
2021 INT nResCode;
2022 BOOL bSuccess = FALSE;
2023 appinfo_t *hIC = NULL;
2025 TRACE("\n");
2027 /* Clear any error information */
2028 INTERNET_SetLastError(0);
2030 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2031 goto lend;
2033 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2034 if (nResCode)
2036 if (nResCode == 250)
2037 bSuccess = TRUE;
2038 else
2039 FTP_SetResponseError(nResCode);
2042 lend:
2043 hIC = lpwfs->lpAppInfo;
2044 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2046 INTERNET_ASYNC_RESULT iar;
2048 iar.dwResult = (DWORD)bSuccess;
2049 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2050 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2051 &iar, sizeof(INTERNET_ASYNC_RESULT));
2054 return bSuccess;
2058 /***********************************************************************
2059 * FtpRenameFileA (WININET.@)
2061 * Rename a file on the ftp server
2063 * RETURNS
2064 * TRUE on success
2065 * FALSE on failure
2068 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2070 LPWSTR lpwzSrc;
2071 LPWSTR lpwzDest;
2072 BOOL ret;
2074 lpwzSrc = heap_strdupAtoW(lpszSrc);
2075 lpwzDest = heap_strdupAtoW(lpszDest);
2076 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2077 heap_free(lpwzSrc);
2078 heap_free(lpwzDest);
2079 return ret;
2082 typedef struct {
2083 task_header_t hdr;
2084 WCHAR *src_file;
2085 WCHAR *dst_file;
2086 } rename_file_task_t;
2088 static void AsyncFtpRenameFileProc(task_header_t *hdr)
2090 rename_file_task_t *task = (rename_file_task_t*)hdr;
2091 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
2093 TRACE("%p\n", session);
2095 FTP_FtpRenameFileW(session, task->src_file, task->dst_file);
2096 heap_free(task->src_file);
2097 heap_free(task->dst_file);
2100 /***********************************************************************
2101 * FtpRenameFileW (WININET.@)
2103 * Rename a file on the ftp server
2105 * RETURNS
2106 * TRUE on success
2107 * FALSE on failure
2110 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2112 ftp_session_t *lpwfs;
2113 appinfo_t *hIC = NULL;
2114 BOOL r = FALSE;
2116 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
2117 if (!lpwfs)
2119 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2120 return FALSE;
2123 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2125 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2126 goto lend;
2129 if (lpwfs->download_in_progress != NULL)
2131 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2132 goto lend;
2135 if (!lpszSrc || !lpszDest)
2137 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2138 goto lend;
2141 hIC = lpwfs->lpAppInfo;
2142 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2144 rename_file_task_t *task;
2146 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRenameFileProc, sizeof(*task));
2147 task->src_file = heap_strdupW(lpszSrc);
2148 task->dst_file = heap_strdupW(lpszDest);
2150 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
2152 else
2154 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2157 lend:
2158 WININET_Release( &lpwfs->hdr );
2160 return r;
2163 /***********************************************************************
2164 * FTP_FtpRenameFileW (Internal)
2166 * Rename a file on the ftp server
2168 * RETURNS
2169 * TRUE on success
2170 * FALSE on failure
2173 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2175 INT nResCode;
2176 BOOL bSuccess = FALSE;
2177 appinfo_t *hIC = NULL;
2179 TRACE("\n");
2181 /* Clear any error information */
2182 INTERNET_SetLastError(0);
2184 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2185 goto lend;
2187 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2188 if (nResCode == 350)
2190 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2191 goto lend;
2193 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2196 if (nResCode == 250)
2197 bSuccess = TRUE;
2198 else
2199 FTP_SetResponseError(nResCode);
2201 lend:
2202 hIC = lpwfs->lpAppInfo;
2203 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2205 INTERNET_ASYNC_RESULT iar;
2207 iar.dwResult = (DWORD)bSuccess;
2208 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2209 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2210 &iar, sizeof(INTERNET_ASYNC_RESULT));
2213 return bSuccess;
2216 /***********************************************************************
2217 * FtpCommandA (WININET.@)
2219 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2220 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2222 BOOL r;
2223 WCHAR *cmdW;
2225 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2226 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2228 if (fExpectResponse)
2230 FIXME("data connection not supported\n");
2231 return FALSE;
2234 if (!lpszCommand || !lpszCommand[0])
2236 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2237 return FALSE;
2240 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2242 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2243 return FALSE;
2246 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2248 heap_free(cmdW);
2249 return r;
2252 /***********************************************************************
2253 * FtpCommandW (WININET.@)
2255 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2256 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2258 BOOL r = FALSE;
2259 ftp_session_t *lpwfs;
2260 LPSTR cmd = NULL;
2261 DWORD len, nBytesSent= 0;
2262 INT nResCode, nRC = 0;
2264 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2265 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2267 if (!lpszCommand || !lpszCommand[0])
2269 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2270 return FALSE;
2273 if (fExpectResponse)
2275 FIXME("data connection not supported\n");
2276 return FALSE;
2279 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
2280 if (!lpwfs)
2282 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2283 return FALSE;
2286 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2288 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2289 goto lend;
2292 if (lpwfs->download_in_progress != NULL)
2294 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2295 goto lend;
2298 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2299 if ((cmd = heap_alloc(len)))
2300 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2301 else
2303 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2304 goto lend;
2307 strcat(cmd, szCRLF);
2308 len--;
2310 TRACE("Sending (%s) len(%d)\n", cmd, len);
2311 while ((nBytesSent < len) && (nRC != -1))
2313 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2314 if (nRC != -1)
2316 nBytesSent += nRC;
2317 TRACE("Sent %d bytes\n", nRC);
2321 if (nBytesSent)
2323 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2324 if (nResCode > 0 && nResCode < 400)
2325 r = TRUE;
2326 else
2327 FTP_SetResponseError(nResCode);
2330 lend:
2331 WININET_Release( &lpwfs->hdr );
2332 heap_free( cmd );
2333 return r;
2337 /***********************************************************************
2338 * FTPSESSION_Destroy (internal)
2340 * Deallocate session handle
2342 static void FTPSESSION_Destroy(object_header_t *hdr)
2344 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2346 TRACE("\n");
2348 WININET_Release(&lpwfs->lpAppInfo->hdr);
2350 heap_free(lpwfs->lpszPassword);
2351 heap_free(lpwfs->lpszUserName);
2352 heap_free(lpwfs->servername);
2355 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2357 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2359 TRACE("\n");
2361 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2362 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2364 if (lpwfs->download_in_progress != NULL)
2365 lpwfs->download_in_progress->session_deleted = TRUE;
2367 if (lpwfs->sndSocket != -1)
2368 closesocket(lpwfs->sndSocket);
2370 if (lpwfs->lstnSocket != -1)
2371 closesocket(lpwfs->lstnSocket);
2373 if (lpwfs->pasvSocket != -1)
2374 closesocket(lpwfs->pasvSocket);
2376 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2377 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2380 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2382 switch(option) {
2383 case INTERNET_OPTION_HANDLE_TYPE:
2384 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2386 if (*size < sizeof(ULONG))
2387 return ERROR_INSUFFICIENT_BUFFER;
2389 *size = sizeof(DWORD);
2390 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2391 return ERROR_SUCCESS;
2394 return INET_QueryOption(hdr, option, buffer, size, unicode);
2397 static const object_vtbl_t FTPSESSIONVtbl = {
2398 FTPSESSION_Destroy,
2399 FTPSESSION_CloseConnection,
2400 FTPSESSION_QueryOption,
2401 INET_SetOption,
2402 NULL,
2403 NULL,
2404 NULL,
2405 NULL,
2406 NULL
2410 /***********************************************************************
2411 * FTP_Connect (internal)
2413 * Connect to a ftp server
2415 * RETURNS
2416 * HINTERNET a session handle on success
2417 * NULL on failure
2419 * NOTES:
2421 * Windows uses 'anonymous' as the username, when given a NULL username
2422 * and a NULL password. The password is first looked up in:
2424 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2426 * If this entry is not present it uses the current username as the password.
2430 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2431 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2432 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2433 DWORD dwInternalFlags)
2435 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2436 'M','i','c','r','o','s','o','f','t','\\',
2437 'W','i','n','d','o','w','s','\\',
2438 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2439 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2440 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2441 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2442 static const WCHAR szEmpty[] = {'\0'};
2443 struct sockaddr_in socketAddr;
2444 INT nsocket = -1;
2445 socklen_t sock_namelen;
2446 BOOL bSuccess = FALSE;
2447 ftp_session_t *lpwfs = NULL;
2448 char szaddr[INET_ADDRSTRLEN];
2450 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2451 hIC, debugstr_w(lpszServerName),
2452 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2454 assert( hIC->hdr.htype == WH_HINIT );
2456 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2458 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2459 return NULL;
2462 lpwfs = alloc_object(&hIC->hdr, &FTPSESSIONVtbl, sizeof(ftp_session_t));
2463 if (NULL == lpwfs)
2465 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2466 return NULL;
2469 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2470 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2471 else
2472 lpwfs->serverport = nServerPort;
2474 lpwfs->hdr.htype = WH_HFTPSESSION;
2475 lpwfs->hdr.dwFlags = dwFlags;
2476 lpwfs->hdr.dwContext = dwContext;
2477 lpwfs->hdr.dwInternalFlags |= dwInternalFlags;
2478 lpwfs->download_in_progress = NULL;
2479 lpwfs->sndSocket = -1;
2480 lpwfs->lstnSocket = -1;
2481 lpwfs->pasvSocket = -1;
2483 WININET_AddRef( &hIC->hdr );
2484 lpwfs->lpAppInfo = hIC;
2485 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2487 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
2488 if(strchrW(hIC->proxy, ' '))
2489 FIXME("Several proxies not implemented.\n");
2490 if(hIC->proxyBypass)
2491 FIXME("Proxy bypass is ignored.\n");
2493 if (!lpszUserName || !strlenW(lpszUserName)) {
2494 HKEY key;
2495 WCHAR szPassword[MAX_PATH];
2496 DWORD len = sizeof(szPassword);
2498 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2500 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2501 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2502 /* Nothing in the registry, get the username and use that as the password */
2503 if (!GetUserNameW(szPassword, &len)) {
2504 /* Should never get here, but use an empty password as failsafe */
2505 strcpyW(szPassword, szEmpty);
2508 RegCloseKey(key);
2510 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2511 lpwfs->lpszPassword = heap_strdupW(szPassword);
2513 else {
2514 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2515 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2517 lpwfs->servername = heap_strdupW(lpszServerName);
2519 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2520 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2522 INTERNET_ASYNC_RESULT iar;
2524 iar.dwResult = (DWORD_PTR)lpwfs->hdr.hInternet;
2525 iar.dwError = ERROR_SUCCESS;
2527 SendAsyncCallback(&hIC->hdr, dwContext,
2528 INTERNET_STATUS_HANDLE_CREATED, &iar,
2529 sizeof(INTERNET_ASYNC_RESULT));
2532 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2533 (LPWSTR) lpszServerName, (strlenW(lpszServerName)+1) * sizeof(WCHAR));
2535 sock_namelen = sizeof(socketAddr);
2536 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen))
2538 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2539 goto lerror;
2542 if (socketAddr.sin_family != AF_INET)
2544 WARN("unsupported address family %d\n", socketAddr.sin_family);
2545 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2546 goto lerror;
2549 inet_ntop(socketAddr.sin_family, &socketAddr.sin_addr, szaddr, sizeof(szaddr));
2550 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2551 szaddr, strlen(szaddr)+1);
2553 nsocket = socket(AF_INET,SOCK_STREAM,0);
2554 if (nsocket == -1)
2556 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2557 goto lerror;
2560 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2561 szaddr, strlen(szaddr)+1);
2563 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2565 ERR("Unable to connect (%s)\n", strerror(errno));
2566 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2567 closesocket(nsocket);
2569 else
2571 TRACE("Connected to server\n");
2572 lpwfs->sndSocket = nsocket;
2573 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2574 szaddr, strlen(szaddr)+1);
2576 sock_namelen = sizeof(lpwfs->socketAddress);
2577 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2579 if (FTP_ConnectToHost(lpwfs))
2581 TRACE("Successfully logged into server\n");
2582 bSuccess = TRUE;
2586 lerror:
2587 if (!bSuccess)
2589 if(lpwfs)
2590 WININET_Release( &lpwfs->hdr );
2591 return NULL;
2594 return lpwfs->hdr.hInternet;
2598 /***********************************************************************
2599 * FTP_ConnectToHost (internal)
2601 * Connect to a ftp server
2603 * RETURNS
2604 * TRUE on success
2605 * NULL on failure
2608 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2610 INT nResCode;
2611 BOOL bSuccess = FALSE;
2613 TRACE("\n");
2614 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2616 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2617 goto lend;
2619 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2620 if (nResCode)
2622 /* Login successful... */
2623 if (nResCode == 230)
2624 bSuccess = TRUE;
2625 /* User name okay, need password... */
2626 else if (nResCode == 331)
2627 bSuccess = FTP_SendPassword(lpwfs);
2628 /* Need account for login... */
2629 else if (nResCode == 332)
2630 bSuccess = FTP_SendAccount(lpwfs);
2631 else
2632 FTP_SetResponseError(nResCode);
2635 TRACE("Returning %d\n", bSuccess);
2636 lend:
2637 return bSuccess;
2641 /***********************************************************************
2642 * FTP_SendCommandA (internal)
2644 * Send command to server
2646 * RETURNS
2647 * TRUE on success
2648 * NULL on failure
2651 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2652 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2654 DWORD len;
2655 CHAR *buf;
2656 DWORD nBytesSent = 0;
2657 int nRC = 0;
2658 DWORD dwParamLen;
2660 TRACE("%d: (%s) %d\n", ftpCmd, debugstr_a(lpszParam), nSocket);
2662 if (lpfnStatusCB)
2664 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2667 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2668 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2669 if (NULL == (buf = heap_alloc(len+1)))
2671 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2672 return FALSE;
2674 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2675 dwParamLen ? lpszParam : "", szCRLF);
2677 TRACE("Sending (%s) len(%d)\n", buf, len);
2678 while((nBytesSent < len) && (nRC != -1))
2680 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2681 nBytesSent += nRC;
2683 heap_free(buf);
2685 if (lpfnStatusCB)
2687 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2688 &nBytesSent, sizeof(DWORD));
2691 TRACE("Sent %d bytes\n", nBytesSent);
2692 return (nRC != -1);
2695 /***********************************************************************
2696 * FTP_SendCommand (internal)
2698 * Send command to server
2700 * RETURNS
2701 * TRUE on success
2702 * NULL on failure
2705 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2706 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2708 BOOL ret;
2709 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2710 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2711 heap_free(lpszParamA);
2712 return ret;
2715 /***********************************************************************
2716 * FTP_ReceiveResponse (internal)
2718 * Receive response from server
2720 * RETURNS
2721 * Reply code on success
2722 * 0 on failure
2725 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2727 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2728 DWORD nRecv;
2729 INT rc = 0;
2730 char firstprefix[5];
2731 BOOL multiline = FALSE;
2733 TRACE("socket(%d)\n", lpwfs->sndSocket);
2735 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2737 while(1)
2739 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2740 goto lerror;
2742 if (nRecv >= 3)
2744 if(!multiline)
2746 if(lpszResponse[3] != '-')
2747 break;
2748 else
2749 { /* Start of multiline response. Loop until we get "nnn " */
2750 multiline = TRUE;
2751 memcpy(firstprefix, lpszResponse, 3);
2752 firstprefix[3] = ' ';
2753 firstprefix[4] = '\0';
2756 else
2758 if(!memcmp(firstprefix, lpszResponse, 4))
2759 break;
2764 if (nRecv >= 3)
2766 rc = atoi(lpszResponse);
2768 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2769 &nRecv, sizeof(DWORD));
2772 lerror:
2773 TRACE("return %d\n", rc);
2774 return rc;
2778 /***********************************************************************
2779 * FTP_SendPassword (internal)
2781 * Send password to ftp server
2783 * RETURNS
2784 * TRUE on success
2785 * NULL on failure
2788 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2790 INT nResCode;
2791 BOOL bSuccess = FALSE;
2793 TRACE("\n");
2794 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2795 goto lend;
2797 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2798 if (nResCode)
2800 TRACE("Received reply code %d\n", nResCode);
2801 /* Login successful... */
2802 if (nResCode == 230)
2803 bSuccess = TRUE;
2804 /* Command not implemented, superfluous at the server site... */
2805 /* Need account for login... */
2806 else if (nResCode == 332)
2807 bSuccess = FTP_SendAccount(lpwfs);
2808 else
2809 FTP_SetResponseError(nResCode);
2812 lend:
2813 TRACE("Returning %d\n", bSuccess);
2814 return bSuccess;
2818 /***********************************************************************
2819 * FTP_SendAccount (internal)
2823 * RETURNS
2824 * TRUE on success
2825 * FALSE on failure
2828 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2830 INT nResCode;
2831 BOOL bSuccess = FALSE;
2833 TRACE("\n");
2834 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2835 goto lend;
2837 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2838 if (nResCode)
2839 bSuccess = TRUE;
2840 else
2841 FTP_SetResponseError(nResCode);
2843 lend:
2844 return bSuccess;
2848 /***********************************************************************
2849 * FTP_SendStore (internal)
2851 * Send request to upload file to ftp server
2853 * RETURNS
2854 * TRUE on success
2855 * FALSE on failure
2858 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2860 INT nResCode;
2861 BOOL bSuccess = FALSE;
2863 TRACE("\n");
2864 if (!FTP_InitListenSocket(lpwfs))
2865 goto lend;
2867 if (!FTP_SendType(lpwfs, dwType))
2868 goto lend;
2870 if (!FTP_SendPortOrPasv(lpwfs))
2871 goto lend;
2873 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2874 goto lend;
2875 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2876 if (nResCode)
2878 if (nResCode == 150 || nResCode == 125)
2879 bSuccess = TRUE;
2880 else
2881 FTP_SetResponseError(nResCode);
2884 lend:
2885 if (!bSuccess && lpwfs->lstnSocket != -1)
2887 closesocket(lpwfs->lstnSocket);
2888 lpwfs->lstnSocket = -1;
2891 return bSuccess;
2895 /***********************************************************************
2896 * FTP_InitListenSocket (internal)
2898 * Create a socket to listen for server response
2900 * RETURNS
2901 * TRUE on success
2902 * FALSE on failure
2905 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2907 BOOL bSuccess = FALSE;
2908 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2910 TRACE("\n");
2912 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2913 if (lpwfs->lstnSocket == -1)
2915 TRACE("Unable to create listening socket\n");
2916 goto lend;
2919 /* We obtain our ip addr from the name of the command channel socket */
2920 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2922 /* and get the system to assign us a port */
2923 lpwfs->lstnSocketAddress.sin_port = htons(0);
2925 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2927 TRACE("Unable to bind socket\n");
2928 goto lend;
2931 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2933 TRACE("listen failed\n");
2934 goto lend;
2937 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2938 bSuccess = TRUE;
2940 lend:
2941 if (!bSuccess && lpwfs->lstnSocket != -1)
2943 closesocket(lpwfs->lstnSocket);
2944 lpwfs->lstnSocket = -1;
2947 return bSuccess;
2951 /***********************************************************************
2952 * FTP_SendType (internal)
2954 * Tell server type of data being transferred
2956 * RETURNS
2957 * TRUE on success
2958 * FALSE on failure
2960 * W98SE doesn't cache the type that's currently set
2961 * (i.e. it sends it always),
2962 * so we probably don't want to do that either.
2964 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2966 INT nResCode;
2967 WCHAR type[] = { 'I','\0' };
2968 BOOL bSuccess = FALSE;
2970 TRACE("\n");
2971 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2972 type[0] = 'A';
2974 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2975 goto lend;
2977 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2978 if (nResCode)
2980 if (nResCode == 2)
2981 bSuccess = TRUE;
2982 else
2983 FTP_SetResponseError(nResCode);
2986 lend:
2987 return bSuccess;
2991 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2992 /***********************************************************************
2993 * FTP_GetFileSize (internal)
2995 * Retrieves from the server the size of the given file
2997 * RETURNS
2998 * TRUE on success
2999 * FALSE on failure
3002 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
3004 INT nResCode;
3005 BOOL bSuccess = FALSE;
3007 TRACE("\n");
3009 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
3010 goto lend;
3012 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3013 if (nResCode)
3015 if (nResCode == 213) {
3016 /* Now parses the output to get the actual file size */
3017 int i;
3018 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3020 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3021 if (lpszResponseBuffer[i] == '\0') return FALSE;
3022 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3024 bSuccess = TRUE;
3025 } else {
3026 FTP_SetResponseError(nResCode);
3030 lend:
3031 return bSuccess;
3033 #endif
3036 /***********************************************************************
3037 * FTP_SendPort (internal)
3039 * Tell server which port to use
3041 * RETURNS
3042 * TRUE on success
3043 * FALSE on failure
3046 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3048 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3049 INT nResCode;
3050 WCHAR szIPAddress[64];
3051 BOOL bSuccess = FALSE;
3052 TRACE("\n");
3054 sprintfW(szIPAddress, szIPFormat,
3055 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
3056 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
3057 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
3058 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
3059 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3060 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3062 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3063 goto lend;
3065 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3066 if (nResCode)
3068 if (nResCode == 200)
3069 bSuccess = TRUE;
3070 else
3071 FTP_SetResponseError(nResCode);
3074 lend:
3075 return bSuccess;
3079 /***********************************************************************
3080 * FTP_DoPassive (internal)
3082 * Tell server that we want to do passive transfers
3083 * and connect data socket
3085 * RETURNS
3086 * TRUE on success
3087 * FALSE on failure
3090 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3092 INT nResCode;
3093 BOOL bSuccess = FALSE;
3095 TRACE("\n");
3096 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3097 goto lend;
3099 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3100 if (nResCode)
3102 if (nResCode == 227)
3104 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3105 LPSTR p;
3106 int f[6];
3107 int i;
3108 char *pAddr, *pPort;
3109 INT nsocket = -1;
3110 struct sockaddr_in dataSocketAddress;
3112 p = lpszResponseBuffer+4; /* skip status code */
3113 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3115 if (*p == '\0')
3117 ERR("no address found in response, aborting\n");
3118 goto lend;
3121 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3122 &f[4], &f[5]) != 6)
3124 ERR("unknown response address format '%s', aborting\n", p);
3125 goto lend;
3127 for (i=0; i < 6; i++)
3128 f[i] = f[i] & 0xff;
3130 dataSocketAddress = lpwfs->socketAddress;
3131 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3132 pPort = (char *)&(dataSocketAddress.sin_port);
3133 pAddr[0] = f[0];
3134 pAddr[1] = f[1];
3135 pAddr[2] = f[2];
3136 pAddr[3] = f[3];
3137 pPort[0] = f[4];
3138 pPort[1] = f[5];
3140 nsocket = socket(AF_INET,SOCK_STREAM,0);
3141 if (nsocket == -1)
3142 goto lend;
3144 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3146 ERR("can't connect passive FTP data port.\n");
3147 closesocket(nsocket);
3148 goto lend;
3150 lpwfs->pasvSocket = nsocket;
3151 bSuccess = TRUE;
3153 else
3154 FTP_SetResponseError(nResCode);
3157 lend:
3158 return bSuccess;
3162 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3164 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3166 if (!FTP_DoPassive(lpwfs))
3167 return FALSE;
3169 else
3171 if (!FTP_SendPort(lpwfs))
3172 return FALSE;
3174 return TRUE;
3178 /***********************************************************************
3179 * FTP_GetDataSocket (internal)
3181 * Either accepts an incoming data socket connection from the server
3182 * or just returns the already opened socket after a PASV command
3183 * in case of passive FTP.
3186 * RETURNS
3187 * TRUE on success
3188 * FALSE on failure
3191 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3193 struct sockaddr_in saddr;
3194 socklen_t addrlen = sizeof(struct sockaddr);
3196 TRACE("\n");
3197 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3199 *nDataSocket = lpwfs->pasvSocket;
3200 lpwfs->pasvSocket = -1;
3202 else
3204 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3205 closesocket(lpwfs->lstnSocket);
3206 lpwfs->lstnSocket = -1;
3208 return *nDataSocket != -1;
3212 /***********************************************************************
3213 * FTP_SendData (internal)
3215 * Send data to the server
3217 * RETURNS
3218 * TRUE on success
3219 * FALSE on failure
3222 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3224 BY_HANDLE_FILE_INFORMATION fi;
3225 DWORD nBytesRead = 0;
3226 DWORD nBytesSent = 0;
3227 DWORD nTotalSent = 0;
3228 DWORD nBytesToSend, nLen;
3229 int nRC = 1;
3230 time_t s_long_time, e_long_time;
3231 LONG nSeconds;
3232 CHAR *lpszBuffer;
3234 TRACE("\n");
3235 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3237 /* Get the size of the file. */
3238 GetFileInformationByHandle(hFile, &fi);
3239 time(&s_long_time);
3243 nBytesToSend = nBytesRead - nBytesSent;
3245 if (nBytesToSend <= 0)
3247 /* Read data from file. */
3248 nBytesSent = 0;
3249 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3250 ERR("Failed reading from file\n");
3252 if (nBytesRead > 0)
3253 nBytesToSend = nBytesRead;
3254 else
3255 break;
3258 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3259 DATA_PACKET_SIZE : nBytesToSend;
3260 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3262 if (nRC != -1)
3264 nBytesSent += nRC;
3265 nTotalSent += nRC;
3268 /* Do some computation to display the status. */
3269 time(&e_long_time);
3270 nSeconds = e_long_time - s_long_time;
3271 if( nSeconds / 60 > 0 )
3273 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3274 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3275 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3277 else
3279 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3280 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3281 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3283 } while (nRC != -1);
3285 TRACE("file transfer complete!\n");
3287 heap_free(lpszBuffer);
3288 return nTotalSent;
3292 /***********************************************************************
3293 * FTP_SendRetrieve (internal)
3295 * Send request to retrieve a file
3297 * RETURNS
3298 * Number of bytes to be received on success
3299 * 0 on failure
3302 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3304 INT nResCode;
3305 BOOL ret;
3307 TRACE("\n");
3308 if (!(ret = FTP_InitListenSocket(lpwfs)))
3309 goto lend;
3311 if (!(ret = FTP_SendType(lpwfs, dwType)))
3312 goto lend;
3314 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3315 goto lend;
3317 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3318 goto lend;
3320 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3321 if ((nResCode != 125) && (nResCode != 150)) {
3322 /* That means that we got an error getting the file. */
3323 FTP_SetResponseError(nResCode);
3324 ret = FALSE;
3327 lend:
3328 if (!ret && lpwfs->lstnSocket != -1)
3330 closesocket(lpwfs->lstnSocket);
3331 lpwfs->lstnSocket = -1;
3334 return ret;
3338 /***********************************************************************
3339 * FTP_RetrieveData (internal)
3341 * Retrieve data from server
3343 * RETURNS
3344 * TRUE on success
3345 * FALSE on failure
3348 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3350 DWORD nBytesWritten;
3351 INT nRC = 0;
3352 CHAR *lpszBuffer;
3354 TRACE("\n");
3356 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3357 if (NULL == lpszBuffer)
3359 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3360 return FALSE;
3363 while (nRC != -1)
3365 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3366 if (nRC != -1)
3368 /* other side closed socket. */
3369 if (nRC == 0)
3370 goto recv_end;
3371 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3375 TRACE("Data transfer complete\n");
3377 recv_end:
3378 heap_free(lpszBuffer);
3379 return (nRC != -1);
3382 /***********************************************************************
3383 * FTPFINDNEXT_Destroy (internal)
3385 * Deallocate session handle
3387 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3389 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3390 DWORD i;
3392 TRACE("\n");
3394 WININET_Release(&lpwfn->lpFtpSession->hdr);
3396 for (i = 0; i < lpwfn->size; i++)
3398 heap_free(lpwfn->lpafp[i].lpszName);
3400 heap_free(lpwfn->lpafp);
3403 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3405 WIN32_FIND_DATAW *find_data = data;
3406 DWORD res = ERROR_SUCCESS;
3408 TRACE("index(%d) size(%d)\n", find->index, find->size);
3410 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3412 if (find->index < find->size) {
3413 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3414 find->index++;
3416 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3417 }else {
3418 res = ERROR_NO_MORE_FILES;
3421 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3423 INTERNET_ASYNC_RESULT iar;
3425 iar.dwResult = (res == ERROR_SUCCESS);
3426 iar.dwError = res;
3428 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3429 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3430 sizeof(INTERNET_ASYNC_RESULT));
3433 return res;
3436 typedef struct {
3437 task_header_t hdr;
3438 WIN32_FIND_DATAW *find_data;
3439 } find_next_task_t;
3441 static void FTPFINDNEXT_AsyncFindNextFileProc(task_header_t *hdr)
3443 find_next_task_t *task = (find_next_task_t*)hdr;
3445 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)task->hdr.hdr, task->find_data);
3448 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3450 switch(option) {
3451 case INTERNET_OPTION_HANDLE_TYPE:
3452 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3454 if (*size < sizeof(ULONG))
3455 return ERROR_INSUFFICIENT_BUFFER;
3457 *size = sizeof(DWORD);
3458 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3459 return ERROR_SUCCESS;
3462 return INET_QueryOption(hdr, option, buffer, size, unicode);
3465 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3467 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3469 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3471 find_next_task_t *task;
3473 task = alloc_async_task(&find->hdr, FTPFINDNEXT_AsyncFindNextFileProc, sizeof(*task));
3474 task->find_data = data;
3476 INTERNET_AsyncCall(&task->hdr);
3477 return ERROR_SUCCESS;
3480 return FTPFINDNEXT_FindNextFileProc(find, data);
3483 static const object_vtbl_t FTPFINDNEXTVtbl = {
3484 FTPFINDNEXT_Destroy,
3485 NULL,
3486 FTPFINDNEXT_QueryOption,
3487 INET_SetOption,
3488 NULL,
3489 NULL,
3490 NULL,
3491 NULL,
3492 FTPFINDNEXT_FindNextFileW
3495 /***********************************************************************
3496 * FTP_ReceiveFileList (internal)
3498 * Read file list from server
3500 * RETURNS
3501 * Handle to file list on success
3502 * NULL on failure
3505 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3506 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3508 DWORD dwSize = 0;
3509 LPFILEPROPERTIESW lpafp = NULL;
3510 LPWININETFTPFINDNEXTW lpwfn = NULL;
3512 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3514 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3516 if(lpFindFileData)
3517 FTP_ConvertFileProp(lpafp, lpFindFileData);
3519 lpwfn = alloc_object(&lpwfs->hdr, &FTPFINDNEXTVtbl, sizeof(WININETFTPFINDNEXTW));
3520 if (lpwfn)
3522 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3523 lpwfn->hdr.dwContext = dwContext;
3524 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3525 lpwfn->size = dwSize;
3526 lpwfn->lpafp = lpafp;
3528 WININET_AddRef( &lpwfs->hdr );
3529 lpwfn->lpFtpSession = lpwfs;
3530 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3534 TRACE("Matched %d files\n", dwSize);
3535 return lpwfn ? lpwfn->hdr.hInternet : NULL;
3539 /***********************************************************************
3540 * FTP_ConvertFileProp (internal)
3542 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3544 * RETURNS
3545 * TRUE on success
3546 * FALSE on failure
3549 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3551 BOOL bSuccess = FALSE;
3553 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3555 if (lpafp)
3557 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3558 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3559 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3561 /* Not all fields are filled in */
3562 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3563 lpFindFileData->nFileSizeLow = lpafp->nSize;
3565 if (lpafp->bIsDirectory)
3566 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3568 if (lpafp->lpszName)
3569 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3571 bSuccess = TRUE;
3574 return bSuccess;
3577 /***********************************************************************
3578 * FTP_ParseNextFile (internal)
3580 * Parse the next line in file listing
3582 * RETURNS
3583 * TRUE on success
3584 * FALSE on failure
3586 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3588 static const char szSpace[] = " \t";
3589 DWORD nBufLen;
3590 char *pszLine;
3591 char *pszToken;
3592 char *pszTmp;
3593 BOOL found = FALSE;
3594 int i;
3596 lpfp->lpszName = NULL;
3597 do {
3598 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3599 return FALSE;
3601 pszToken = strtok(pszLine, szSpace);
3602 /* ls format
3603 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3605 * For instance:
3606 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3608 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3609 if(!FTP_ParsePermission(pszToken, lpfp))
3610 lpfp->bIsDirectory = FALSE;
3611 for(i=0; i<=3; i++) {
3612 if(!(pszToken = strtok(NULL, szSpace)))
3613 break;
3615 if(!pszToken) continue;
3616 if(lpfp->bIsDirectory) {
3617 TRACE("Is directory\n");
3618 lpfp->nSize = 0;
3620 else {
3621 TRACE("Size: %s\n", pszToken);
3622 lpfp->nSize = atol(pszToken);
3625 lpfp->tmLastModified.wSecond = 0;
3626 lpfp->tmLastModified.wMinute = 0;
3627 lpfp->tmLastModified.wHour = 0;
3628 lpfp->tmLastModified.wDay = 0;
3629 lpfp->tmLastModified.wMonth = 0;
3630 lpfp->tmLastModified.wYear = 0;
3632 /* Determine month */
3633 pszToken = strtok(NULL, szSpace);
3634 if(!pszToken) continue;
3635 if(strlen(pszToken) >= 3) {
3636 pszToken[3] = 0;
3637 if((pszTmp = StrStrIA(szMonths, pszToken)))
3638 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3640 /* Determine day */
3641 pszToken = strtok(NULL, szSpace);
3642 if(!pszToken) continue;
3643 lpfp->tmLastModified.wDay = atoi(pszToken);
3644 /* Determine time or year */
3645 pszToken = strtok(NULL, szSpace);
3646 if(!pszToken) continue;
3647 if((pszTmp = strchr(pszToken, ':'))) {
3648 SYSTEMTIME curr_time;
3649 *pszTmp = 0;
3650 pszTmp++;
3651 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3652 lpfp->tmLastModified.wHour = atoi(pszToken);
3653 GetLocalTime( &curr_time );
3654 lpfp->tmLastModified.wYear = curr_time.wYear;
3656 else {
3657 lpfp->tmLastModified.wYear = atoi(pszToken);
3658 lpfp->tmLastModified.wHour = 12;
3660 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3661 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3662 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3664 pszToken = strtok(NULL, szSpace);
3665 if(!pszToken) continue;
3666 lpfp->lpszName = heap_strdupAtoW(pszToken);
3667 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3669 /* NT way of parsing ... :
3671 07-13-03 08:55PM <DIR> sakpatch
3672 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3674 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3675 int mon, mday, year, hour, min;
3676 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3678 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3679 lpfp->tmLastModified.wDay = mday;
3680 lpfp->tmLastModified.wMonth = mon;
3681 lpfp->tmLastModified.wYear = year;
3683 /* Hacky and bad Y2K protection :-) */
3684 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3686 pszToken = strtok(NULL, szSpace);
3687 if(!pszToken) continue;
3688 sscanf(pszToken, "%d:%d", &hour, &min);
3689 lpfp->tmLastModified.wHour = hour;
3690 lpfp->tmLastModified.wMinute = min;
3691 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3692 lpfp->tmLastModified.wHour += 12;
3694 lpfp->tmLastModified.wSecond = 0;
3696 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3697 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3698 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3700 pszToken = strtok(NULL, szSpace);
3701 if(!pszToken) continue;
3702 if(!strcasecmp(pszToken, "<DIR>")) {
3703 lpfp->bIsDirectory = TRUE;
3704 lpfp->nSize = 0;
3705 TRACE("Is directory\n");
3707 else {
3708 lpfp->bIsDirectory = FALSE;
3709 lpfp->nSize = atol(pszToken);
3710 TRACE("Size: %d\n", lpfp->nSize);
3713 pszToken = strtok(NULL, szSpace);
3714 if(!pszToken) continue;
3715 lpfp->lpszName = heap_strdupAtoW(pszToken);
3716 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3718 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3719 else if(pszToken[0] == '+') {
3720 FIXME("EPLF Format not implemented\n");
3723 if(lpfp->lpszName) {
3724 if((lpszSearchFile == NULL) ||
3725 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3726 found = TRUE;
3727 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3729 else {
3730 heap_free(lpfp->lpszName);
3731 lpfp->lpszName = NULL;
3734 } while(!found);
3735 return TRUE;
3738 /***********************************************************************
3739 * FTP_ParseDirectory (internal)
3741 * Parse string of directory information
3743 * RETURNS
3744 * TRUE on success
3745 * FALSE on failure
3747 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3748 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3750 BOOL bSuccess = TRUE;
3751 INT sizeFilePropArray = 500;/*20; */
3752 INT indexFilePropArray = -1;
3754 TRACE("\n");
3756 /* Allocate initial file properties array */
3757 *lpafp = heap_alloc_zero(sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3758 if (!*lpafp)
3759 return FALSE;
3761 do {
3762 if (indexFilePropArray+1 >= sizeFilePropArray)
3764 LPFILEPROPERTIESW tmpafp;
3766 sizeFilePropArray *= 2;
3767 tmpafp = heap_realloc_zero(*lpafp, sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3768 if (NULL == tmpafp)
3770 bSuccess = FALSE;
3771 break;
3774 *lpafp = tmpafp;
3776 indexFilePropArray++;
3777 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3779 if (bSuccess && indexFilePropArray)
3781 if (indexFilePropArray < sizeFilePropArray - 1)
3783 LPFILEPROPERTIESW tmpafp;
3785 tmpafp = heap_realloc(*lpafp, sizeof(FILEPROPERTIESW)*indexFilePropArray);
3786 if (NULL != tmpafp)
3787 *lpafp = tmpafp;
3789 *dwfp = indexFilePropArray;
3791 else
3793 heap_free(*lpafp);
3794 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3795 bSuccess = FALSE;
3798 return bSuccess;
3802 /***********************************************************************
3803 * FTP_ParsePermission (internal)
3805 * Parse permission string of directory information
3807 * RETURNS
3808 * TRUE on success
3809 * FALSE on failure
3812 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3814 BOOL bSuccess = TRUE;
3815 unsigned short nPermission = 0;
3816 INT nPos = 1;
3817 INT nLast = 9;
3819 TRACE("\n");
3820 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3822 bSuccess = FALSE;
3823 return bSuccess;
3826 lpfp->bIsDirectory = (*lpszPermission == 'd');
3829 switch (nPos)
3831 case 1:
3832 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3833 break;
3834 case 2:
3835 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3836 break;
3837 case 3:
3838 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3839 break;
3840 case 4:
3841 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3842 break;
3843 case 5:
3844 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3845 break;
3846 case 6:
3847 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3848 break;
3849 case 7:
3850 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3851 break;
3852 case 8:
3853 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3854 break;
3855 case 9:
3856 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3857 break;
3859 nPos++;
3860 }while (nPos <= nLast);
3862 lpfp->permissions = nPermission;
3863 return bSuccess;
3867 /***********************************************************************
3868 * FTP_SetResponseError (internal)
3870 * Set the appropriate error code for a given response from the server
3872 * RETURNS
3875 static DWORD FTP_SetResponseError(DWORD dwResponse)
3877 DWORD dwCode = 0;
3879 switch(dwResponse)
3881 case 425: /* Cannot open data connection. */
3882 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3883 break;
3885 case 426: /* Connection closed, transer aborted. */
3886 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3887 break;
3889 case 530: /* Not logged in. Login incorrect. */
3890 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3891 break;
3893 case 421: /* Service not available - Server may be shutting down. */
3894 case 450: /* File action not taken. File may be busy. */
3895 case 451: /* Action aborted. Server error. */
3896 case 452: /* Action not taken. Insufficient storage space on server. */
3897 case 500: /* Syntax error. Command unrecognized. */
3898 case 501: /* Syntax error. Error in parameters or arguments. */
3899 case 502: /* Command not implemented. */
3900 case 503: /* Bad sequence of commands. */
3901 case 504: /* Command not implemented for that parameter. */
3902 case 532: /* Need account for storing files */
3903 case 550: /* File action not taken. File not found or no access. */
3904 case 551: /* Requested action aborted. Page type unknown */
3905 case 552: /* Action aborted. Exceeded storage allocation */
3906 case 553: /* Action not taken. File name not allowed. */
3908 default:
3909 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3910 break;
3913 INTERNET_SetLastError(dwCode);
3914 return dwCode;