d3d8: Get rid of the format switching code in d3d8_device_CopyRects().
[wine.git] / dlls / wininet / ftp.c
blob855aee071a18d41e438ce05b6c828e659e5b1540
1 /*
2 * WININET - Ftp implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2004 Mike McCormack for CodeWeavers
6 * Copyright 2004 Kevin Koltzau
7 * Copyright 2007 Hans Leidekker
9 * Ulrich Czekalla
10 * Noureddine Jemmali
12 * Copyright 2000 Andreas Mohr
13 * Copyright 2002 Jaco Greeff
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "config.h"
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
34 #include <ws2tcpip.h>
35 #endif
37 #include <errno.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/types.h>
43 #ifdef HAVE_SYS_SOCKET_H
44 # include <sys/socket.h>
45 #endif
46 #ifdef HAVE_ARPA_INET_H
47 # include <arpa/inet.h>
48 #endif
49 #ifdef HAVE_UNISTD_H
50 # include <unistd.h>
51 #endif
52 #ifdef HAVE_SYS_IOCTL_H
53 # include <sys/ioctl.h>
54 #endif
55 #include <time.h>
56 #include <assert.h>
58 #include "windef.h"
59 #include "winbase.h"
60 #include "wingdi.h"
61 #include "winuser.h"
62 #include "wininet.h"
63 #include "winnls.h"
64 #include "winerror.h"
65 #include "winreg.h"
66 #include "winternl.h"
67 #include "shlwapi.h"
69 #include "wine/debug.h"
70 #include "internet.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
74 typedef struct _ftp_session_t ftp_session_t;
76 typedef struct
78 object_header_t hdr;
79 ftp_session_t *lpFtpSession;
80 BOOL session_deleted;
81 int nDataSocket;
82 WCHAR *cache_file;
83 HANDLE cache_file_handle;
84 } ftp_file_t;
86 struct _ftp_session_t
88 object_header_t hdr;
89 appinfo_t *lpAppInfo;
90 int sndSocket;
91 int lstnSocket;
92 int pasvSocket; /* data socket connected by us in case of passive FTP */
93 ftp_file_t *download_in_progress;
94 struct sockaddr_in socketAddress;
95 struct sockaddr_in lstnSocketAddress;
96 LPWSTR servername;
97 INTERNET_PORT serverport;
98 LPWSTR lpszPassword;
99 LPWSTR lpszUserName;
102 typedef struct
104 BOOL bIsDirectory;
105 LPWSTR lpszName;
106 DWORD nSize;
107 SYSTEMTIME tmLastModified;
108 unsigned short permissions;
109 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
111 typedef struct
113 object_header_t hdr;
114 ftp_session_t *lpFtpSession;
115 DWORD index;
116 DWORD size;
117 LPFILEPROPERTIESW lpafp;
118 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
120 #define DATA_PACKET_SIZE 0x2000
121 #define szCRLF "\r\n"
122 #define MAX_BACKLOG 5
124 /* Testing shows that Windows only accepts dwFlags where the last
125 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
127 #define FTP_CONDITION_MASK 0x0007
129 typedef enum {
130 /* FTP commands with arguments. */
131 FTP_CMD_ACCT,
132 FTP_CMD_CWD,
133 FTP_CMD_DELE,
134 FTP_CMD_MKD,
135 FTP_CMD_PASS,
136 FTP_CMD_PORT,
137 FTP_CMD_RETR,
138 FTP_CMD_RMD,
139 FTP_CMD_RNFR,
140 FTP_CMD_RNTO,
141 FTP_CMD_STOR,
142 FTP_CMD_TYPE,
143 FTP_CMD_USER,
144 FTP_CMD_SIZE,
146 /* FTP commands without arguments. */
147 FTP_CMD_ABOR,
148 FTP_CMD_LIST,
149 FTP_CMD_NLST,
150 FTP_CMD_PASV,
151 FTP_CMD_PWD,
152 FTP_CMD_QUIT,
153 } FTP_COMMAND;
155 static const CHAR *const szFtpCommands[] = {
156 "ACCT",
157 "CWD",
158 "DELE",
159 "MKD",
160 "PASS",
161 "PORT",
162 "RETR",
163 "RMD",
164 "RNFR",
165 "RNTO",
166 "STOR",
167 "TYPE",
168 "USER",
169 "SIZE",
170 "ABOR",
171 "LIST",
172 "NLST",
173 "PASV",
174 "PWD",
175 "QUIT",
178 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
179 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
181 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
182 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext);
183 static BOOL FTP_SendStore(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
184 static BOOL FTP_GetDataSocket(ftp_session_t*, LPINT nDataSocket);
185 static BOOL FTP_SendData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
186 static INT FTP_ReceiveResponse(ftp_session_t*, DWORD_PTR dwContext);
187 static BOOL FTP_SendRetrieve(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
188 static BOOL FTP_RetrieveFileData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
189 static BOOL FTP_InitListenSocket(ftp_session_t*);
190 static BOOL FTP_ConnectToHost(ftp_session_t*);
191 static BOOL FTP_SendPassword(ftp_session_t*);
192 static BOOL FTP_SendAccount(ftp_session_t*);
193 static BOOL FTP_SendType(ftp_session_t*, DWORD dwType);
194 static BOOL FTP_SendPort(ftp_session_t*);
195 static BOOL FTP_DoPassive(ftp_session_t*);
196 static BOOL FTP_SendPortOrPasv(ftp_session_t*);
197 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
198 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
199 static BOOL FTP_ParseDirectory(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
200 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
201 static HINTERNET FTP_ReceiveFileList(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
202 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
203 static DWORD FTP_SetResponseError(DWORD dwResponse);
204 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
205 static BOOL FTP_FtpPutFileW(ftp_session_t*, LPCWSTR lpszLocalFile,
206 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
207 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
208 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
209 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t*,
210 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
211 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t*, LPWSTR lpszCurrentDirectory,
212 LPDWORD lpdwCurrentDirectory);
213 static BOOL FTP_FtpRenameFileW(ftp_session_t*, LPCWSTR lpszSrc, LPCWSTR lpszDest);
214 static BOOL FTP_FtpRemoveDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
215 static BOOL FTP_FtpDeleteFileW(ftp_session_t*, LPCWSTR lpszFileName);
216 static BOOL FTP_FtpGetFileW(ftp_session_t*, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
217 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
218 DWORD_PTR dwContext);
220 /* A temporary helper until we get rid of INTERNET_GetLastError calls */
221 static BOOL res_to_le(DWORD res)
223 if(res != ERROR_SUCCESS)
224 INTERNET_SetLastError(res);
225 return res == ERROR_SUCCESS;
228 /***********************************************************************
229 * FtpPutFileA (WININET.@)
231 * Uploads a file to the FTP server
233 * RETURNS
234 * TRUE on success
235 * FALSE on failure
238 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
239 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
241 LPWSTR lpwzLocalFile;
242 LPWSTR lpwzNewRemoteFile;
243 BOOL ret;
245 lpwzLocalFile = heap_strdupAtoW(lpszLocalFile);
246 lpwzNewRemoteFile = heap_strdupAtoW(lpszNewRemoteFile);
247 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
248 dwFlags, dwContext);
249 heap_free(lpwzLocalFile);
250 heap_free(lpwzNewRemoteFile);
251 return ret;
254 typedef struct {
255 task_header_t hdr;
256 WCHAR *local_file;
257 WCHAR *remote_file;
258 DWORD flags;
259 DWORD_PTR context;
260 } put_file_task_t;
262 static void AsyncFtpPutFileProc(task_header_t *hdr)
264 put_file_task_t *task = (put_file_task_t*)hdr;
265 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
267 TRACE("%p\n", session);
269 FTP_FtpPutFileW(session, task->local_file, task->remote_file,
270 task->flags, task->context);
272 heap_free(task->local_file);
273 heap_free(task->remote_file);
276 /***********************************************************************
277 * FtpPutFileW (WININET.@)
279 * Uploads a file to the FTP server
281 * RETURNS
282 * TRUE on success
283 * FALSE on failure
286 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
287 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
289 ftp_session_t *lpwfs;
290 appinfo_t *hIC = NULL;
291 BOOL r = FALSE;
293 if (!lpszLocalFile || !lpszNewRemoteFile)
295 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
296 return FALSE;
299 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
300 if (!lpwfs)
302 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
303 return FALSE;
306 if (WH_HFTPSESSION != lpwfs->hdr.htype)
308 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
309 goto lend;
312 if (lpwfs->download_in_progress != NULL)
314 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
315 goto lend;
318 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
320 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
321 goto lend;
324 hIC = lpwfs->lpAppInfo;
325 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
327 put_file_task_t *task = alloc_async_task(&lpwfs->hdr, AsyncFtpPutFileProc, sizeof(*task));
329 task->local_file = heap_strdupW(lpszLocalFile);
330 task->remote_file = heap_strdupW(lpszNewRemoteFile);
331 task->flags = dwFlags;
332 task->context = dwContext;
334 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
336 else
338 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
339 lpszNewRemoteFile, dwFlags, dwContext);
342 lend:
343 WININET_Release( &lpwfs->hdr );
345 return r;
348 /***********************************************************************
349 * FTP_FtpPutFileW (Internal)
351 * Uploads a file to the FTP server
353 * RETURNS
354 * TRUE on success
355 * FALSE on failure
358 static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
359 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
361 HANDLE hFile;
362 BOOL bSuccess = FALSE;
363 appinfo_t *hIC = NULL;
364 INT nResCode;
366 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
368 /* Clear any error information */
369 INTERNET_SetLastError(0);
371 /* Open file to be uploaded */
372 if (INVALID_HANDLE_VALUE ==
373 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
374 /* Let CreateFile set the appropriate error */
375 return FALSE;
377 hIC = lpwfs->lpAppInfo;
379 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
381 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
383 INT nDataSocket;
385 /* Get data socket to server */
386 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
388 FTP_SendData(lpwfs, nDataSocket, hFile);
389 closesocket(nDataSocket);
390 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
391 if (nResCode)
393 if (nResCode == 226)
394 bSuccess = TRUE;
395 else
396 FTP_SetResponseError(nResCode);
401 if (lpwfs->lstnSocket != -1)
403 closesocket(lpwfs->lstnSocket);
404 lpwfs->lstnSocket = -1;
407 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
409 INTERNET_ASYNC_RESULT iar;
411 iar.dwResult = (DWORD)bSuccess;
412 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
413 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
414 &iar, sizeof(INTERNET_ASYNC_RESULT));
417 CloseHandle(hFile);
419 return bSuccess;
423 /***********************************************************************
424 * FtpSetCurrentDirectoryA (WININET.@)
426 * Change the working directory on the FTP server
428 * RETURNS
429 * TRUE on success
430 * FALSE on failure
433 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
435 LPWSTR lpwzDirectory;
436 BOOL ret;
438 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
439 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
440 heap_free(lpwzDirectory);
441 return ret;
444 typedef struct {
445 task_header_t hdr;
446 WCHAR *directory;
447 } directory_task_t;
449 static void AsyncFtpSetCurrentDirectoryProc(task_header_t *hdr)
451 directory_task_t *task = (directory_task_t*)hdr;
452 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
454 TRACE("%p\n", session);
456 FTP_FtpSetCurrentDirectoryW(session, task->directory);
457 heap_free(task->directory);
460 /***********************************************************************
461 * FtpSetCurrentDirectoryW (WININET.@)
463 * Change the working directory on the FTP server
465 * RETURNS
466 * TRUE on success
467 * FALSE on failure
470 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
472 ftp_session_t *lpwfs = NULL;
473 appinfo_t *hIC = NULL;
474 BOOL r = FALSE;
476 if (!lpszDirectory)
478 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
479 goto lend;
482 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
483 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
485 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
486 goto lend;
489 if (lpwfs->download_in_progress != NULL)
491 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
492 goto lend;
495 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
497 hIC = lpwfs->lpAppInfo;
498 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
500 directory_task_t *task;
502 task = alloc_async_task(&lpwfs->hdr, AsyncFtpSetCurrentDirectoryProc, sizeof(*task));
503 task->directory = heap_strdupW(lpszDirectory);
505 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
507 else
509 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
512 lend:
513 if( lpwfs )
514 WININET_Release( &lpwfs->hdr );
516 return r;
520 /***********************************************************************
521 * FTP_FtpSetCurrentDirectoryW (Internal)
523 * Change the working directory on the FTP server
525 * RETURNS
526 * TRUE on success
527 * FALSE on failure
530 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
532 INT nResCode;
533 appinfo_t *hIC = NULL;
534 BOOL bSuccess = FALSE;
536 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
538 /* Clear any error information */
539 INTERNET_SetLastError(0);
541 hIC = lpwfs->lpAppInfo;
542 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
543 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
544 goto lend;
546 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
548 if (nResCode)
550 if (nResCode == 250)
551 bSuccess = TRUE;
552 else
553 FTP_SetResponseError(nResCode);
556 lend:
557 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
559 INTERNET_ASYNC_RESULT iar;
561 iar.dwResult = bSuccess;
562 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
563 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
564 &iar, sizeof(INTERNET_ASYNC_RESULT));
566 return bSuccess;
570 /***********************************************************************
571 * FtpCreateDirectoryA (WININET.@)
573 * Create new directory on the FTP server
575 * RETURNS
576 * TRUE on success
577 * FALSE on failure
580 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
582 LPWSTR lpwzDirectory;
583 BOOL ret;
585 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
586 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
587 heap_free(lpwzDirectory);
588 return ret;
592 static void AsyncFtpCreateDirectoryProc(task_header_t *hdr)
594 directory_task_t *task = (directory_task_t*)hdr;
595 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
597 TRACE(" %p\n", session);
599 FTP_FtpCreateDirectoryW(session, task->directory);
600 heap_free(task->directory);
603 /***********************************************************************
604 * FtpCreateDirectoryW (WININET.@)
606 * Create new directory on the FTP server
608 * RETURNS
609 * TRUE on success
610 * FALSE on failure
613 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
615 ftp_session_t *lpwfs;
616 appinfo_t *hIC = NULL;
617 BOOL r = FALSE;
619 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
620 if (!lpwfs)
622 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
623 return FALSE;
626 if (WH_HFTPSESSION != lpwfs->hdr.htype)
628 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
629 goto lend;
632 if (lpwfs->download_in_progress != NULL)
634 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
635 goto lend;
638 if (!lpszDirectory)
640 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
641 goto lend;
644 hIC = lpwfs->lpAppInfo;
645 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
647 directory_task_t *task;
649 task = alloc_async_task(&lpwfs->hdr, AsyncFtpCreateDirectoryProc, sizeof(*task));
650 task->directory = heap_strdupW(lpszDirectory);
652 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
654 else
656 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
658 lend:
659 WININET_Release( &lpwfs->hdr );
661 return r;
665 /***********************************************************************
666 * FTP_FtpCreateDirectoryW (Internal)
668 * Create new directory on the FTP server
670 * RETURNS
671 * TRUE on success
672 * FALSE on failure
675 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
677 INT nResCode;
678 BOOL bSuccess = FALSE;
679 appinfo_t *hIC = NULL;
681 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
683 /* Clear any error information */
684 INTERNET_SetLastError(0);
686 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
687 goto lend;
689 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
690 if (nResCode)
692 if (nResCode == 257)
693 bSuccess = TRUE;
694 else
695 FTP_SetResponseError(nResCode);
698 lend:
699 hIC = lpwfs->lpAppInfo;
700 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
702 INTERNET_ASYNC_RESULT iar;
704 iar.dwResult = (DWORD)bSuccess;
705 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
706 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
707 &iar, sizeof(INTERNET_ASYNC_RESULT));
710 return bSuccess;
713 /***********************************************************************
714 * FtpFindFirstFileA (WININET.@)
716 * Search the specified directory
718 * RETURNS
719 * HINTERNET on success
720 * NULL on failure
723 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
724 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
726 LPWSTR lpwzSearchFile;
727 WIN32_FIND_DATAW wfd;
728 LPWIN32_FIND_DATAW lpFindFileDataW;
729 HINTERNET ret;
731 lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
732 lpFindFileDataW = lpFindFileData?&wfd:NULL;
733 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
734 heap_free(lpwzSearchFile);
736 if (ret && lpFindFileData)
737 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
739 return ret;
742 typedef struct {
743 task_header_t hdr;
744 WCHAR *search_file;
745 WIN32_FIND_DATAW *find_file_data;
746 DWORD flags;
747 DWORD_PTR context;
748 } find_first_file_task_t;
750 static void AsyncFtpFindFirstFileProc(task_header_t *hdr)
752 find_first_file_task_t *task = (find_first_file_task_t*)hdr;
753 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
755 TRACE("%p\n", session);
757 FTP_FtpFindFirstFileW(session, task->search_file, task->find_file_data, task->flags, task->context);
758 heap_free(task->search_file);
761 /***********************************************************************
762 * FtpFindFirstFileW (WININET.@)
764 * Search the specified directory
766 * RETURNS
767 * HINTERNET on success
768 * NULL on failure
771 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
772 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
774 ftp_session_t *lpwfs;
775 appinfo_t *hIC = NULL;
776 HINTERNET r = NULL;
778 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
779 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
781 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
782 goto lend;
785 if (lpwfs->download_in_progress != NULL)
787 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
788 goto lend;
791 hIC = lpwfs->lpAppInfo;
792 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
794 find_first_file_task_t *task;
796 task = alloc_async_task(&lpwfs->hdr, AsyncFtpFindFirstFileProc, sizeof(*task));
797 task->search_file = heap_strdupW(lpszSearchFile);
798 task->find_file_data = lpFindFileData;
799 task->flags = dwFlags;
800 task->context = dwContext;
802 INTERNET_AsyncCall(&task->hdr);
803 r = NULL;
805 else
807 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
808 dwFlags, dwContext);
810 lend:
811 if( lpwfs )
812 WININET_Release( &lpwfs->hdr );
814 return r;
818 /***********************************************************************
819 * FTP_FtpFindFirstFileW (Internal)
821 * Search the specified directory
823 * RETURNS
824 * HINTERNET on success
825 * NULL on failure
828 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
829 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
831 INT nResCode;
832 appinfo_t *hIC = NULL;
833 HINTERNET hFindNext = NULL;
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 BOOL 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 = sock_recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1199 *read = res>0 ? res : 0;
1201 error = res >= 0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME */
1202 if (error == ERROR_SUCCESS && file->cache_file)
1204 DWORD bytes_written;
1206 if (!WriteFile(file->cache_file_handle, buffer, *read, &bytes_written, NULL))
1207 WARN("WriteFile failed: %u\n", GetLastError());
1209 return error;
1212 static DWORD FTPFILE_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 = sock_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 = sock_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(task_header_t *task)
1253 ftp_file_t *file = (ftp_file_t*)task->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 = sock_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 return ERROR_IO_PENDING;
1291 return ERROR_SUCCESS;
1294 static DWORD FTPFILE_LockRequestFile(object_header_t *hdr, req_file_t **ret)
1296 ftp_file_t *file = (ftp_file_t*)hdr;
1297 FIXME("%p\n", file);
1298 return ERROR_NOT_SUPPORTED;
1301 static const object_vtbl_t FTPFILEVtbl = {
1302 FTPFILE_Destroy,
1303 NULL,
1304 FTPFILE_QueryOption,
1305 INET_SetOption,
1306 FTPFILE_ReadFile,
1307 FTPFILE_ReadFileEx,
1308 FTPFILE_WriteFile,
1309 FTPFILE_QueryDataAvailable,
1310 NULL,
1311 FTPFILE_LockRequestFile
1314 /***********************************************************************
1315 * FTP_FtpOpenFileW (Internal)
1317 * Open a remote file for writing or reading
1319 * RETURNS
1320 * HINTERNET handle on success
1321 * NULL on failure
1324 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1325 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1326 DWORD_PTR dwContext)
1328 INT nDataSocket;
1329 BOOL bSuccess = FALSE;
1330 ftp_file_t *lpwh = NULL;
1331 appinfo_t *hIC = NULL;
1333 TRACE("\n");
1335 /* Clear any error information */
1336 INTERNET_SetLastError(0);
1338 if (GENERIC_READ == fdwAccess)
1340 /* Set up socket to retrieve data */
1341 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1343 else if (GENERIC_WRITE == fdwAccess)
1345 /* Set up socket to send data */
1346 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1349 /* Get data socket to server */
1350 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1352 lpwh = alloc_object(&lpwfs->hdr, &FTPFILEVtbl, sizeof(ftp_file_t));
1353 lpwh->hdr.htype = WH_HFILE;
1354 lpwh->hdr.dwFlags = dwFlags;
1355 lpwh->hdr.dwContext = dwContext;
1356 lpwh->nDataSocket = nDataSocket;
1357 lpwh->cache_file = NULL;
1358 lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1359 lpwh->session_deleted = FALSE;
1361 WININET_AddRef( &lpwfs->hdr );
1362 lpwh->lpFtpSession = lpwfs;
1363 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1365 /* Indicate that a download is currently in progress */
1366 lpwfs->download_in_progress = lpwh;
1369 if (lpwfs->lstnSocket != -1)
1371 closesocket(lpwfs->lstnSocket);
1372 lpwfs->lstnSocket = -1;
1375 if (bSuccess && fdwAccess == GENERIC_READ)
1377 WCHAR filename[MAX_PATH + 1];
1378 URL_COMPONENTSW uc;
1379 DWORD len;
1381 memset(&uc, 0, sizeof(uc));
1382 uc.dwStructSize = sizeof(uc);
1383 uc.nScheme = INTERNET_SCHEME_FTP;
1384 uc.lpszHostName = lpwfs->servername;
1385 uc.nPort = lpwfs->serverport;
1386 uc.lpszUserName = lpwfs->lpszUserName;
1387 uc.lpszUrlPath = heap_strdupW(lpszFileName);
1389 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1391 WCHAR *url = heap_alloc(len * sizeof(WCHAR));
1393 if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1395 lpwh->cache_file = heap_strdupW(filename);
1396 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1397 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1398 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1400 WARN("Could not create cache file: %u\n", GetLastError());
1401 heap_free(lpwh->cache_file);
1402 lpwh->cache_file = NULL;
1405 heap_free(url);
1407 heap_free(uc.lpszUrlPath);
1410 hIC = lpwfs->lpAppInfo;
1411 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1413 INTERNET_ASYNC_RESULT iar;
1415 if (lpwh)
1417 iar.dwResult = (DWORD_PTR)lpwh->hdr.hInternet;
1418 iar.dwError = ERROR_SUCCESS;
1419 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1420 &iar, sizeof(INTERNET_ASYNC_RESULT));
1423 if(bSuccess) {
1424 FTP_ReceiveRequestData(lpwh, TRUE);
1425 }else {
1426 iar.dwResult = 0;
1427 iar.dwError = INTERNET_GetLastError();
1428 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1429 &iar, sizeof(INTERNET_ASYNC_RESULT));
1433 if(!bSuccess)
1434 return FALSE;
1436 return lpwh->hdr.hInternet;
1440 /***********************************************************************
1441 * FtpOpenFileA (WININET.@)
1443 * Open a remote file for writing or reading
1445 * RETURNS
1446 * HINTERNET handle on success
1447 * NULL on failure
1450 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1451 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1452 DWORD_PTR dwContext)
1454 LPWSTR lpwzFileName;
1455 HINTERNET ret;
1457 lpwzFileName = heap_strdupAtoW(lpszFileName);
1458 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1459 heap_free(lpwzFileName);
1460 return ret;
1463 typedef struct {
1464 task_header_t hdr;
1465 WCHAR *file_name;
1466 DWORD access;
1467 DWORD flags;
1468 DWORD_PTR context;
1469 } open_file_task_t;
1471 static void AsyncFtpOpenFileProc(task_header_t *hdr)
1473 open_file_task_t *task = (open_file_task_t*)hdr;
1474 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1476 TRACE("%p\n", session);
1478 FTP_FtpOpenFileW(session, task->file_name, task->access, task->flags, task->context);
1479 heap_free(task->file_name);
1482 /***********************************************************************
1483 * FtpOpenFileW (WININET.@)
1485 * Open a remote file for writing or reading
1487 * RETURNS
1488 * HINTERNET handle on success
1489 * NULL on failure
1492 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1493 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1494 DWORD_PTR dwContext)
1496 ftp_session_t *lpwfs;
1497 appinfo_t *hIC = NULL;
1498 HINTERNET r = NULL;
1500 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1501 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1503 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1504 if (!lpwfs)
1506 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1507 return FALSE;
1510 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1512 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1513 goto lend;
1516 if ((!lpszFileName) ||
1517 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1518 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1520 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1521 goto lend;
1524 if (lpwfs->download_in_progress != NULL)
1526 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1527 goto lend;
1530 hIC = lpwfs->lpAppInfo;
1531 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1533 open_file_task_t *task;
1535 task = alloc_async_task(&lpwfs->hdr, AsyncFtpOpenFileProc, sizeof(*task));
1536 task->file_name = heap_strdupW(lpszFileName);
1537 task->access = fdwAccess;
1538 task->flags = dwFlags;
1539 task->context = dwContext;
1541 INTERNET_AsyncCall(&task->hdr);
1542 r = NULL;
1544 else
1546 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1549 lend:
1550 WININET_Release( &lpwfs->hdr );
1552 return r;
1556 /***********************************************************************
1557 * FtpGetFileA (WININET.@)
1559 * Retrieve file from the FTP server
1561 * RETURNS
1562 * TRUE on success
1563 * FALSE on failure
1566 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1567 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1568 DWORD_PTR dwContext)
1570 LPWSTR lpwzRemoteFile;
1571 LPWSTR lpwzNewFile;
1572 BOOL ret;
1574 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1575 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1576 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1577 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1578 heap_free(lpwzRemoteFile);
1579 heap_free(lpwzNewFile);
1580 return ret;
1583 typedef struct {
1584 task_header_t hdr;
1585 WCHAR *remote_file;
1586 WCHAR *new_file;
1587 BOOL fail_if_exists;
1588 DWORD local_attr;
1589 DWORD flags;
1590 DWORD_PTR context;
1591 } get_file_task_t;
1593 static void AsyncFtpGetFileProc(task_header_t *hdr)
1595 get_file_task_t *task = (get_file_task_t*)hdr;
1596 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1598 TRACE("%p\n", session);
1600 FTP_FtpGetFileW(session, task->remote_file, task->new_file, task->fail_if_exists,
1601 task->local_attr, task->flags, task->context);
1602 heap_free(task->remote_file);
1603 heap_free(task->new_file);
1607 /***********************************************************************
1608 * FtpGetFileW (WININET.@)
1610 * Retrieve file from the FTP server
1612 * RETURNS
1613 * TRUE on success
1614 * FALSE on failure
1617 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1618 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1619 DWORD_PTR dwContext)
1621 ftp_session_t *lpwfs;
1622 appinfo_t *hIC = NULL;
1623 BOOL r = FALSE;
1625 if (!lpszRemoteFile || !lpszNewFile)
1627 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1628 return FALSE;
1631 lpwfs = (ftp_session_t*) get_handle_object( hInternet );
1632 if (!lpwfs)
1634 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1635 return FALSE;
1638 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1640 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1641 goto lend;
1644 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1646 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1647 goto lend;
1650 if (lpwfs->download_in_progress != NULL)
1652 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1653 goto lend;
1656 hIC = lpwfs->lpAppInfo;
1657 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1659 get_file_task_t *task;
1661 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetFileProc, sizeof(*task));
1662 task->remote_file = heap_strdupW(lpszRemoteFile);
1663 task->new_file = heap_strdupW(lpszNewFile);
1664 task->local_attr = dwLocalFlagsAttribute;
1665 task->fail_if_exists = fFailIfExists;
1666 task->flags = dwInternetFlags;
1667 task->context = dwContext;
1669 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1671 else
1673 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1674 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1677 lend:
1678 WININET_Release( &lpwfs->hdr );
1680 return r;
1684 /***********************************************************************
1685 * FTP_FtpGetFileW (Internal)
1687 * Retrieve file from the FTP server
1689 * RETURNS
1690 * TRUE on success
1691 * FALSE on failure
1694 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1695 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1696 DWORD_PTR dwContext)
1698 BOOL bSuccess = FALSE;
1699 HANDLE hFile;
1700 appinfo_t *hIC = NULL;
1702 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1704 /* Clear any error information */
1705 INTERNET_SetLastError(0);
1707 /* Ensure we can write to lpszNewfile by opening it */
1708 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1709 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1710 if (INVALID_HANDLE_VALUE == hFile)
1711 return FALSE;
1713 /* Set up socket to retrieve data */
1714 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1716 INT nDataSocket;
1718 /* Get data socket to server */
1719 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1721 INT nResCode;
1723 /* Receive data */
1724 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1725 closesocket(nDataSocket);
1727 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1728 if (nResCode)
1730 if (nResCode == 226)
1731 bSuccess = TRUE;
1732 else
1733 FTP_SetResponseError(nResCode);
1738 if (lpwfs->lstnSocket != -1)
1740 closesocket(lpwfs->lstnSocket);
1741 lpwfs->lstnSocket = -1;
1744 CloseHandle(hFile);
1746 hIC = lpwfs->lpAppInfo;
1747 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1749 INTERNET_ASYNC_RESULT iar;
1751 iar.dwResult = (DWORD)bSuccess;
1752 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1753 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1754 &iar, sizeof(INTERNET_ASYNC_RESULT));
1757 if (!bSuccess) DeleteFileW(lpszNewFile);
1758 return bSuccess;
1761 /***********************************************************************
1762 * FtpGetFileSize (WININET.@)
1764 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1766 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1768 if (lpdwFileSizeHigh)
1769 *lpdwFileSizeHigh = 0;
1771 return 0;
1774 /***********************************************************************
1775 * FtpDeleteFileA (WININET.@)
1777 * Delete a file on the ftp server
1779 * RETURNS
1780 * TRUE on success
1781 * FALSE on failure
1784 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1786 LPWSTR lpwzFileName;
1787 BOOL ret;
1789 lpwzFileName = heap_strdupAtoW(lpszFileName);
1790 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1791 heap_free(lpwzFileName);
1792 return ret;
1795 typedef struct {
1796 task_header_t hdr;
1797 WCHAR *file_name;
1798 } delete_file_task_t;
1800 static void AsyncFtpDeleteFileProc(task_header_t *hdr)
1802 delete_file_task_t *task = (delete_file_task_t*)hdr;
1803 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1805 TRACE("%p\n", session);
1807 FTP_FtpDeleteFileW(session, task->file_name);
1808 heap_free(task->file_name);
1811 /***********************************************************************
1812 * FtpDeleteFileW (WININET.@)
1814 * Delete a file on the ftp server
1816 * RETURNS
1817 * TRUE on success
1818 * FALSE on failure
1821 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1823 ftp_session_t *lpwfs;
1824 appinfo_t *hIC = NULL;
1825 BOOL r = FALSE;
1827 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1828 if (!lpwfs)
1830 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1831 return FALSE;
1834 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1836 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1837 goto lend;
1840 if (lpwfs->download_in_progress != NULL)
1842 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1843 goto lend;
1846 if (!lpszFileName)
1848 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1849 goto lend;
1852 hIC = lpwfs->lpAppInfo;
1853 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1855 delete_file_task_t *task;
1857 task = alloc_async_task(&lpwfs->hdr, AsyncFtpDeleteFileProc, sizeof(*task));
1858 task->file_name = heap_strdupW(lpszFileName);
1860 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1862 else
1864 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1867 lend:
1868 WININET_Release( &lpwfs->hdr );
1870 return r;
1873 /***********************************************************************
1874 * FTP_FtpDeleteFileW (Internal)
1876 * Delete a file on the ftp server
1878 * RETURNS
1879 * TRUE on success
1880 * FALSE on failure
1883 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1885 INT nResCode;
1886 BOOL bSuccess = FALSE;
1887 appinfo_t *hIC = NULL;
1889 TRACE("%p\n", lpwfs);
1891 /* Clear any error information */
1892 INTERNET_SetLastError(0);
1894 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1895 goto lend;
1897 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1898 if (nResCode)
1900 if (nResCode == 250)
1901 bSuccess = TRUE;
1902 else
1903 FTP_SetResponseError(nResCode);
1905 lend:
1906 hIC = lpwfs->lpAppInfo;
1907 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1909 INTERNET_ASYNC_RESULT iar;
1911 iar.dwResult = (DWORD)bSuccess;
1912 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1913 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1914 &iar, sizeof(INTERNET_ASYNC_RESULT));
1917 return bSuccess;
1921 /***********************************************************************
1922 * FtpRemoveDirectoryA (WININET.@)
1924 * Remove a directory on the ftp server
1926 * RETURNS
1927 * TRUE on success
1928 * FALSE on failure
1931 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1933 LPWSTR lpwzDirectory;
1934 BOOL ret;
1936 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1937 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1938 heap_free(lpwzDirectory);
1939 return ret;
1942 static void AsyncFtpRemoveDirectoryProc(task_header_t *hdr)
1944 directory_task_t *task = (directory_task_t*)hdr;
1945 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1947 TRACE("%p\n", session);
1949 FTP_FtpRemoveDirectoryW(session, task->directory);
1950 heap_free(task->directory);
1953 /***********************************************************************
1954 * FtpRemoveDirectoryW (WININET.@)
1956 * Remove a directory on the ftp server
1958 * RETURNS
1959 * TRUE on success
1960 * FALSE on failure
1963 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1965 ftp_session_t *lpwfs;
1966 appinfo_t *hIC = NULL;
1967 BOOL r = FALSE;
1969 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1970 if (!lpwfs)
1972 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1973 return FALSE;
1976 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1978 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1979 goto lend;
1982 if (lpwfs->download_in_progress != NULL)
1984 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1985 goto lend;
1988 if (!lpszDirectory)
1990 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1991 goto lend;
1994 hIC = lpwfs->lpAppInfo;
1995 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1997 directory_task_t *task;
1999 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRemoveDirectoryProc, sizeof(*task));
2000 task->directory = heap_strdupW(lpszDirectory);
2002 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
2004 else
2006 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
2009 lend:
2010 WININET_Release( &lpwfs->hdr );
2012 return r;
2015 /***********************************************************************
2016 * FTP_FtpRemoveDirectoryW (Internal)
2018 * Remove a directory on the ftp server
2020 * RETURNS
2021 * TRUE on success
2022 * FALSE on failure
2025 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
2027 INT nResCode;
2028 BOOL bSuccess = FALSE;
2029 appinfo_t *hIC = NULL;
2031 TRACE("\n");
2033 /* Clear any error information */
2034 INTERNET_SetLastError(0);
2036 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2037 goto lend;
2039 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2040 if (nResCode)
2042 if (nResCode == 250)
2043 bSuccess = TRUE;
2044 else
2045 FTP_SetResponseError(nResCode);
2048 lend:
2049 hIC = lpwfs->lpAppInfo;
2050 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2052 INTERNET_ASYNC_RESULT iar;
2054 iar.dwResult = (DWORD)bSuccess;
2055 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2056 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2057 &iar, sizeof(INTERNET_ASYNC_RESULT));
2060 return bSuccess;
2064 /***********************************************************************
2065 * FtpRenameFileA (WININET.@)
2067 * Rename a file on the ftp server
2069 * RETURNS
2070 * TRUE on success
2071 * FALSE on failure
2074 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2076 LPWSTR lpwzSrc;
2077 LPWSTR lpwzDest;
2078 BOOL ret;
2080 lpwzSrc = heap_strdupAtoW(lpszSrc);
2081 lpwzDest = heap_strdupAtoW(lpszDest);
2082 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2083 heap_free(lpwzSrc);
2084 heap_free(lpwzDest);
2085 return ret;
2088 typedef struct {
2089 task_header_t hdr;
2090 WCHAR *src_file;
2091 WCHAR *dst_file;
2092 } rename_file_task_t;
2094 static void AsyncFtpRenameFileProc(task_header_t *hdr)
2096 rename_file_task_t *task = (rename_file_task_t*)hdr;
2097 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
2099 TRACE("%p\n", session);
2101 FTP_FtpRenameFileW(session, task->src_file, task->dst_file);
2102 heap_free(task->src_file);
2103 heap_free(task->dst_file);
2106 /***********************************************************************
2107 * FtpRenameFileW (WININET.@)
2109 * Rename a file on the ftp server
2111 * RETURNS
2112 * TRUE on success
2113 * FALSE on failure
2116 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2118 ftp_session_t *lpwfs;
2119 appinfo_t *hIC = NULL;
2120 BOOL r = FALSE;
2122 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
2123 if (!lpwfs)
2125 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2126 return FALSE;
2129 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2131 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2132 goto lend;
2135 if (lpwfs->download_in_progress != NULL)
2137 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2138 goto lend;
2141 if (!lpszSrc || !lpszDest)
2143 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2144 goto lend;
2147 hIC = lpwfs->lpAppInfo;
2148 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2150 rename_file_task_t *task;
2152 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRenameFileProc, sizeof(*task));
2153 task->src_file = heap_strdupW(lpszSrc);
2154 task->dst_file = heap_strdupW(lpszDest);
2156 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
2158 else
2160 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2163 lend:
2164 WININET_Release( &lpwfs->hdr );
2166 return r;
2169 /***********************************************************************
2170 * FTP_FtpRenameFileW (Internal)
2172 * Rename a file on the ftp server
2174 * RETURNS
2175 * TRUE on success
2176 * FALSE on failure
2179 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2181 INT nResCode;
2182 BOOL bSuccess = FALSE;
2183 appinfo_t *hIC = NULL;
2185 TRACE("\n");
2187 /* Clear any error information */
2188 INTERNET_SetLastError(0);
2190 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2191 goto lend;
2193 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2194 if (nResCode == 350)
2196 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2197 goto lend;
2199 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2202 if (nResCode == 250)
2203 bSuccess = TRUE;
2204 else
2205 FTP_SetResponseError(nResCode);
2207 lend:
2208 hIC = lpwfs->lpAppInfo;
2209 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2211 INTERNET_ASYNC_RESULT iar;
2213 iar.dwResult = (DWORD)bSuccess;
2214 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2215 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2216 &iar, sizeof(INTERNET_ASYNC_RESULT));
2219 return bSuccess;
2222 /***********************************************************************
2223 * FtpCommandA (WININET.@)
2225 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2226 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2228 BOOL r;
2229 WCHAR *cmdW;
2231 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2232 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2234 if (fExpectResponse)
2236 FIXME("data connection not supported\n");
2237 return FALSE;
2240 if (!lpszCommand || !lpszCommand[0])
2242 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2243 return FALSE;
2246 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2248 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2249 return FALSE;
2252 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2254 heap_free(cmdW);
2255 return r;
2258 /***********************************************************************
2259 * FtpCommandW (WININET.@)
2261 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2262 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2264 BOOL r = FALSE;
2265 ftp_session_t *lpwfs;
2266 LPSTR cmd = NULL;
2267 DWORD len, nBytesSent= 0;
2268 INT nResCode, nRC = 0;
2270 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2271 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2273 if (!lpszCommand || !lpszCommand[0])
2275 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2276 return FALSE;
2279 if (fExpectResponse)
2281 FIXME("data connection not supported\n");
2282 return FALSE;
2285 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
2286 if (!lpwfs)
2288 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2289 return FALSE;
2292 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2294 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2295 goto lend;
2298 if (lpwfs->download_in_progress != NULL)
2300 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2301 goto lend;
2304 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2305 if ((cmd = heap_alloc(len)))
2306 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2307 else
2309 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2310 goto lend;
2313 strcat(cmd, szCRLF);
2314 len--;
2316 TRACE("Sending (%s) len(%d)\n", cmd, len);
2317 while ((nBytesSent < len) && (nRC != -1))
2319 nRC = sock_send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2320 if (nRC != -1)
2322 nBytesSent += nRC;
2323 TRACE("Sent %d bytes\n", nRC);
2327 if (nBytesSent)
2329 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2330 if (nResCode > 0 && nResCode < 400)
2331 r = TRUE;
2332 else
2333 FTP_SetResponseError(nResCode);
2336 lend:
2337 WININET_Release( &lpwfs->hdr );
2338 heap_free( cmd );
2339 return r;
2343 /***********************************************************************
2344 * FTPSESSION_Destroy (internal)
2346 * Deallocate session handle
2348 static void FTPSESSION_Destroy(object_header_t *hdr)
2350 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2352 TRACE("\n");
2354 WININET_Release(&lpwfs->lpAppInfo->hdr);
2356 heap_free(lpwfs->lpszPassword);
2357 heap_free(lpwfs->lpszUserName);
2358 heap_free(lpwfs->servername);
2361 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2363 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2365 TRACE("\n");
2367 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2368 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2370 if (lpwfs->download_in_progress != NULL)
2371 lpwfs->download_in_progress->session_deleted = TRUE;
2373 if (lpwfs->sndSocket != -1)
2374 closesocket(lpwfs->sndSocket);
2376 if (lpwfs->lstnSocket != -1)
2377 closesocket(lpwfs->lstnSocket);
2379 if (lpwfs->pasvSocket != -1)
2380 closesocket(lpwfs->pasvSocket);
2382 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2383 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2386 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2388 switch(option) {
2389 case INTERNET_OPTION_HANDLE_TYPE:
2390 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2392 if (*size < sizeof(ULONG))
2393 return ERROR_INSUFFICIENT_BUFFER;
2395 *size = sizeof(DWORD);
2396 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2397 return ERROR_SUCCESS;
2400 return INET_QueryOption(hdr, option, buffer, size, unicode);
2403 static const object_vtbl_t FTPSESSIONVtbl = {
2404 FTPSESSION_Destroy,
2405 FTPSESSION_CloseConnection,
2406 FTPSESSION_QueryOption,
2407 INET_SetOption,
2408 NULL,
2409 NULL,
2410 NULL,
2411 NULL,
2412 NULL
2416 /***********************************************************************
2417 * FTP_Connect (internal)
2419 * Connect to a ftp server
2421 * RETURNS
2422 * HINTERNET a session handle on success
2423 * NULL on failure
2425 * NOTES:
2427 * Windows uses 'anonymous' as the username, when given a NULL username
2428 * and a NULL password. The password is first looked up in:
2430 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2432 * If this entry is not present it uses the current username as the password.
2436 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2437 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2438 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2439 DWORD dwInternalFlags)
2441 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2442 'M','i','c','r','o','s','o','f','t','\\',
2443 'W','i','n','d','o','w','s','\\',
2444 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2445 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2446 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2447 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2448 static const WCHAR szEmpty[] = {'\0'};
2449 struct sockaddr_in socketAddr;
2450 INT nsocket = -1;
2451 socklen_t sock_namelen;
2452 BOOL bSuccess = FALSE;
2453 ftp_session_t *lpwfs = NULL;
2454 char szaddr[INET_ADDRSTRLEN];
2456 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2457 hIC, debugstr_w(lpszServerName),
2458 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2460 assert( hIC->hdr.htype == WH_HINIT );
2462 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2464 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2465 return NULL;
2468 lpwfs = alloc_object(&hIC->hdr, &FTPSESSIONVtbl, sizeof(ftp_session_t));
2469 if (NULL == lpwfs)
2471 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2472 return NULL;
2475 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2476 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2477 else
2478 lpwfs->serverport = nServerPort;
2480 lpwfs->hdr.htype = WH_HFTPSESSION;
2481 lpwfs->hdr.dwFlags = dwFlags;
2482 lpwfs->hdr.dwContext = dwContext;
2483 lpwfs->hdr.dwInternalFlags |= dwInternalFlags;
2484 lpwfs->download_in_progress = NULL;
2485 lpwfs->sndSocket = -1;
2486 lpwfs->lstnSocket = -1;
2487 lpwfs->pasvSocket = -1;
2489 WININET_AddRef( &hIC->hdr );
2490 lpwfs->lpAppInfo = hIC;
2491 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2493 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
2494 if(strchrW(hIC->proxy, ' '))
2495 FIXME("Several proxies not implemented.\n");
2496 if(hIC->proxyBypass)
2497 FIXME("Proxy bypass is ignored.\n");
2499 if (!lpszUserName || !strlenW(lpszUserName)) {
2500 HKEY key;
2501 WCHAR szPassword[MAX_PATH];
2502 DWORD len = sizeof(szPassword);
2504 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2506 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2507 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2508 /* Nothing in the registry, get the username and use that as the password */
2509 if (!GetUserNameW(szPassword, &len)) {
2510 /* Should never get here, but use an empty password as failsafe */
2511 strcpyW(szPassword, szEmpty);
2514 RegCloseKey(key);
2516 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2517 lpwfs->lpszPassword = heap_strdupW(szPassword);
2519 else {
2520 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2521 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2523 lpwfs->servername = heap_strdupW(lpszServerName);
2525 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2526 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2528 INTERNET_ASYNC_RESULT iar;
2530 iar.dwResult = (DWORD_PTR)lpwfs->hdr.hInternet;
2531 iar.dwError = ERROR_SUCCESS;
2533 SendAsyncCallback(&hIC->hdr, dwContext,
2534 INTERNET_STATUS_HANDLE_CREATED, &iar,
2535 sizeof(INTERNET_ASYNC_RESULT));
2538 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2539 (LPWSTR) lpszServerName, (strlenW(lpszServerName)+1) * sizeof(WCHAR));
2541 sock_namelen = sizeof(socketAddr);
2542 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen))
2544 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2545 goto lerror;
2548 if (socketAddr.sin_family != AF_INET)
2550 WARN("unsupported address family %d\n", socketAddr.sin_family);
2551 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2552 goto lerror;
2555 inet_ntop(socketAddr.sin_family, &socketAddr.sin_addr, szaddr, sizeof(szaddr));
2556 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2557 szaddr, strlen(szaddr)+1);
2559 nsocket = socket(AF_INET,SOCK_STREAM,0);
2560 if (nsocket == -1)
2562 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2563 goto lerror;
2566 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2567 szaddr, strlen(szaddr)+1);
2569 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2571 ERR("Unable to connect (%s)\n", strerror(errno));
2572 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2573 closesocket(nsocket);
2575 else
2577 TRACE("Connected to server\n");
2578 lpwfs->sndSocket = nsocket;
2579 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2580 szaddr, strlen(szaddr)+1);
2582 sock_namelen = sizeof(lpwfs->socketAddress);
2583 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2585 if (FTP_ConnectToHost(lpwfs))
2587 TRACE("Successfully logged into server\n");
2588 bSuccess = TRUE;
2592 lerror:
2593 if (!bSuccess)
2595 if(lpwfs)
2596 WININET_Release( &lpwfs->hdr );
2597 return NULL;
2600 return lpwfs->hdr.hInternet;
2604 /***********************************************************************
2605 * FTP_ConnectToHost (internal)
2607 * Connect to a ftp server
2609 * RETURNS
2610 * TRUE on success
2611 * NULL on failure
2614 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2616 INT nResCode;
2617 BOOL bSuccess = FALSE;
2619 TRACE("\n");
2620 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2622 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2623 goto lend;
2625 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2626 if (nResCode)
2628 /* Login successful... */
2629 if (nResCode == 230)
2630 bSuccess = TRUE;
2631 /* User name okay, need password... */
2632 else if (nResCode == 331)
2633 bSuccess = FTP_SendPassword(lpwfs);
2634 /* Need account for login... */
2635 else if (nResCode == 332)
2636 bSuccess = FTP_SendAccount(lpwfs);
2637 else
2638 FTP_SetResponseError(nResCode);
2641 TRACE("Returning %d\n", bSuccess);
2642 lend:
2643 return bSuccess;
2647 /***********************************************************************
2648 * FTP_SendCommandA (internal)
2650 * Send command to server
2652 * RETURNS
2653 * TRUE on success
2654 * NULL on failure
2657 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2658 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2660 DWORD len;
2661 CHAR *buf;
2662 DWORD nBytesSent = 0;
2663 int nRC = 0;
2664 DWORD dwParamLen;
2666 TRACE("%d: (%s) %d\n", ftpCmd, debugstr_a(lpszParam), nSocket);
2668 if (lpfnStatusCB)
2670 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2673 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2674 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2675 if (NULL == (buf = heap_alloc(len+1)))
2677 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2678 return FALSE;
2680 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2681 dwParamLen ? lpszParam : "", szCRLF);
2683 TRACE("Sending (%s) len(%d)\n", buf, len);
2684 while((nBytesSent < len) && (nRC != -1))
2686 nRC = sock_send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2687 nBytesSent += nRC;
2689 heap_free(buf);
2691 if (lpfnStatusCB)
2693 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2694 &nBytesSent, sizeof(DWORD));
2697 TRACE("Sent %d bytes\n", nBytesSent);
2698 return (nRC != -1);
2701 /***********************************************************************
2702 * FTP_SendCommand (internal)
2704 * Send command to server
2706 * RETURNS
2707 * TRUE on success
2708 * NULL on failure
2711 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2712 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2714 BOOL ret;
2715 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2716 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2717 heap_free(lpszParamA);
2718 return ret;
2721 /***********************************************************************
2722 * FTP_ReceiveResponse (internal)
2724 * Receive response from server
2726 * RETURNS
2727 * Reply code on success
2728 * 0 on failure
2731 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2733 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2734 DWORD nRecv;
2735 INT rc = 0;
2736 char firstprefix[5];
2737 BOOL multiline = FALSE;
2739 TRACE("socket(%d)\n", lpwfs->sndSocket);
2741 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2743 while(1)
2745 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2746 goto lerror;
2748 if (nRecv >= 3)
2750 if(!multiline)
2752 if(lpszResponse[3] != '-')
2753 break;
2754 else
2755 { /* Start of multiline response. Loop until we get "nnn " */
2756 multiline = TRUE;
2757 memcpy(firstprefix, lpszResponse, 3);
2758 firstprefix[3] = ' ';
2759 firstprefix[4] = '\0';
2762 else
2764 if(!memcmp(firstprefix, lpszResponse, 4))
2765 break;
2770 if (nRecv >= 3)
2772 rc = atoi(lpszResponse);
2774 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2775 &nRecv, sizeof(DWORD));
2778 lerror:
2779 TRACE("return %d\n", rc);
2780 return rc;
2784 /***********************************************************************
2785 * FTP_SendPassword (internal)
2787 * Send password to ftp server
2789 * RETURNS
2790 * TRUE on success
2791 * NULL on failure
2794 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2796 INT nResCode;
2797 BOOL bSuccess = FALSE;
2799 TRACE("\n");
2800 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2801 goto lend;
2803 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2804 if (nResCode)
2806 TRACE("Received reply code %d\n", nResCode);
2807 /* Login successful... */
2808 if (nResCode == 230)
2809 bSuccess = TRUE;
2810 /* Command not implemented, superfluous at the server site... */
2811 /* Need account for login... */
2812 else if (nResCode == 332)
2813 bSuccess = FTP_SendAccount(lpwfs);
2814 else
2815 FTP_SetResponseError(nResCode);
2818 lend:
2819 TRACE("Returning %d\n", bSuccess);
2820 return bSuccess;
2824 /***********************************************************************
2825 * FTP_SendAccount (internal)
2829 * RETURNS
2830 * TRUE on success
2831 * FALSE on failure
2834 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2836 INT nResCode;
2837 BOOL bSuccess = FALSE;
2839 TRACE("\n");
2840 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2841 goto lend;
2843 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2844 if (nResCode)
2845 bSuccess = TRUE;
2846 else
2847 FTP_SetResponseError(nResCode);
2849 lend:
2850 return bSuccess;
2854 /***********************************************************************
2855 * FTP_SendStore (internal)
2857 * Send request to upload file to ftp server
2859 * RETURNS
2860 * TRUE on success
2861 * FALSE on failure
2864 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2866 INT nResCode;
2867 BOOL bSuccess = FALSE;
2869 TRACE("\n");
2870 if (!FTP_InitListenSocket(lpwfs))
2871 goto lend;
2873 if (!FTP_SendType(lpwfs, dwType))
2874 goto lend;
2876 if (!FTP_SendPortOrPasv(lpwfs))
2877 goto lend;
2879 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2880 goto lend;
2881 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2882 if (nResCode)
2884 if (nResCode == 150 || nResCode == 125)
2885 bSuccess = TRUE;
2886 else
2887 FTP_SetResponseError(nResCode);
2890 lend:
2891 if (!bSuccess && lpwfs->lstnSocket != -1)
2893 closesocket(lpwfs->lstnSocket);
2894 lpwfs->lstnSocket = -1;
2897 return bSuccess;
2901 /***********************************************************************
2902 * FTP_InitListenSocket (internal)
2904 * Create a socket to listen for server response
2906 * RETURNS
2907 * TRUE on success
2908 * FALSE on failure
2911 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2913 BOOL bSuccess = FALSE;
2914 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2916 TRACE("\n");
2918 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2919 if (lpwfs->lstnSocket == -1)
2921 TRACE("Unable to create listening socket\n");
2922 goto lend;
2925 /* We obtain our ip addr from the name of the command channel socket */
2926 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2928 /* and get the system to assign us a port */
2929 lpwfs->lstnSocketAddress.sin_port = htons(0);
2931 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2933 TRACE("Unable to bind socket\n");
2934 goto lend;
2937 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2939 TRACE("listen failed\n");
2940 goto lend;
2943 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2944 bSuccess = TRUE;
2946 lend:
2947 if (!bSuccess && lpwfs->lstnSocket != -1)
2949 closesocket(lpwfs->lstnSocket);
2950 lpwfs->lstnSocket = -1;
2953 return bSuccess;
2957 /***********************************************************************
2958 * FTP_SendType (internal)
2960 * Tell server type of data being transferred
2962 * RETURNS
2963 * TRUE on success
2964 * FALSE on failure
2966 * W98SE doesn't cache the type that's currently set
2967 * (i.e. it sends it always),
2968 * so we probably don't want to do that either.
2970 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2972 INT nResCode;
2973 WCHAR type[] = { 'I','\0' };
2974 BOOL bSuccess = FALSE;
2976 TRACE("\n");
2977 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2978 type[0] = 'A';
2980 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2981 goto lend;
2983 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2984 if (nResCode)
2986 if (nResCode == 2)
2987 bSuccess = TRUE;
2988 else
2989 FTP_SetResponseError(nResCode);
2992 lend:
2993 return bSuccess;
2997 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2998 /***********************************************************************
2999 * FTP_GetFileSize (internal)
3001 * Retrieves from the server the size of the given file
3003 * RETURNS
3004 * TRUE on success
3005 * FALSE on failure
3008 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
3010 INT nResCode;
3011 BOOL bSuccess = FALSE;
3013 TRACE("\n");
3015 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
3016 goto lend;
3018 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3019 if (nResCode)
3021 if (nResCode == 213) {
3022 /* Now parses the output to get the actual file size */
3023 int i;
3024 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3026 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3027 if (lpszResponseBuffer[i] == '\0') return FALSE;
3028 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3030 bSuccess = TRUE;
3031 } else {
3032 FTP_SetResponseError(nResCode);
3036 lend:
3037 return bSuccess;
3039 #endif
3042 /***********************************************************************
3043 * FTP_SendPort (internal)
3045 * Tell server which port to use
3047 * RETURNS
3048 * TRUE on success
3049 * FALSE on failure
3052 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3054 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3055 INT nResCode;
3056 WCHAR szIPAddress[64];
3057 BOOL bSuccess = FALSE;
3058 TRACE("\n");
3060 sprintfW(szIPAddress, szIPFormat,
3061 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
3062 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
3063 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
3064 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
3065 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3066 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3068 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3069 goto lend;
3071 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3072 if (nResCode)
3074 if (nResCode == 200)
3075 bSuccess = TRUE;
3076 else
3077 FTP_SetResponseError(nResCode);
3080 lend:
3081 return bSuccess;
3085 /***********************************************************************
3086 * FTP_DoPassive (internal)
3088 * Tell server that we want to do passive transfers
3089 * and connect data socket
3091 * RETURNS
3092 * TRUE on success
3093 * FALSE on failure
3096 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3098 INT nResCode;
3099 BOOL bSuccess = FALSE;
3101 TRACE("\n");
3102 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3103 goto lend;
3105 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3106 if (nResCode)
3108 if (nResCode == 227)
3110 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3111 LPSTR p;
3112 int f[6];
3113 int i;
3114 char *pAddr, *pPort;
3115 INT nsocket = -1;
3116 struct sockaddr_in dataSocketAddress;
3118 p = lpszResponseBuffer+4; /* skip status code */
3119 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3121 if (*p == '\0')
3123 ERR("no address found in response, aborting\n");
3124 goto lend;
3127 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3128 &f[4], &f[5]) != 6)
3130 ERR("unknown response address format '%s', aborting\n", p);
3131 goto lend;
3133 for (i=0; i < 6; i++)
3134 f[i] = f[i] & 0xff;
3136 dataSocketAddress = lpwfs->socketAddress;
3137 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3138 pPort = (char *)&(dataSocketAddress.sin_port);
3139 pAddr[0] = f[0];
3140 pAddr[1] = f[1];
3141 pAddr[2] = f[2];
3142 pAddr[3] = f[3];
3143 pPort[0] = f[4];
3144 pPort[1] = f[5];
3146 nsocket = socket(AF_INET,SOCK_STREAM,0);
3147 if (nsocket == -1)
3148 goto lend;
3150 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3152 ERR("can't connect passive FTP data port.\n");
3153 closesocket(nsocket);
3154 goto lend;
3156 lpwfs->pasvSocket = nsocket;
3157 bSuccess = TRUE;
3159 else
3160 FTP_SetResponseError(nResCode);
3163 lend:
3164 return bSuccess;
3168 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3170 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3172 if (!FTP_DoPassive(lpwfs))
3173 return FALSE;
3175 else
3177 if (!FTP_SendPort(lpwfs))
3178 return FALSE;
3180 return TRUE;
3184 /***********************************************************************
3185 * FTP_GetDataSocket (internal)
3187 * Either accepts an incoming data socket connection from the server
3188 * or just returns the already opened socket after a PASV command
3189 * in case of passive FTP.
3192 * RETURNS
3193 * TRUE on success
3194 * FALSE on failure
3197 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3199 struct sockaddr_in saddr;
3200 socklen_t addrlen = sizeof(struct sockaddr);
3202 TRACE("\n");
3203 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3205 *nDataSocket = lpwfs->pasvSocket;
3206 lpwfs->pasvSocket = -1;
3208 else
3210 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3211 closesocket(lpwfs->lstnSocket);
3212 lpwfs->lstnSocket = -1;
3214 return *nDataSocket != -1;
3218 /***********************************************************************
3219 * FTP_SendData (internal)
3221 * Send data to the server
3223 * RETURNS
3224 * TRUE on success
3225 * FALSE on failure
3228 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3230 BY_HANDLE_FILE_INFORMATION fi;
3231 DWORD nBytesRead = 0;
3232 DWORD nBytesSent = 0;
3233 DWORD nTotalSent = 0;
3234 DWORD nBytesToSend, nLen;
3235 int nRC = 1;
3236 time_t s_long_time, e_long_time;
3237 LONG nSeconds;
3238 CHAR *lpszBuffer;
3240 TRACE("\n");
3241 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3243 /* Get the size of the file. */
3244 GetFileInformationByHandle(hFile, &fi);
3245 time(&s_long_time);
3249 nBytesToSend = nBytesRead - nBytesSent;
3251 if (nBytesToSend <= 0)
3253 /* Read data from file. */
3254 nBytesSent = 0;
3255 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3256 ERR("Failed reading from file\n");
3258 if (nBytesRead > 0)
3259 nBytesToSend = nBytesRead;
3260 else
3261 break;
3264 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3265 DATA_PACKET_SIZE : nBytesToSend;
3266 nRC = sock_send(nDataSocket, lpszBuffer, nLen, 0);
3268 if (nRC != -1)
3270 nBytesSent += nRC;
3271 nTotalSent += nRC;
3274 /* Do some computation to display the status. */
3275 time(&e_long_time);
3276 nSeconds = e_long_time - s_long_time;
3277 if( nSeconds / 60 > 0 )
3279 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3280 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3281 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3283 else
3285 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3286 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3287 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3289 } while (nRC != -1);
3291 TRACE("file transfer complete!\n");
3293 heap_free(lpszBuffer);
3294 return nTotalSent;
3298 /***********************************************************************
3299 * FTP_SendRetrieve (internal)
3301 * Send request to retrieve a file
3303 * RETURNS
3304 * Number of bytes to be received on success
3305 * 0 on failure
3308 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3310 INT nResCode;
3311 BOOL ret;
3313 TRACE("\n");
3314 if (!(ret = FTP_InitListenSocket(lpwfs)))
3315 goto lend;
3317 if (!(ret = FTP_SendType(lpwfs, dwType)))
3318 goto lend;
3320 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3321 goto lend;
3323 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3324 goto lend;
3326 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3327 if ((nResCode != 125) && (nResCode != 150)) {
3328 /* That means that we got an error getting the file. */
3329 FTP_SetResponseError(nResCode);
3330 ret = FALSE;
3333 lend:
3334 if (!ret && lpwfs->lstnSocket != -1)
3336 closesocket(lpwfs->lstnSocket);
3337 lpwfs->lstnSocket = -1;
3340 return ret;
3344 /***********************************************************************
3345 * FTP_RetrieveData (internal)
3347 * Retrieve data from server
3349 * RETURNS
3350 * TRUE on success
3351 * FALSE on failure
3354 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3356 DWORD nBytesWritten;
3357 INT nRC = 0;
3358 CHAR *lpszBuffer;
3360 TRACE("\n");
3362 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3363 if (NULL == lpszBuffer)
3365 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3366 return FALSE;
3369 while (nRC != -1)
3371 nRC = sock_recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3372 if (nRC != -1)
3374 /* other side closed socket. */
3375 if (nRC == 0)
3376 goto recv_end;
3377 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3381 TRACE("Data transfer complete\n");
3383 recv_end:
3384 heap_free(lpszBuffer);
3385 return (nRC != -1);
3388 /***********************************************************************
3389 * FTPFINDNEXT_Destroy (internal)
3391 * Deallocate session handle
3393 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3395 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3396 DWORD i;
3398 TRACE("\n");
3400 WININET_Release(&lpwfn->lpFtpSession->hdr);
3402 for (i = 0; i < lpwfn->size; i++)
3404 heap_free(lpwfn->lpafp[i].lpszName);
3406 heap_free(lpwfn->lpafp);
3409 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3411 WIN32_FIND_DATAW *find_data = data;
3412 DWORD res = ERROR_SUCCESS;
3414 TRACE("index(%d) size(%d)\n", find->index, find->size);
3416 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3418 if (find->index < find->size) {
3419 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3420 find->index++;
3422 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3423 }else {
3424 res = ERROR_NO_MORE_FILES;
3427 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3429 INTERNET_ASYNC_RESULT iar;
3431 iar.dwResult = (res == ERROR_SUCCESS);
3432 iar.dwError = res;
3434 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3435 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3436 sizeof(INTERNET_ASYNC_RESULT));
3439 return res;
3442 typedef struct {
3443 task_header_t hdr;
3444 WIN32_FIND_DATAW *find_data;
3445 } find_next_task_t;
3447 static void FTPFINDNEXT_AsyncFindNextFileProc(task_header_t *hdr)
3449 find_next_task_t *task = (find_next_task_t*)hdr;
3451 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)task->hdr.hdr, task->find_data);
3454 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3456 switch(option) {
3457 case INTERNET_OPTION_HANDLE_TYPE:
3458 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3460 if (*size < sizeof(ULONG))
3461 return ERROR_INSUFFICIENT_BUFFER;
3463 *size = sizeof(DWORD);
3464 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3465 return ERROR_SUCCESS;
3468 return INET_QueryOption(hdr, option, buffer, size, unicode);
3471 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3473 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3475 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3477 find_next_task_t *task;
3479 task = alloc_async_task(&find->hdr, FTPFINDNEXT_AsyncFindNextFileProc, sizeof(*task));
3480 task->find_data = data;
3482 INTERNET_AsyncCall(&task->hdr);
3483 return ERROR_SUCCESS;
3486 return FTPFINDNEXT_FindNextFileProc(find, data);
3489 static const object_vtbl_t FTPFINDNEXTVtbl = {
3490 FTPFINDNEXT_Destroy,
3491 NULL,
3492 FTPFINDNEXT_QueryOption,
3493 INET_SetOption,
3494 NULL,
3495 NULL,
3496 NULL,
3497 NULL,
3498 FTPFINDNEXT_FindNextFileW
3501 /***********************************************************************
3502 * FTP_ReceiveFileList (internal)
3504 * Read file list from server
3506 * RETURNS
3507 * Handle to file list on success
3508 * NULL on failure
3511 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3512 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3514 DWORD dwSize = 0;
3515 LPFILEPROPERTIESW lpafp = NULL;
3516 LPWININETFTPFINDNEXTW lpwfn = NULL;
3518 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3520 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3522 if(lpFindFileData)
3523 FTP_ConvertFileProp(lpafp, lpFindFileData);
3525 lpwfn = alloc_object(&lpwfs->hdr, &FTPFINDNEXTVtbl, sizeof(WININETFTPFINDNEXTW));
3526 if (lpwfn)
3528 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3529 lpwfn->hdr.dwContext = dwContext;
3530 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3531 lpwfn->size = dwSize;
3532 lpwfn->lpafp = lpafp;
3534 WININET_AddRef( &lpwfs->hdr );
3535 lpwfn->lpFtpSession = lpwfs;
3536 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3540 TRACE("Matched %d files\n", dwSize);
3541 return lpwfn ? lpwfn->hdr.hInternet : NULL;
3545 /***********************************************************************
3546 * FTP_ConvertFileProp (internal)
3548 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3550 * RETURNS
3551 * TRUE on success
3552 * FALSE on failure
3555 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3557 BOOL bSuccess = FALSE;
3559 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3561 if (lpafp)
3563 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3564 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3565 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3567 /* Not all fields are filled in */
3568 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3569 lpFindFileData->nFileSizeLow = lpafp->nSize;
3571 if (lpafp->bIsDirectory)
3572 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3574 if (lpafp->lpszName)
3575 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3577 bSuccess = TRUE;
3580 return bSuccess;
3583 /***********************************************************************
3584 * FTP_ParseNextFile (internal)
3586 * Parse the next line in file listing
3588 * RETURNS
3589 * TRUE on success
3590 * FALSE on failure
3592 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3594 static const char szSpace[] = " \t";
3595 DWORD nBufLen;
3596 char *pszLine;
3597 char *pszToken;
3598 char *pszTmp;
3599 BOOL found = FALSE;
3600 int i;
3602 lpfp->lpszName = NULL;
3603 do {
3604 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3605 return FALSE;
3607 pszToken = strtok(pszLine, szSpace);
3608 /* ls format
3609 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3611 * For instance:
3612 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3614 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3615 if(!FTP_ParsePermission(pszToken, lpfp))
3616 lpfp->bIsDirectory = FALSE;
3617 for(i=0; i<=3; i++) {
3618 if(!(pszToken = strtok(NULL, szSpace)))
3619 break;
3621 if(!pszToken) continue;
3622 if(lpfp->bIsDirectory) {
3623 TRACE("Is directory\n");
3624 lpfp->nSize = 0;
3626 else {
3627 TRACE("Size: %s\n", pszToken);
3628 lpfp->nSize = atol(pszToken);
3631 lpfp->tmLastModified.wSecond = 0;
3632 lpfp->tmLastModified.wMinute = 0;
3633 lpfp->tmLastModified.wHour = 0;
3634 lpfp->tmLastModified.wDay = 0;
3635 lpfp->tmLastModified.wMonth = 0;
3636 lpfp->tmLastModified.wYear = 0;
3638 /* Determine month */
3639 pszToken = strtok(NULL, szSpace);
3640 if(!pszToken) continue;
3641 if(strlen(pszToken) >= 3) {
3642 pszToken[3] = 0;
3643 if((pszTmp = StrStrIA(szMonths, pszToken)))
3644 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3646 /* Determine day */
3647 pszToken = strtok(NULL, szSpace);
3648 if(!pszToken) continue;
3649 lpfp->tmLastModified.wDay = atoi(pszToken);
3650 /* Determine time or year */
3651 pszToken = strtok(NULL, szSpace);
3652 if(!pszToken) continue;
3653 if((pszTmp = strchr(pszToken, ':'))) {
3654 SYSTEMTIME curr_time;
3655 *pszTmp = 0;
3656 pszTmp++;
3657 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3658 lpfp->tmLastModified.wHour = atoi(pszToken);
3659 GetLocalTime( &curr_time );
3660 lpfp->tmLastModified.wYear = curr_time.wYear;
3662 else {
3663 lpfp->tmLastModified.wYear = atoi(pszToken);
3664 lpfp->tmLastModified.wHour = 12;
3666 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3667 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3668 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3670 pszToken = strtok(NULL, szSpace);
3671 if(!pszToken) continue;
3672 lpfp->lpszName = heap_strdupAtoW(pszToken);
3673 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3675 /* NT way of parsing ... :
3677 07-13-03 08:55PM <DIR> sakpatch
3678 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3680 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3681 int mon, mday, year, hour, min;
3682 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3684 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3685 lpfp->tmLastModified.wDay = mday;
3686 lpfp->tmLastModified.wMonth = mon;
3687 lpfp->tmLastModified.wYear = year;
3689 /* Hacky and bad Y2K protection :-) */
3690 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3692 pszToken = strtok(NULL, szSpace);
3693 if(!pszToken) continue;
3694 sscanf(pszToken, "%d:%d", &hour, &min);
3695 lpfp->tmLastModified.wHour = hour;
3696 lpfp->tmLastModified.wMinute = min;
3697 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3698 lpfp->tmLastModified.wHour += 12;
3700 lpfp->tmLastModified.wSecond = 0;
3702 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3703 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3704 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3706 pszToken = strtok(NULL, szSpace);
3707 if(!pszToken) continue;
3708 if(!strcasecmp(pszToken, "<DIR>")) {
3709 lpfp->bIsDirectory = TRUE;
3710 lpfp->nSize = 0;
3711 TRACE("Is directory\n");
3713 else {
3714 lpfp->bIsDirectory = FALSE;
3715 lpfp->nSize = atol(pszToken);
3716 TRACE("Size: %d\n", lpfp->nSize);
3719 pszToken = strtok(NULL, szSpace);
3720 if(!pszToken) continue;
3721 lpfp->lpszName = heap_strdupAtoW(pszToken);
3722 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3724 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3725 else if(pszToken[0] == '+') {
3726 FIXME("EPLF Format not implemented\n");
3729 if(lpfp->lpszName) {
3730 if((lpszSearchFile == NULL) ||
3731 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3732 found = TRUE;
3733 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3735 else {
3736 heap_free(lpfp->lpszName);
3737 lpfp->lpszName = NULL;
3740 } while(!found);
3741 return TRUE;
3744 /***********************************************************************
3745 * FTP_ParseDirectory (internal)
3747 * Parse string of directory information
3749 * RETURNS
3750 * TRUE on success
3751 * FALSE on failure
3753 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3754 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3756 BOOL bSuccess = TRUE;
3757 INT sizeFilePropArray = 500;/*20; */
3758 INT indexFilePropArray = -1;
3760 TRACE("\n");
3762 /* Allocate initial file properties array */
3763 *lpafp = heap_alloc_zero(sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3764 if (!*lpafp)
3765 return FALSE;
3767 do {
3768 if (indexFilePropArray+1 >= sizeFilePropArray)
3770 LPFILEPROPERTIESW tmpafp;
3772 sizeFilePropArray *= 2;
3773 tmpafp = heap_realloc_zero(*lpafp, sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3774 if (NULL == tmpafp)
3776 bSuccess = FALSE;
3777 break;
3780 *lpafp = tmpafp;
3782 indexFilePropArray++;
3783 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3785 if (bSuccess && indexFilePropArray)
3787 if (indexFilePropArray < sizeFilePropArray - 1)
3789 LPFILEPROPERTIESW tmpafp;
3791 tmpafp = heap_realloc(*lpafp, sizeof(FILEPROPERTIESW)*indexFilePropArray);
3792 if (NULL != tmpafp)
3793 *lpafp = tmpafp;
3795 *dwfp = indexFilePropArray;
3797 else
3799 heap_free(*lpafp);
3800 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3801 bSuccess = FALSE;
3804 return bSuccess;
3808 /***********************************************************************
3809 * FTP_ParsePermission (internal)
3811 * Parse permission string of directory information
3813 * RETURNS
3814 * TRUE on success
3815 * FALSE on failure
3818 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3820 BOOL bSuccess = TRUE;
3821 unsigned short nPermission = 0;
3822 INT nPos = 1;
3823 INT nLast = 9;
3825 TRACE("\n");
3826 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3828 bSuccess = FALSE;
3829 return bSuccess;
3832 lpfp->bIsDirectory = (*lpszPermission == 'd');
3835 switch (nPos)
3837 case 1:
3838 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3839 break;
3840 case 2:
3841 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3842 break;
3843 case 3:
3844 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3845 break;
3846 case 4:
3847 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3848 break;
3849 case 5:
3850 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3851 break;
3852 case 6:
3853 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3854 break;
3855 case 7:
3856 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3857 break;
3858 case 8:
3859 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3860 break;
3861 case 9:
3862 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3863 break;
3865 nPos++;
3866 }while (nPos <= nLast);
3868 lpfp->permissions = nPermission;
3869 return bSuccess;
3873 /***********************************************************************
3874 * FTP_SetResponseError (internal)
3876 * Set the appropriate error code for a given response from the server
3878 * RETURNS
3881 static DWORD FTP_SetResponseError(DWORD dwResponse)
3883 DWORD dwCode = 0;
3885 switch(dwResponse)
3887 case 425: /* Cannot open data connection. */
3888 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3889 break;
3891 case 426: /* Connection closed, transer aborted. */
3892 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3893 break;
3895 case 530: /* Not logged in. Login incorrect. */
3896 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3897 break;
3899 case 421: /* Service not available - Server may be shutting down. */
3900 case 450: /* File action not taken. File may be busy. */
3901 case 451: /* Action aborted. Server error. */
3902 case 452: /* Action not taken. Insufficient storage space on server. */
3903 case 500: /* Syntax error. Command unrecognized. */
3904 case 501: /* Syntax error. Error in parameters or arguments. */
3905 case 502: /* Command not implemented. */
3906 case 503: /* Bad sequence of commands. */
3907 case 504: /* Command not implemented for that parameter. */
3908 case 532: /* Need account for storing files */
3909 case 550: /* File action not taken. File not found or no access. */
3910 case 551: /* Requested action aborted. Page type unknown */
3911 case 552: /* Action aborted. Exceeded storage allocation */
3912 case 553: /* Action not taken. File name not allowed. */
3914 default:
3915 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3916 break;
3919 INTERNET_SetLastError(dwCode);
3920 return dwCode;