user32/tests: Test MDI child order changing caused by WM_MDINEXT.
[wine/multimedia.git] / dlls / wininet / ftp.c
blob1b67211863926e65d2c86725102c2f2a71e3c904
1 /*
2 * WININET - Ftp implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2004 Mike McCormack for CodeWeavers
6 * Copyright 2004 Kevin Koltzau
7 * Copyright 2007 Hans Leidekker
9 * Ulrich Czekalla
10 * Noureddine Jemmali
12 * Copyright 2000 Andreas Mohr
13 * Copyright 2002 Jaco Greeff
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "config.h"
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
34 #include <ws2tcpip.h>
35 #endif
37 #include <errno.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/types.h>
43 #ifdef HAVE_SYS_SOCKET_H
44 # include <sys/socket.h>
45 #endif
46 #ifdef HAVE_ARPA_INET_H
47 # include <arpa/inet.h>
48 #endif
49 #ifdef HAVE_UNISTD_H
50 # include <unistd.h>
51 #endif
52 #ifdef HAVE_SYS_IOCTL_H
53 # include <sys/ioctl.h>
54 #endif
55 #include <time.h>
56 #include <assert.h>
58 #include "windef.h"
59 #include "winbase.h"
60 #include "wingdi.h"
61 #include "winuser.h"
62 #include "wininet.h"
63 #include "winnls.h"
64 #include "winerror.h"
65 #include "winreg.h"
66 #include "winternl.h"
67 #include "shlwapi.h"
69 #include "wine/debug.h"
70 #include "internet.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
74 typedef struct _ftp_session_t ftp_session_t;
76 typedef struct
78 object_header_t hdr;
79 ftp_session_t *lpFtpSession;
80 BOOL session_deleted;
81 int nDataSocket;
82 WCHAR *cache_file;
83 HANDLE cache_file_handle;
84 } ftp_file_t;
86 struct _ftp_session_t
88 object_header_t hdr;
89 appinfo_t *lpAppInfo;
90 int sndSocket;
91 int lstnSocket;
92 int pasvSocket; /* data socket connected by us in case of passive FTP */
93 ftp_file_t *download_in_progress;
94 struct sockaddr_in socketAddress;
95 struct sockaddr_in lstnSocketAddress;
96 LPWSTR servername;
97 INTERNET_PORT serverport;
98 LPWSTR lpszPassword;
99 LPWSTR lpszUserName;
102 typedef struct
104 BOOL bIsDirectory;
105 LPWSTR lpszName;
106 DWORD nSize;
107 SYSTEMTIME tmLastModified;
108 unsigned short permissions;
109 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
111 typedef struct
113 object_header_t hdr;
114 ftp_session_t *lpFtpSession;
115 DWORD index;
116 DWORD size;
117 LPFILEPROPERTIESW lpafp;
118 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
120 #define DATA_PACKET_SIZE 0x2000
121 #define szCRLF "\r\n"
122 #define MAX_BACKLOG 5
124 /* Testing shows that Windows only accepts dwFlags where the last
125 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
127 #define FTP_CONDITION_MASK 0x0007
129 typedef enum {
130 /* FTP commands with arguments. */
131 FTP_CMD_ACCT,
132 FTP_CMD_CWD,
133 FTP_CMD_DELE,
134 FTP_CMD_MKD,
135 FTP_CMD_PASS,
136 FTP_CMD_PORT,
137 FTP_CMD_RETR,
138 FTP_CMD_RMD,
139 FTP_CMD_RNFR,
140 FTP_CMD_RNTO,
141 FTP_CMD_STOR,
142 FTP_CMD_TYPE,
143 FTP_CMD_USER,
144 FTP_CMD_SIZE,
146 /* FTP commands without arguments. */
147 FTP_CMD_ABOR,
148 FTP_CMD_LIST,
149 FTP_CMD_NLST,
150 FTP_CMD_PASV,
151 FTP_CMD_PWD,
152 FTP_CMD_QUIT,
153 } FTP_COMMAND;
155 static const CHAR *const szFtpCommands[] = {
156 "ACCT",
157 "CWD",
158 "DELE",
159 "MKD",
160 "PASS",
161 "PORT",
162 "RETR",
163 "RMD",
164 "RNFR",
165 "RNTO",
166 "STOR",
167 "TYPE",
168 "USER",
169 "SIZE",
170 "ABOR",
171 "LIST",
172 "NLST",
173 "PASV",
174 "PWD",
175 "QUIT",
178 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
179 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
181 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
182 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext);
183 static BOOL FTP_SendStore(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
184 static BOOL FTP_GetDataSocket(ftp_session_t*, LPINT nDataSocket);
185 static BOOL FTP_SendData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
186 static INT FTP_ReceiveResponse(ftp_session_t*, DWORD_PTR dwContext);
187 static BOOL FTP_SendRetrieve(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
188 static BOOL FTP_RetrieveFileData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
189 static BOOL FTP_InitListenSocket(ftp_session_t*);
190 static BOOL FTP_ConnectToHost(ftp_session_t*);
191 static BOOL FTP_SendPassword(ftp_session_t*);
192 static BOOL FTP_SendAccount(ftp_session_t*);
193 static BOOL FTP_SendType(ftp_session_t*, DWORD dwType);
194 static BOOL FTP_SendPort(ftp_session_t*);
195 static BOOL FTP_DoPassive(ftp_session_t*);
196 static BOOL FTP_SendPortOrPasv(ftp_session_t*);
197 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
198 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
199 static BOOL FTP_ParseDirectory(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
200 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
201 static HINTERNET FTP_ReceiveFileList(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
202 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
203 static DWORD FTP_SetResponseError(DWORD dwResponse);
204 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
205 static BOOL FTP_FtpPutFileW(ftp_session_t*, LPCWSTR lpszLocalFile,
206 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
207 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
208 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
209 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t*,
210 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
211 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t*, LPWSTR lpszCurrentDirectory,
212 LPDWORD lpdwCurrentDirectory);
213 static BOOL FTP_FtpRenameFileW(ftp_session_t*, LPCWSTR lpszSrc, LPCWSTR lpszDest);
214 static BOOL FTP_FtpRemoveDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
215 static BOOL FTP_FtpDeleteFileW(ftp_session_t*, LPCWSTR lpszFileName);
216 static BOOL FTP_FtpGetFileW(ftp_session_t*, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
217 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
218 DWORD_PTR dwContext);
220 /* A temporary helper until we get rid of INTERNET_GetLastError calls */
221 static BOOL res_to_le(DWORD res)
223 if(res != ERROR_SUCCESS)
224 INTERNET_SetLastError(res);
225 return res == ERROR_SUCCESS;
228 /***********************************************************************
229 * FtpPutFileA (WININET.@)
231 * Uploads a file to the FTP server
233 * RETURNS
234 * TRUE on success
235 * FALSE on failure
238 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
239 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
241 LPWSTR lpwzLocalFile;
242 LPWSTR lpwzNewRemoteFile;
243 BOOL ret;
245 lpwzLocalFile = heap_strdupAtoW(lpszLocalFile);
246 lpwzNewRemoteFile = heap_strdupAtoW(lpszNewRemoteFile);
247 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
248 dwFlags, dwContext);
249 heap_free(lpwzLocalFile);
250 heap_free(lpwzNewRemoteFile);
251 return ret;
254 typedef struct {
255 task_header_t hdr;
256 WCHAR *local_file;
257 WCHAR *remote_file;
258 DWORD flags;
259 DWORD_PTR context;
260 } put_file_task_t;
262 static void AsyncFtpPutFileProc(task_header_t *hdr)
264 put_file_task_t *task = (put_file_task_t*)hdr;
265 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
267 TRACE("%p\n", session);
269 FTP_FtpPutFileW(session, task->local_file, task->remote_file,
270 task->flags, task->context);
272 heap_free(task->local_file);
273 heap_free(task->remote_file);
276 /***********************************************************************
277 * FtpPutFileW (WININET.@)
279 * Uploads a file to the FTP server
281 * RETURNS
282 * TRUE on success
283 * FALSE on failure
286 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
287 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
289 ftp_session_t *lpwfs;
290 appinfo_t *hIC = NULL;
291 BOOL r = FALSE;
293 if (!lpszLocalFile || !lpszNewRemoteFile)
295 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
296 return FALSE;
299 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
300 if (!lpwfs)
302 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
303 return FALSE;
306 if (WH_HFTPSESSION != lpwfs->hdr.htype)
308 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
309 goto lend;
312 if (lpwfs->download_in_progress != NULL)
314 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
315 goto lend;
318 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
320 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
321 goto lend;
324 hIC = lpwfs->lpAppInfo;
325 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
327 put_file_task_t *task = alloc_async_task(&lpwfs->hdr, AsyncFtpPutFileProc, sizeof(*task));
329 task->local_file = heap_strdupW(lpszLocalFile);
330 task->remote_file = heap_strdupW(lpszNewRemoteFile);
331 task->flags = dwFlags;
332 task->context = dwContext;
334 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
336 else
338 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
339 lpszNewRemoteFile, dwFlags, dwContext);
342 lend:
343 WININET_Release( &lpwfs->hdr );
345 return r;
348 /***********************************************************************
349 * FTP_FtpPutFileW (Internal)
351 * Uploads a file to the FTP server
353 * RETURNS
354 * TRUE on success
355 * FALSE on failure
358 static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
359 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
361 HANDLE hFile;
362 BOOL bSuccess = FALSE;
363 appinfo_t *hIC = NULL;
364 INT nResCode;
366 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
368 /* Clear any error information */
369 INTERNET_SetLastError(0);
371 /* Open file to be uploaded */
372 if (INVALID_HANDLE_VALUE ==
373 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
374 /* Let CreateFile set the appropriate error */
375 return FALSE;
377 hIC = lpwfs->lpAppInfo;
379 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
381 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
383 INT nDataSocket;
385 /* Get data socket to server */
386 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
388 FTP_SendData(lpwfs, nDataSocket, hFile);
389 closesocket(nDataSocket);
390 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
391 if (nResCode)
393 if (nResCode == 226)
394 bSuccess = TRUE;
395 else
396 FTP_SetResponseError(nResCode);
401 if (lpwfs->lstnSocket != -1)
403 closesocket(lpwfs->lstnSocket);
404 lpwfs->lstnSocket = -1;
407 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
409 INTERNET_ASYNC_RESULT iar;
411 iar.dwResult = (DWORD)bSuccess;
412 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
413 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
414 &iar, sizeof(INTERNET_ASYNC_RESULT));
417 CloseHandle(hFile);
419 return bSuccess;
423 /***********************************************************************
424 * FtpSetCurrentDirectoryA (WININET.@)
426 * Change the working directory on the FTP server
428 * RETURNS
429 * TRUE on success
430 * FALSE on failure
433 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
435 LPWSTR lpwzDirectory;
436 BOOL ret;
438 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
439 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
440 heap_free(lpwzDirectory);
441 return ret;
444 typedef struct {
445 task_header_t hdr;
446 WCHAR *directory;
447 } directory_task_t;
449 static void AsyncFtpSetCurrentDirectoryProc(task_header_t *hdr)
451 directory_task_t *task = (directory_task_t*)hdr;
452 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
454 TRACE("%p\n", session);
456 FTP_FtpSetCurrentDirectoryW(session, task->directory);
457 heap_free(task->directory);
460 /***********************************************************************
461 * FtpSetCurrentDirectoryW (WININET.@)
463 * Change the working directory on the FTP server
465 * RETURNS
466 * TRUE on success
467 * FALSE on failure
470 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
472 ftp_session_t *lpwfs = NULL;
473 appinfo_t *hIC = NULL;
474 BOOL r = FALSE;
476 if (!lpszDirectory)
478 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
479 goto lend;
482 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
483 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
485 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
486 goto lend;
489 if (lpwfs->download_in_progress != NULL)
491 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
492 goto lend;
495 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
497 hIC = lpwfs->lpAppInfo;
498 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
500 directory_task_t *task;
502 task = alloc_async_task(&lpwfs->hdr, AsyncFtpSetCurrentDirectoryProc, sizeof(*task));
503 task->directory = heap_strdupW(lpszDirectory);
505 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
507 else
509 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
512 lend:
513 if( lpwfs )
514 WININET_Release( &lpwfs->hdr );
516 return r;
520 /***********************************************************************
521 * FTP_FtpSetCurrentDirectoryW (Internal)
523 * Change the working directory on the FTP server
525 * RETURNS
526 * TRUE on success
527 * FALSE on failure
530 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
532 INT nResCode;
533 appinfo_t *hIC = NULL;
534 DWORD bSuccess = FALSE;
536 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
538 /* Clear any error information */
539 INTERNET_SetLastError(0);
541 hIC = lpwfs->lpAppInfo;
542 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
543 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
544 goto lend;
546 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
548 if (nResCode)
550 if (nResCode == 250)
551 bSuccess = TRUE;
552 else
553 FTP_SetResponseError(nResCode);
556 lend:
557 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
559 INTERNET_ASYNC_RESULT iar;
561 iar.dwResult = bSuccess;
562 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
563 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
564 &iar, sizeof(INTERNET_ASYNC_RESULT));
566 return bSuccess;
570 /***********************************************************************
571 * FtpCreateDirectoryA (WININET.@)
573 * Create new directory on the FTP server
575 * RETURNS
576 * TRUE on success
577 * FALSE on failure
580 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
582 LPWSTR lpwzDirectory;
583 BOOL ret;
585 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
586 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
587 heap_free(lpwzDirectory);
588 return ret;
592 static void AsyncFtpCreateDirectoryProc(task_header_t *hdr)
594 directory_task_t *task = (directory_task_t*)hdr;
595 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
597 TRACE(" %p\n", session);
599 FTP_FtpCreateDirectoryW(session, task->directory);
600 heap_free(task->directory);
603 /***********************************************************************
604 * FtpCreateDirectoryW (WININET.@)
606 * Create new directory on the FTP server
608 * RETURNS
609 * TRUE on success
610 * FALSE on failure
613 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
615 ftp_session_t *lpwfs;
616 appinfo_t *hIC = NULL;
617 BOOL r = FALSE;
619 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
620 if (!lpwfs)
622 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
623 return FALSE;
626 if (WH_HFTPSESSION != lpwfs->hdr.htype)
628 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
629 goto lend;
632 if (lpwfs->download_in_progress != NULL)
634 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
635 goto lend;
638 if (!lpszDirectory)
640 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
641 goto lend;
644 hIC = lpwfs->lpAppInfo;
645 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
647 directory_task_t *task;
649 task = alloc_async_task(&lpwfs->hdr, AsyncFtpCreateDirectoryProc, sizeof(*task));
650 task->directory = heap_strdupW(lpszDirectory);
652 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
654 else
656 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
658 lend:
659 WININET_Release( &lpwfs->hdr );
661 return r;
665 /***********************************************************************
666 * FTP_FtpCreateDirectoryW (Internal)
668 * Create new directory on the FTP server
670 * RETURNS
671 * TRUE on success
672 * FALSE on failure
675 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
677 INT nResCode;
678 BOOL bSuccess = FALSE;
679 appinfo_t *hIC = NULL;
681 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
683 /* Clear any error information */
684 INTERNET_SetLastError(0);
686 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
687 goto lend;
689 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
690 if (nResCode)
692 if (nResCode == 257)
693 bSuccess = TRUE;
694 else
695 FTP_SetResponseError(nResCode);
698 lend:
699 hIC = lpwfs->lpAppInfo;
700 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
702 INTERNET_ASYNC_RESULT iar;
704 iar.dwResult = (DWORD)bSuccess;
705 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
706 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
707 &iar, sizeof(INTERNET_ASYNC_RESULT));
710 return bSuccess;
713 /***********************************************************************
714 * FtpFindFirstFileA (WININET.@)
716 * Search the specified directory
718 * RETURNS
719 * HINTERNET on success
720 * NULL on failure
723 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
724 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
726 LPWSTR lpwzSearchFile;
727 WIN32_FIND_DATAW wfd;
728 LPWIN32_FIND_DATAW lpFindFileDataW;
729 HINTERNET ret;
731 lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
732 lpFindFileDataW = lpFindFileData?&wfd:NULL;
733 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
734 heap_free(lpwzSearchFile);
736 if (ret && lpFindFileData)
737 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
739 return ret;
742 typedef struct {
743 task_header_t hdr;
744 WCHAR *search_file;
745 WIN32_FIND_DATAW *find_file_data;
746 DWORD flags;
747 DWORD_PTR context;
748 } find_first_file_task_t;
750 static void AsyncFtpFindFirstFileProc(task_header_t *hdr)
752 find_first_file_task_t *task = (find_first_file_task_t*)hdr;
753 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
755 TRACE("%p\n", session);
757 FTP_FtpFindFirstFileW(session, task->search_file, task->find_file_data, task->flags, task->context);
758 heap_free(task->search_file);
761 /***********************************************************************
762 * FtpFindFirstFileW (WININET.@)
764 * Search the specified directory
766 * RETURNS
767 * HINTERNET on success
768 * NULL on failure
771 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
772 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
774 ftp_session_t *lpwfs;
775 appinfo_t *hIC = NULL;
776 HINTERNET r = NULL;
778 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
779 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
781 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
782 goto lend;
785 if (lpwfs->download_in_progress != NULL)
787 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
788 goto lend;
791 hIC = lpwfs->lpAppInfo;
792 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
794 find_first_file_task_t *task;
796 task = alloc_async_task(&lpwfs->hdr, AsyncFtpFindFirstFileProc, sizeof(*task));
797 task->search_file = heap_strdupW(lpszSearchFile);
798 task->find_file_data = lpFindFileData;
799 task->flags = dwFlags;
800 task->context = dwContext;
802 INTERNET_AsyncCall(&task->hdr);
803 r = NULL;
805 else
807 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
808 dwFlags, dwContext);
810 lend:
811 if( lpwfs )
812 WININET_Release( &lpwfs->hdr );
814 return r;
818 /***********************************************************************
819 * FTP_FtpFindFirstFileW (Internal)
821 * Search the specified directory
823 * RETURNS
824 * HINTERNET on success
825 * NULL on failure
828 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
829 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
831 INT nResCode;
832 appinfo_t *hIC = NULL;
833 HINTERNET hFindNext = NULL;
835 TRACE("\n");
837 /* Clear any error information */
838 INTERNET_SetLastError(0);
840 if (!FTP_InitListenSocket(lpwfs))
841 goto lend;
843 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
844 goto lend;
846 if (!FTP_SendPortOrPasv(lpwfs))
847 goto lend;
849 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
850 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
851 goto lend;
853 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
854 if (nResCode)
856 if (nResCode == 125 || nResCode == 150)
858 INT nDataSocket;
860 /* Get data socket to server */
861 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
863 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
864 closesocket(nDataSocket);
865 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
866 if (nResCode != 226 && nResCode != 250)
867 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
870 else
871 FTP_SetResponseError(nResCode);
874 lend:
875 if (lpwfs->lstnSocket != -1)
877 closesocket(lpwfs->lstnSocket);
878 lpwfs->lstnSocket = -1;
881 hIC = lpwfs->lpAppInfo;
882 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
884 INTERNET_ASYNC_RESULT iar;
886 if (hFindNext)
888 iar.dwResult = (DWORD_PTR)hFindNext;
889 iar.dwError = ERROR_SUCCESS;
890 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
891 &iar, sizeof(INTERNET_ASYNC_RESULT));
894 iar.dwResult = (DWORD_PTR)hFindNext;
895 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
896 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
897 &iar, sizeof(INTERNET_ASYNC_RESULT));
900 return hFindNext;
904 /***********************************************************************
905 * FtpGetCurrentDirectoryA (WININET.@)
907 * Retrieves the current directory
909 * RETURNS
910 * TRUE on success
911 * FALSE on failure
914 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
915 LPDWORD lpdwCurrentDirectory)
917 WCHAR *dir = NULL;
918 DWORD len;
919 BOOL ret;
921 if(lpdwCurrentDirectory) {
922 len = *lpdwCurrentDirectory;
923 if(lpszCurrentDirectory)
925 dir = heap_alloc(len * sizeof(WCHAR));
926 if (NULL == dir)
928 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
929 return FALSE;
933 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
935 if (ret && lpszCurrentDirectory)
936 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
938 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
939 heap_free(dir);
940 return ret;
943 typedef struct {
944 task_header_t hdr;
945 WCHAR *directory;
946 DWORD *directory_len;
947 } get_current_dir_task_t;
949 static void AsyncFtpGetCurrentDirectoryProc(task_header_t *hdr)
951 get_current_dir_task_t *task = (get_current_dir_task_t*)hdr;
952 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
954 TRACE("%p\n", session);
956 FTP_FtpGetCurrentDirectoryW(session, task->directory, task->directory_len);
959 /***********************************************************************
960 * FtpGetCurrentDirectoryW (WININET.@)
962 * Retrieves the current directory
964 * RETURNS
965 * TRUE on success
966 * FALSE on failure
969 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
970 LPDWORD lpdwCurrentDirectory)
972 ftp_session_t *lpwfs;
973 appinfo_t *hIC = NULL;
974 BOOL r = FALSE;
976 TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
978 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
979 if (NULL == lpwfs)
981 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
982 goto lend;
985 if (WH_HFTPSESSION != lpwfs->hdr.htype)
987 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
988 goto lend;
991 if (!lpdwCurrentDirectory)
993 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
994 goto lend;
997 if (lpszCurrentDirectory == NULL)
999 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1000 goto lend;
1003 if (lpwfs->download_in_progress != NULL)
1005 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1006 goto lend;
1009 hIC = lpwfs->lpAppInfo;
1010 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1012 get_current_dir_task_t *task;
1014 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetCurrentDirectoryProc, sizeof(*task));
1015 task->directory = lpszCurrentDirectory;
1016 task->directory_len = lpdwCurrentDirectory;
1018 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1020 else
1022 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
1023 lpdwCurrentDirectory);
1026 lend:
1027 if( lpwfs )
1028 WININET_Release( &lpwfs->hdr );
1030 return r;
1034 /***********************************************************************
1035 * FTP_FtpGetCurrentDirectoryW (Internal)
1037 * Retrieves the current directory
1039 * RETURNS
1040 * TRUE on success
1041 * FALSE on failure
1044 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
1045 LPDWORD lpdwCurrentDirectory)
1047 INT nResCode;
1048 appinfo_t *hIC = NULL;
1049 DWORD bSuccess = FALSE;
1051 /* Clear any error information */
1052 INTERNET_SetLastError(0);
1054 hIC = lpwfs->lpAppInfo;
1055 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1056 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1057 goto lend;
1059 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1060 if (nResCode)
1062 if (nResCode == 257) /* Extract directory name */
1064 DWORD firstpos, lastpos, len;
1065 LPWSTR lpszResponseBuffer = heap_strdupAtoW(INTERNET_GetResponseBuffer());
1067 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1069 if ('"' == lpszResponseBuffer[lastpos])
1071 if (!firstpos)
1072 firstpos = lastpos;
1073 else
1074 break;
1077 len = lastpos - firstpos;
1078 if (*lpdwCurrentDirectory >= len)
1080 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1081 lpszCurrentDirectory[len - 1] = 0;
1082 *lpdwCurrentDirectory = len;
1083 bSuccess = TRUE;
1085 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1087 heap_free(lpszResponseBuffer);
1089 else
1090 FTP_SetResponseError(nResCode);
1093 lend:
1094 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1096 INTERNET_ASYNC_RESULT iar;
1098 iar.dwResult = bSuccess;
1099 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1100 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1101 &iar, sizeof(INTERNET_ASYNC_RESULT));
1104 return bSuccess;
1108 /***********************************************************************
1109 * FTPFILE_Destroy(internal)
1111 * Closes the file transfer handle. This also 'cleans' the data queue of
1112 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1115 static void FTPFILE_Destroy(object_header_t *hdr)
1117 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1118 ftp_session_t *lpwfs = lpwh->lpFtpSession;
1119 INT nResCode;
1121 TRACE("\n");
1123 if (lpwh->cache_file_handle != INVALID_HANDLE_VALUE)
1124 CloseHandle(lpwh->cache_file_handle);
1126 heap_free(lpwh->cache_file);
1128 if (!lpwh->session_deleted)
1129 lpwfs->download_in_progress = NULL;
1131 if (lpwh->nDataSocket != -1)
1132 closesocket(lpwh->nDataSocket);
1134 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1135 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1137 WININET_Release(&lpwh->lpFtpSession->hdr);
1140 static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1142 switch(option) {
1143 case INTERNET_OPTION_HANDLE_TYPE:
1144 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1146 if (*size < sizeof(ULONG))
1147 return ERROR_INSUFFICIENT_BUFFER;
1149 *size = sizeof(DWORD);
1150 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1151 return ERROR_SUCCESS;
1152 case INTERNET_OPTION_DATAFILE_NAME:
1154 DWORD required;
1155 ftp_file_t *file = (ftp_file_t *)hdr;
1157 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1159 if (!file->cache_file)
1161 *size = 0;
1162 return ERROR_INTERNET_ITEM_NOT_FOUND;
1164 if (unicode)
1166 required = (lstrlenW(file->cache_file) + 1) * sizeof(WCHAR);
1167 if (*size < required)
1168 return ERROR_INSUFFICIENT_BUFFER;
1170 *size = required;
1171 memcpy(buffer, file->cache_file, *size);
1172 return ERROR_SUCCESS;
1174 else
1176 required = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, NULL, 0, NULL, NULL);
1177 if (required > *size)
1178 return ERROR_INSUFFICIENT_BUFFER;
1180 *size = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, buffer, *size, NULL, NULL);
1181 return ERROR_SUCCESS;
1185 return INET_QueryOption(hdr, option, buffer, size, unicode);
1188 static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
1190 ftp_file_t *file = (ftp_file_t*)hdr;
1191 int res;
1192 DWORD error;
1194 if (file->nDataSocket == -1)
1195 return ERROR_INTERNET_DISCONNECTED;
1197 /* FIXME: FTP should use NETCON_ stuff */
1198 res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1199 *read = res>0 ? res : 0;
1201 error = res >= 0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME */
1202 if (error == ERROR_SUCCESS && file->cache_file)
1204 DWORD bytes_written;
1206 if (!WriteFile(file->cache_file_handle, buffer, *read, &bytes_written, NULL))
1207 WARN("WriteFile failed: %u\n", GetLastError());
1209 return error;
1212 static DWORD FTPFILE_ReadFileEx(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_size,
1213 DWORD flags, DWORD_PTR context)
1215 return FTPFILE_ReadFile(hdr, buf, size, ret_size);
1218 static DWORD FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1220 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1221 int res;
1223 res = send(lpwh->nDataSocket, buffer, size, 0);
1225 *written = res>0 ? res : 0;
1226 return res >= 0 ? ERROR_SUCCESS : sock_get_error(errno);
1229 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
1231 INTERNET_ASYNC_RESULT iar;
1232 BYTE buffer[4096];
1233 int available;
1235 TRACE("%p\n", file);
1237 available = recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1239 if(available != -1) {
1240 iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1241 iar.dwError = first_notif ? 0 : available;
1242 }else {
1243 iar.dwResult = 0;
1244 iar.dwError = INTERNET_GetLastError();
1247 INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1248 sizeof(INTERNET_ASYNC_RESULT));
1251 static void FTPFILE_AsyncQueryDataAvailableProc(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 = 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;
1295 static const object_vtbl_t FTPFILEVtbl = {
1296 FTPFILE_Destroy,
1297 NULL,
1298 FTPFILE_QueryOption,
1299 INET_SetOption,
1300 FTPFILE_ReadFile,
1301 FTPFILE_ReadFileEx,
1302 FTPFILE_WriteFile,
1303 FTPFILE_QueryDataAvailable,
1304 NULL
1307 /***********************************************************************
1308 * FTP_FtpOpenFileW (Internal)
1310 * Open a remote file for writing or reading
1312 * RETURNS
1313 * HINTERNET handle on success
1314 * NULL on failure
1317 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1318 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1319 DWORD_PTR dwContext)
1321 INT nDataSocket;
1322 BOOL bSuccess = FALSE;
1323 ftp_file_t *lpwh = NULL;
1324 appinfo_t *hIC = NULL;
1326 TRACE("\n");
1328 /* Clear any error information */
1329 INTERNET_SetLastError(0);
1331 if (GENERIC_READ == fdwAccess)
1333 /* Set up socket to retrieve data */
1334 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1336 else if (GENERIC_WRITE == fdwAccess)
1338 /* Set up socket to send data */
1339 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1342 /* Get data socket to server */
1343 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1345 lpwh = alloc_object(&lpwfs->hdr, &FTPFILEVtbl, sizeof(ftp_file_t));
1346 lpwh->hdr.htype = WH_HFILE;
1347 lpwh->hdr.dwFlags = dwFlags;
1348 lpwh->hdr.dwContext = dwContext;
1349 lpwh->nDataSocket = nDataSocket;
1350 lpwh->cache_file = NULL;
1351 lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1352 lpwh->session_deleted = FALSE;
1354 WININET_AddRef( &lpwfs->hdr );
1355 lpwh->lpFtpSession = lpwfs;
1356 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1358 /* Indicate that a download is currently in progress */
1359 lpwfs->download_in_progress = lpwh;
1362 if (lpwfs->lstnSocket != -1)
1364 closesocket(lpwfs->lstnSocket);
1365 lpwfs->lstnSocket = -1;
1368 if (bSuccess && fdwAccess == GENERIC_READ)
1370 WCHAR filename[MAX_PATH + 1];
1371 URL_COMPONENTSW uc;
1372 DWORD len;
1374 memset(&uc, 0, sizeof(uc));
1375 uc.dwStructSize = sizeof(uc);
1376 uc.nScheme = INTERNET_SCHEME_FTP;
1377 uc.lpszHostName = lpwfs->servername;
1378 uc.nPort = lpwfs->serverport;
1379 uc.lpszUserName = lpwfs->lpszUserName;
1380 uc.lpszUrlPath = heap_strdupW(lpszFileName);
1382 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1384 WCHAR *url = heap_alloc(len * sizeof(WCHAR));
1386 if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1388 lpwh->cache_file = heap_strdupW(filename);
1389 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1390 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1391 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1393 WARN("Could not create cache file: %u\n", GetLastError());
1394 heap_free(lpwh->cache_file);
1395 lpwh->cache_file = NULL;
1398 heap_free(url);
1400 heap_free(uc.lpszUrlPath);
1403 hIC = lpwfs->lpAppInfo;
1404 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1406 INTERNET_ASYNC_RESULT iar;
1408 if (lpwh)
1410 iar.dwResult = (DWORD_PTR)lpwh->hdr.hInternet;
1411 iar.dwError = ERROR_SUCCESS;
1412 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1413 &iar, sizeof(INTERNET_ASYNC_RESULT));
1416 if(bSuccess) {
1417 FTP_ReceiveRequestData(lpwh, TRUE);
1418 }else {
1419 iar.dwResult = 0;
1420 iar.dwError = INTERNET_GetLastError();
1421 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1422 &iar, sizeof(INTERNET_ASYNC_RESULT));
1426 if(!bSuccess)
1427 return FALSE;
1429 return lpwh->hdr.hInternet;
1433 /***********************************************************************
1434 * FtpOpenFileA (WININET.@)
1436 * Open a remote file for writing or reading
1438 * RETURNS
1439 * HINTERNET handle on success
1440 * NULL on failure
1443 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1444 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1445 DWORD_PTR dwContext)
1447 LPWSTR lpwzFileName;
1448 HINTERNET ret;
1450 lpwzFileName = heap_strdupAtoW(lpszFileName);
1451 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1452 heap_free(lpwzFileName);
1453 return ret;
1456 typedef struct {
1457 task_header_t hdr;
1458 WCHAR *file_name;
1459 DWORD access;
1460 DWORD flags;
1461 DWORD_PTR context;
1462 } open_file_task_t;
1464 static void AsyncFtpOpenFileProc(task_header_t *hdr)
1466 open_file_task_t *task = (open_file_task_t*)hdr;
1467 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1469 TRACE("%p\n", session);
1471 FTP_FtpOpenFileW(session, task->file_name, task->access, task->flags, task->context);
1472 heap_free(task->file_name);
1475 /***********************************************************************
1476 * FtpOpenFileW (WININET.@)
1478 * Open a remote file for writing or reading
1480 * RETURNS
1481 * HINTERNET handle on success
1482 * NULL on failure
1485 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1486 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1487 DWORD_PTR dwContext)
1489 ftp_session_t *lpwfs;
1490 appinfo_t *hIC = NULL;
1491 HINTERNET r = NULL;
1493 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1494 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1496 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1497 if (!lpwfs)
1499 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1500 return FALSE;
1503 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1505 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1506 goto lend;
1509 if ((!lpszFileName) ||
1510 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1511 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1513 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1514 goto lend;
1517 if (lpwfs->download_in_progress != NULL)
1519 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1520 goto lend;
1523 hIC = lpwfs->lpAppInfo;
1524 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1526 open_file_task_t *task;
1528 task = alloc_async_task(&lpwfs->hdr, AsyncFtpOpenFileProc, sizeof(*task));
1529 task->file_name = heap_strdupW(lpszFileName);
1530 task->access = fdwAccess;
1531 task->flags = dwFlags;
1532 task->context = dwContext;
1534 INTERNET_AsyncCall(&task->hdr);
1535 r = NULL;
1537 else
1539 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1542 lend:
1543 WININET_Release( &lpwfs->hdr );
1545 return r;
1549 /***********************************************************************
1550 * FtpGetFileA (WININET.@)
1552 * Retrieve file from the FTP server
1554 * RETURNS
1555 * TRUE on success
1556 * FALSE on failure
1559 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1560 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1561 DWORD_PTR dwContext)
1563 LPWSTR lpwzRemoteFile;
1564 LPWSTR lpwzNewFile;
1565 BOOL ret;
1567 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1568 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1569 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1570 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1571 heap_free(lpwzRemoteFile);
1572 heap_free(lpwzNewFile);
1573 return ret;
1576 typedef struct {
1577 task_header_t hdr;
1578 WCHAR *remote_file;
1579 WCHAR *new_file;
1580 BOOL fail_if_exists;
1581 DWORD local_attr;
1582 DWORD flags;
1583 DWORD_PTR context;
1584 } get_file_task_t;
1586 static void AsyncFtpGetFileProc(task_header_t *hdr)
1588 get_file_task_t *task = (get_file_task_t*)hdr;
1589 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1591 TRACE("%p\n", session);
1593 FTP_FtpGetFileW(session, task->remote_file, task->new_file, task->fail_if_exists,
1594 task->local_attr, task->flags, task->context);
1595 heap_free(task->remote_file);
1596 heap_free(task->new_file);
1600 /***********************************************************************
1601 * FtpGetFileW (WININET.@)
1603 * Retrieve file from the FTP server
1605 * RETURNS
1606 * TRUE on success
1607 * FALSE on failure
1610 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1611 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1612 DWORD_PTR dwContext)
1614 ftp_session_t *lpwfs;
1615 appinfo_t *hIC = NULL;
1616 BOOL r = FALSE;
1618 if (!lpszRemoteFile || !lpszNewFile)
1620 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1621 return FALSE;
1624 lpwfs = (ftp_session_t*) get_handle_object( hInternet );
1625 if (!lpwfs)
1627 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1628 return FALSE;
1631 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1633 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1634 goto lend;
1637 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1639 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1640 goto lend;
1643 if (lpwfs->download_in_progress != NULL)
1645 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1646 goto lend;
1649 hIC = lpwfs->lpAppInfo;
1650 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1652 get_file_task_t *task;
1654 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetFileProc, sizeof(*task));
1655 task->remote_file = heap_strdupW(lpszRemoteFile);
1656 task->new_file = heap_strdupW(lpszNewFile);
1657 task->local_attr = dwLocalFlagsAttribute;
1658 task->fail_if_exists = fFailIfExists;
1659 task->flags = dwInternetFlags;
1660 task->context = dwContext;
1662 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1664 else
1666 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1667 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1670 lend:
1671 WININET_Release( &lpwfs->hdr );
1673 return r;
1677 /***********************************************************************
1678 * FTP_FtpGetFileW (Internal)
1680 * Retrieve file from the FTP server
1682 * RETURNS
1683 * TRUE on success
1684 * FALSE on failure
1687 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1688 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1689 DWORD_PTR dwContext)
1691 BOOL bSuccess = FALSE;
1692 HANDLE hFile;
1693 appinfo_t *hIC = NULL;
1695 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1697 /* Clear any error information */
1698 INTERNET_SetLastError(0);
1700 /* Ensure we can write to lpszNewfile by opening it */
1701 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1702 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1703 if (INVALID_HANDLE_VALUE == hFile)
1704 return FALSE;
1706 /* Set up socket to retrieve data */
1707 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1709 INT nDataSocket;
1711 /* Get data socket to server */
1712 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1714 INT nResCode;
1716 /* Receive data */
1717 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1718 closesocket(nDataSocket);
1720 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1721 if (nResCode)
1723 if (nResCode == 226)
1724 bSuccess = TRUE;
1725 else
1726 FTP_SetResponseError(nResCode);
1731 if (lpwfs->lstnSocket != -1)
1733 closesocket(lpwfs->lstnSocket);
1734 lpwfs->lstnSocket = -1;
1737 CloseHandle(hFile);
1739 hIC = lpwfs->lpAppInfo;
1740 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1742 INTERNET_ASYNC_RESULT iar;
1744 iar.dwResult = (DWORD)bSuccess;
1745 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1746 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1747 &iar, sizeof(INTERNET_ASYNC_RESULT));
1750 if (!bSuccess) DeleteFileW(lpszNewFile);
1751 return bSuccess;
1754 /***********************************************************************
1755 * FtpGetFileSize (WININET.@)
1757 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1759 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1761 if (lpdwFileSizeHigh)
1762 *lpdwFileSizeHigh = 0;
1764 return 0;
1767 /***********************************************************************
1768 * FtpDeleteFileA (WININET.@)
1770 * Delete a file on the ftp server
1772 * RETURNS
1773 * TRUE on success
1774 * FALSE on failure
1777 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1779 LPWSTR lpwzFileName;
1780 BOOL ret;
1782 lpwzFileName = heap_strdupAtoW(lpszFileName);
1783 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1784 heap_free(lpwzFileName);
1785 return ret;
1788 typedef struct {
1789 task_header_t hdr;
1790 WCHAR *file_name;
1791 } delete_file_task_t;
1793 static void AsyncFtpDeleteFileProc(task_header_t *hdr)
1795 delete_file_task_t *task = (delete_file_task_t*)hdr;
1796 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1798 TRACE("%p\n", session);
1800 FTP_FtpDeleteFileW(session, task->file_name);
1801 heap_free(task->file_name);
1804 /***********************************************************************
1805 * FtpDeleteFileW (WININET.@)
1807 * Delete a file on the ftp server
1809 * RETURNS
1810 * TRUE on success
1811 * FALSE on failure
1814 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1816 ftp_session_t *lpwfs;
1817 appinfo_t *hIC = NULL;
1818 BOOL r = FALSE;
1820 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1821 if (!lpwfs)
1823 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1824 return FALSE;
1827 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1829 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1830 goto lend;
1833 if (lpwfs->download_in_progress != NULL)
1835 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1836 goto lend;
1839 if (!lpszFileName)
1841 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1842 goto lend;
1845 hIC = lpwfs->lpAppInfo;
1846 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1848 delete_file_task_t *task;
1850 task = alloc_async_task(&lpwfs->hdr, AsyncFtpDeleteFileProc, sizeof(*task));
1851 task->file_name = heap_strdupW(lpszFileName);
1853 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1855 else
1857 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1860 lend:
1861 WININET_Release( &lpwfs->hdr );
1863 return r;
1866 /***********************************************************************
1867 * FTP_FtpDeleteFileW (Internal)
1869 * Delete a file on the ftp server
1871 * RETURNS
1872 * TRUE on success
1873 * FALSE on failure
1876 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1878 INT nResCode;
1879 BOOL bSuccess = FALSE;
1880 appinfo_t *hIC = NULL;
1882 TRACE("%p\n", lpwfs);
1884 /* Clear any error information */
1885 INTERNET_SetLastError(0);
1887 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1888 goto lend;
1890 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1891 if (nResCode)
1893 if (nResCode == 250)
1894 bSuccess = TRUE;
1895 else
1896 FTP_SetResponseError(nResCode);
1898 lend:
1899 hIC = lpwfs->lpAppInfo;
1900 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1902 INTERNET_ASYNC_RESULT iar;
1904 iar.dwResult = (DWORD)bSuccess;
1905 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1906 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1907 &iar, sizeof(INTERNET_ASYNC_RESULT));
1910 return bSuccess;
1914 /***********************************************************************
1915 * FtpRemoveDirectoryA (WININET.@)
1917 * Remove a directory on the ftp server
1919 * RETURNS
1920 * TRUE on success
1921 * FALSE on failure
1924 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1926 LPWSTR lpwzDirectory;
1927 BOOL ret;
1929 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1930 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1931 heap_free(lpwzDirectory);
1932 return ret;
1935 static void AsyncFtpRemoveDirectoryProc(task_header_t *hdr)
1937 directory_task_t *task = (directory_task_t*)hdr;
1938 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1940 TRACE("%p\n", session);
1942 FTP_FtpRemoveDirectoryW(session, task->directory);
1943 heap_free(task->directory);
1946 /***********************************************************************
1947 * FtpRemoveDirectoryW (WININET.@)
1949 * Remove a directory on the ftp server
1951 * RETURNS
1952 * TRUE on success
1953 * FALSE on failure
1956 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1958 ftp_session_t *lpwfs;
1959 appinfo_t *hIC = NULL;
1960 BOOL r = FALSE;
1962 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1963 if (!lpwfs)
1965 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1966 return FALSE;
1969 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1971 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1972 goto lend;
1975 if (lpwfs->download_in_progress != NULL)
1977 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1978 goto lend;
1981 if (!lpszDirectory)
1983 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1984 goto lend;
1987 hIC = lpwfs->lpAppInfo;
1988 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1990 directory_task_t *task;
1992 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRemoveDirectoryProc, sizeof(*task));
1993 task->directory = heap_strdupW(lpszDirectory);
1995 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1997 else
1999 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
2002 lend:
2003 WININET_Release( &lpwfs->hdr );
2005 return r;
2008 /***********************************************************************
2009 * FTP_FtpRemoveDirectoryW (Internal)
2011 * Remove a directory on the ftp server
2013 * RETURNS
2014 * TRUE on success
2015 * FALSE on failure
2018 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
2020 INT nResCode;
2021 BOOL bSuccess = FALSE;
2022 appinfo_t *hIC = NULL;
2024 TRACE("\n");
2026 /* Clear any error information */
2027 INTERNET_SetLastError(0);
2029 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2030 goto lend;
2032 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2033 if (nResCode)
2035 if (nResCode == 250)
2036 bSuccess = TRUE;
2037 else
2038 FTP_SetResponseError(nResCode);
2041 lend:
2042 hIC = lpwfs->lpAppInfo;
2043 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2045 INTERNET_ASYNC_RESULT iar;
2047 iar.dwResult = (DWORD)bSuccess;
2048 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2049 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2050 &iar, sizeof(INTERNET_ASYNC_RESULT));
2053 return bSuccess;
2057 /***********************************************************************
2058 * FtpRenameFileA (WININET.@)
2060 * Rename a file on the ftp server
2062 * RETURNS
2063 * TRUE on success
2064 * FALSE on failure
2067 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2069 LPWSTR lpwzSrc;
2070 LPWSTR lpwzDest;
2071 BOOL ret;
2073 lpwzSrc = heap_strdupAtoW(lpszSrc);
2074 lpwzDest = heap_strdupAtoW(lpszDest);
2075 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2076 heap_free(lpwzSrc);
2077 heap_free(lpwzDest);
2078 return ret;
2081 typedef struct {
2082 task_header_t hdr;
2083 WCHAR *src_file;
2084 WCHAR *dst_file;
2085 } rename_file_task_t;
2087 static void AsyncFtpRenameFileProc(task_header_t *hdr)
2089 rename_file_task_t *task = (rename_file_task_t*)hdr;
2090 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
2092 TRACE("%p\n", session);
2094 FTP_FtpRenameFileW(session, task->src_file, task->dst_file);
2095 heap_free(task->src_file);
2096 heap_free(task->dst_file);
2099 /***********************************************************************
2100 * FtpRenameFileW (WININET.@)
2102 * Rename a file on the ftp server
2104 * RETURNS
2105 * TRUE on success
2106 * FALSE on failure
2109 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2111 ftp_session_t *lpwfs;
2112 appinfo_t *hIC = NULL;
2113 BOOL r = FALSE;
2115 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
2116 if (!lpwfs)
2118 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2119 return FALSE;
2122 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2124 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2125 goto lend;
2128 if (lpwfs->download_in_progress != NULL)
2130 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2131 goto lend;
2134 if (!lpszSrc || !lpszDest)
2136 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2137 goto lend;
2140 hIC = lpwfs->lpAppInfo;
2141 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2143 rename_file_task_t *task;
2145 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRenameFileProc, sizeof(*task));
2146 task->src_file = heap_strdupW(lpszSrc);
2147 task->dst_file = heap_strdupW(lpszDest);
2149 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
2151 else
2153 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2156 lend:
2157 WININET_Release( &lpwfs->hdr );
2159 return r;
2162 /***********************************************************************
2163 * FTP_FtpRenameFileW (Internal)
2165 * Rename a file on the ftp server
2167 * RETURNS
2168 * TRUE on success
2169 * FALSE on failure
2172 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2174 INT nResCode;
2175 BOOL bSuccess = FALSE;
2176 appinfo_t *hIC = NULL;
2178 TRACE("\n");
2180 /* Clear any error information */
2181 INTERNET_SetLastError(0);
2183 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2184 goto lend;
2186 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2187 if (nResCode == 350)
2189 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2190 goto lend;
2192 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2195 if (nResCode == 250)
2196 bSuccess = TRUE;
2197 else
2198 FTP_SetResponseError(nResCode);
2200 lend:
2201 hIC = lpwfs->lpAppInfo;
2202 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2204 INTERNET_ASYNC_RESULT iar;
2206 iar.dwResult = (DWORD)bSuccess;
2207 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2208 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2209 &iar, sizeof(INTERNET_ASYNC_RESULT));
2212 return bSuccess;
2215 /***********************************************************************
2216 * FtpCommandA (WININET.@)
2218 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2219 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2221 BOOL r;
2222 WCHAR *cmdW;
2224 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2225 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2227 if (fExpectResponse)
2229 FIXME("data connection not supported\n");
2230 return FALSE;
2233 if (!lpszCommand || !lpszCommand[0])
2235 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2236 return FALSE;
2239 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2241 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2242 return FALSE;
2245 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2247 heap_free(cmdW);
2248 return r;
2251 /***********************************************************************
2252 * FtpCommandW (WININET.@)
2254 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2255 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2257 BOOL r = FALSE;
2258 ftp_session_t *lpwfs;
2259 LPSTR cmd = NULL;
2260 DWORD len, nBytesSent= 0;
2261 INT nResCode, nRC = 0;
2263 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2264 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2266 if (!lpszCommand || !lpszCommand[0])
2268 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2269 return FALSE;
2272 if (fExpectResponse)
2274 FIXME("data connection not supported\n");
2275 return FALSE;
2278 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
2279 if (!lpwfs)
2281 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2282 return FALSE;
2285 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2287 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2288 goto lend;
2291 if (lpwfs->download_in_progress != NULL)
2293 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2294 goto lend;
2297 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2298 if ((cmd = heap_alloc(len)))
2299 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2300 else
2302 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2303 goto lend;
2306 strcat(cmd, szCRLF);
2307 len--;
2309 TRACE("Sending (%s) len(%d)\n", cmd, len);
2310 while ((nBytesSent < len) && (nRC != -1))
2312 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2313 if (nRC != -1)
2315 nBytesSent += nRC;
2316 TRACE("Sent %d bytes\n", nRC);
2320 if (nBytesSent)
2322 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2323 if (nResCode > 0 && nResCode < 400)
2324 r = TRUE;
2325 else
2326 FTP_SetResponseError(nResCode);
2329 lend:
2330 WININET_Release( &lpwfs->hdr );
2331 heap_free( cmd );
2332 return r;
2336 /***********************************************************************
2337 * FTPSESSION_Destroy (internal)
2339 * Deallocate session handle
2341 static void FTPSESSION_Destroy(object_header_t *hdr)
2343 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2345 TRACE("\n");
2347 WININET_Release(&lpwfs->lpAppInfo->hdr);
2349 heap_free(lpwfs->lpszPassword);
2350 heap_free(lpwfs->lpszUserName);
2351 heap_free(lpwfs->servername);
2354 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2356 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2358 TRACE("\n");
2360 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2361 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2363 if (lpwfs->download_in_progress != NULL)
2364 lpwfs->download_in_progress->session_deleted = TRUE;
2366 if (lpwfs->sndSocket != -1)
2367 closesocket(lpwfs->sndSocket);
2369 if (lpwfs->lstnSocket != -1)
2370 closesocket(lpwfs->lstnSocket);
2372 if (lpwfs->pasvSocket != -1)
2373 closesocket(lpwfs->pasvSocket);
2375 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2376 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2379 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2381 switch(option) {
2382 case INTERNET_OPTION_HANDLE_TYPE:
2383 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2385 if (*size < sizeof(ULONG))
2386 return ERROR_INSUFFICIENT_BUFFER;
2388 *size = sizeof(DWORD);
2389 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2390 return ERROR_SUCCESS;
2393 return INET_QueryOption(hdr, option, buffer, size, unicode);
2396 static const object_vtbl_t FTPSESSIONVtbl = {
2397 FTPSESSION_Destroy,
2398 FTPSESSION_CloseConnection,
2399 FTPSESSION_QueryOption,
2400 INET_SetOption,
2401 NULL,
2402 NULL,
2403 NULL,
2404 NULL,
2405 NULL
2409 /***********************************************************************
2410 * FTP_Connect (internal)
2412 * Connect to a ftp server
2414 * RETURNS
2415 * HINTERNET a session handle on success
2416 * NULL on failure
2418 * NOTES:
2420 * Windows uses 'anonymous' as the username, when given a NULL username
2421 * and a NULL password. The password is first looked up in:
2423 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2425 * If this entry is not present it uses the current username as the password.
2429 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2430 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2431 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2432 DWORD dwInternalFlags)
2434 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2435 'M','i','c','r','o','s','o','f','t','\\',
2436 'W','i','n','d','o','w','s','\\',
2437 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2438 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2439 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2440 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2441 static const WCHAR szEmpty[] = {'\0'};
2442 struct sockaddr_in socketAddr;
2443 INT nsocket = -1;
2444 socklen_t sock_namelen;
2445 BOOL bSuccess = FALSE;
2446 ftp_session_t *lpwfs = NULL;
2447 char szaddr[INET_ADDRSTRLEN];
2449 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2450 hIC, debugstr_w(lpszServerName),
2451 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2453 assert( hIC->hdr.htype == WH_HINIT );
2455 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2457 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2458 return NULL;
2461 lpwfs = alloc_object(&hIC->hdr, &FTPSESSIONVtbl, sizeof(ftp_session_t));
2462 if (NULL == lpwfs)
2464 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2465 return NULL;
2468 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2469 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2470 else
2471 lpwfs->serverport = nServerPort;
2473 lpwfs->hdr.htype = WH_HFTPSESSION;
2474 lpwfs->hdr.dwFlags = dwFlags;
2475 lpwfs->hdr.dwContext = dwContext;
2476 lpwfs->hdr.dwInternalFlags |= dwInternalFlags;
2477 lpwfs->download_in_progress = NULL;
2478 lpwfs->sndSocket = -1;
2479 lpwfs->lstnSocket = -1;
2480 lpwfs->pasvSocket = -1;
2482 WININET_AddRef( &hIC->hdr );
2483 lpwfs->lpAppInfo = hIC;
2484 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2486 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
2487 if(strchrW(hIC->proxy, ' '))
2488 FIXME("Several proxies not implemented.\n");
2489 if(hIC->proxyBypass)
2490 FIXME("Proxy bypass is ignored.\n");
2492 if (!lpszUserName || !strlenW(lpszUserName)) {
2493 HKEY key;
2494 WCHAR szPassword[MAX_PATH];
2495 DWORD len = sizeof(szPassword);
2497 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2499 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2500 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2501 /* Nothing in the registry, get the username and use that as the password */
2502 if (!GetUserNameW(szPassword, &len)) {
2503 /* Should never get here, but use an empty password as failsafe */
2504 strcpyW(szPassword, szEmpty);
2507 RegCloseKey(key);
2509 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2510 lpwfs->lpszPassword = heap_strdupW(szPassword);
2512 else {
2513 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2514 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2516 lpwfs->servername = heap_strdupW(lpszServerName);
2518 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2519 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2521 INTERNET_ASYNC_RESULT iar;
2523 iar.dwResult = (DWORD_PTR)lpwfs->hdr.hInternet;
2524 iar.dwError = ERROR_SUCCESS;
2526 SendAsyncCallback(&hIC->hdr, dwContext,
2527 INTERNET_STATUS_HANDLE_CREATED, &iar,
2528 sizeof(INTERNET_ASYNC_RESULT));
2531 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2532 (LPWSTR) lpszServerName, (strlenW(lpszServerName)+1) * sizeof(WCHAR));
2534 sock_namelen = sizeof(socketAddr);
2535 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen))
2537 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2538 goto lerror;
2541 if (socketAddr.sin_family != AF_INET)
2543 WARN("unsupported address family %d\n", socketAddr.sin_family);
2544 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2545 goto lerror;
2548 inet_ntop(socketAddr.sin_family, &socketAddr.sin_addr, szaddr, sizeof(szaddr));
2549 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2550 szaddr, strlen(szaddr)+1);
2552 nsocket = socket(AF_INET,SOCK_STREAM,0);
2553 if (nsocket == -1)
2555 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2556 goto lerror;
2559 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2560 szaddr, strlen(szaddr)+1);
2562 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2564 ERR("Unable to connect (%s)\n", strerror(errno));
2565 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2566 closesocket(nsocket);
2568 else
2570 TRACE("Connected to server\n");
2571 lpwfs->sndSocket = nsocket;
2572 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2573 szaddr, strlen(szaddr)+1);
2575 sock_namelen = sizeof(lpwfs->socketAddress);
2576 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2578 if (FTP_ConnectToHost(lpwfs))
2580 TRACE("Successfully logged into server\n");
2581 bSuccess = TRUE;
2585 lerror:
2586 if (!bSuccess)
2588 if(lpwfs)
2589 WININET_Release( &lpwfs->hdr );
2590 return NULL;
2593 return lpwfs->hdr.hInternet;
2597 /***********************************************************************
2598 * FTP_ConnectToHost (internal)
2600 * Connect to a ftp server
2602 * RETURNS
2603 * TRUE on success
2604 * NULL on failure
2607 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2609 INT nResCode;
2610 BOOL bSuccess = FALSE;
2612 TRACE("\n");
2613 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2615 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2616 goto lend;
2618 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2619 if (nResCode)
2621 /* Login successful... */
2622 if (nResCode == 230)
2623 bSuccess = TRUE;
2624 /* User name okay, need password... */
2625 else if (nResCode == 331)
2626 bSuccess = FTP_SendPassword(lpwfs);
2627 /* Need account for login... */
2628 else if (nResCode == 332)
2629 bSuccess = FTP_SendAccount(lpwfs);
2630 else
2631 FTP_SetResponseError(nResCode);
2634 TRACE("Returning %d\n", bSuccess);
2635 lend:
2636 return bSuccess;
2640 /***********************************************************************
2641 * FTP_SendCommandA (internal)
2643 * Send command to server
2645 * RETURNS
2646 * TRUE on success
2647 * NULL on failure
2650 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2651 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2653 DWORD len;
2654 CHAR *buf;
2655 DWORD nBytesSent = 0;
2656 int nRC = 0;
2657 DWORD dwParamLen;
2659 TRACE("%d: (%s) %d\n", ftpCmd, debugstr_a(lpszParam), nSocket);
2661 if (lpfnStatusCB)
2663 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2666 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2667 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2668 if (NULL == (buf = heap_alloc(len+1)))
2670 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2671 return FALSE;
2673 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2674 dwParamLen ? lpszParam : "", szCRLF);
2676 TRACE("Sending (%s) len(%d)\n", buf, len);
2677 while((nBytesSent < len) && (nRC != -1))
2679 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2680 nBytesSent += nRC;
2682 heap_free(buf);
2684 if (lpfnStatusCB)
2686 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2687 &nBytesSent, sizeof(DWORD));
2690 TRACE("Sent %d bytes\n", nBytesSent);
2691 return (nRC != -1);
2694 /***********************************************************************
2695 * FTP_SendCommand (internal)
2697 * Send command to server
2699 * RETURNS
2700 * TRUE on success
2701 * NULL on failure
2704 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2705 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2707 BOOL ret;
2708 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2709 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2710 heap_free(lpszParamA);
2711 return ret;
2714 /***********************************************************************
2715 * FTP_ReceiveResponse (internal)
2717 * Receive response from server
2719 * RETURNS
2720 * Reply code on success
2721 * 0 on failure
2724 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2726 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2727 DWORD nRecv;
2728 INT rc = 0;
2729 char firstprefix[5];
2730 BOOL multiline = FALSE;
2732 TRACE("socket(%d)\n", lpwfs->sndSocket);
2734 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2736 while(1)
2738 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2739 goto lerror;
2741 if (nRecv >= 3)
2743 if(!multiline)
2745 if(lpszResponse[3] != '-')
2746 break;
2747 else
2748 { /* Start of multiline response. Loop until we get "nnn " */
2749 multiline = TRUE;
2750 memcpy(firstprefix, lpszResponse, 3);
2751 firstprefix[3] = ' ';
2752 firstprefix[4] = '\0';
2755 else
2757 if(!memcmp(firstprefix, lpszResponse, 4))
2758 break;
2763 if (nRecv >= 3)
2765 rc = atoi(lpszResponse);
2767 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2768 &nRecv, sizeof(DWORD));
2771 lerror:
2772 TRACE("return %d\n", rc);
2773 return rc;
2777 /***********************************************************************
2778 * FTP_SendPassword (internal)
2780 * Send password to ftp server
2782 * RETURNS
2783 * TRUE on success
2784 * NULL on failure
2787 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2789 INT nResCode;
2790 BOOL bSuccess = FALSE;
2792 TRACE("\n");
2793 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2794 goto lend;
2796 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2797 if (nResCode)
2799 TRACE("Received reply code %d\n", nResCode);
2800 /* Login successful... */
2801 if (nResCode == 230)
2802 bSuccess = TRUE;
2803 /* Command not implemented, superfluous at the server site... */
2804 /* Need account for login... */
2805 else if (nResCode == 332)
2806 bSuccess = FTP_SendAccount(lpwfs);
2807 else
2808 FTP_SetResponseError(nResCode);
2811 lend:
2812 TRACE("Returning %d\n", bSuccess);
2813 return bSuccess;
2817 /***********************************************************************
2818 * FTP_SendAccount (internal)
2822 * RETURNS
2823 * TRUE on success
2824 * FALSE on failure
2827 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2829 INT nResCode;
2830 BOOL bSuccess = FALSE;
2832 TRACE("\n");
2833 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2834 goto lend;
2836 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2837 if (nResCode)
2838 bSuccess = TRUE;
2839 else
2840 FTP_SetResponseError(nResCode);
2842 lend:
2843 return bSuccess;
2847 /***********************************************************************
2848 * FTP_SendStore (internal)
2850 * Send request to upload file to ftp server
2852 * RETURNS
2853 * TRUE on success
2854 * FALSE on failure
2857 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2859 INT nResCode;
2860 BOOL bSuccess = FALSE;
2862 TRACE("\n");
2863 if (!FTP_InitListenSocket(lpwfs))
2864 goto lend;
2866 if (!FTP_SendType(lpwfs, dwType))
2867 goto lend;
2869 if (!FTP_SendPortOrPasv(lpwfs))
2870 goto lend;
2872 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2873 goto lend;
2874 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2875 if (nResCode)
2877 if (nResCode == 150 || nResCode == 125)
2878 bSuccess = TRUE;
2879 else
2880 FTP_SetResponseError(nResCode);
2883 lend:
2884 if (!bSuccess && lpwfs->lstnSocket != -1)
2886 closesocket(lpwfs->lstnSocket);
2887 lpwfs->lstnSocket = -1;
2890 return bSuccess;
2894 /***********************************************************************
2895 * FTP_InitListenSocket (internal)
2897 * Create a socket to listen for server response
2899 * RETURNS
2900 * TRUE on success
2901 * FALSE on failure
2904 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2906 BOOL bSuccess = FALSE;
2907 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2909 TRACE("\n");
2911 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2912 if (lpwfs->lstnSocket == -1)
2914 TRACE("Unable to create listening socket\n");
2915 goto lend;
2918 /* We obtain our ip addr from the name of the command channel socket */
2919 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2921 /* and get the system to assign us a port */
2922 lpwfs->lstnSocketAddress.sin_port = htons(0);
2924 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2926 TRACE("Unable to bind socket\n");
2927 goto lend;
2930 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2932 TRACE("listen failed\n");
2933 goto lend;
2936 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2937 bSuccess = TRUE;
2939 lend:
2940 if (!bSuccess && lpwfs->lstnSocket != -1)
2942 closesocket(lpwfs->lstnSocket);
2943 lpwfs->lstnSocket = -1;
2946 return bSuccess;
2950 /***********************************************************************
2951 * FTP_SendType (internal)
2953 * Tell server type of data being transferred
2955 * RETURNS
2956 * TRUE on success
2957 * FALSE on failure
2959 * W98SE doesn't cache the type that's currently set
2960 * (i.e. it sends it always),
2961 * so we probably don't want to do that either.
2963 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2965 INT nResCode;
2966 WCHAR type[] = { 'I','\0' };
2967 BOOL bSuccess = FALSE;
2969 TRACE("\n");
2970 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2971 type[0] = 'A';
2973 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2974 goto lend;
2976 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2977 if (nResCode)
2979 if (nResCode == 2)
2980 bSuccess = TRUE;
2981 else
2982 FTP_SetResponseError(nResCode);
2985 lend:
2986 return bSuccess;
2990 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2991 /***********************************************************************
2992 * FTP_GetFileSize (internal)
2994 * Retrieves from the server the size of the given file
2996 * RETURNS
2997 * TRUE on success
2998 * FALSE on failure
3001 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
3003 INT nResCode;
3004 BOOL bSuccess = FALSE;
3006 TRACE("\n");
3008 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
3009 goto lend;
3011 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3012 if (nResCode)
3014 if (nResCode == 213) {
3015 /* Now parses the output to get the actual file size */
3016 int i;
3017 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3019 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3020 if (lpszResponseBuffer[i] == '\0') return FALSE;
3021 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3023 bSuccess = TRUE;
3024 } else {
3025 FTP_SetResponseError(nResCode);
3029 lend:
3030 return bSuccess;
3032 #endif
3035 /***********************************************************************
3036 * FTP_SendPort (internal)
3038 * Tell server which port to use
3040 * RETURNS
3041 * TRUE on success
3042 * FALSE on failure
3045 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3047 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3048 INT nResCode;
3049 WCHAR szIPAddress[64];
3050 BOOL bSuccess = FALSE;
3051 TRACE("\n");
3053 sprintfW(szIPAddress, szIPFormat,
3054 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
3055 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
3056 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
3057 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
3058 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3059 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3061 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3062 goto lend;
3064 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3065 if (nResCode)
3067 if (nResCode == 200)
3068 bSuccess = TRUE;
3069 else
3070 FTP_SetResponseError(nResCode);
3073 lend:
3074 return bSuccess;
3078 /***********************************************************************
3079 * FTP_DoPassive (internal)
3081 * Tell server that we want to do passive transfers
3082 * and connect data socket
3084 * RETURNS
3085 * TRUE on success
3086 * FALSE on failure
3089 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3091 INT nResCode;
3092 BOOL bSuccess = FALSE;
3094 TRACE("\n");
3095 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3096 goto lend;
3098 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3099 if (nResCode)
3101 if (nResCode == 227)
3103 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3104 LPSTR p;
3105 int f[6];
3106 int i;
3107 char *pAddr, *pPort;
3108 INT nsocket = -1;
3109 struct sockaddr_in dataSocketAddress;
3111 p = lpszResponseBuffer+4; /* skip status code */
3112 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3114 if (*p == '\0')
3116 ERR("no address found in response, aborting\n");
3117 goto lend;
3120 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3121 &f[4], &f[5]) != 6)
3123 ERR("unknown response address format '%s', aborting\n", p);
3124 goto lend;
3126 for (i=0; i < 6; i++)
3127 f[i] = f[i] & 0xff;
3129 dataSocketAddress = lpwfs->socketAddress;
3130 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3131 pPort = (char *)&(dataSocketAddress.sin_port);
3132 pAddr[0] = f[0];
3133 pAddr[1] = f[1];
3134 pAddr[2] = f[2];
3135 pAddr[3] = f[3];
3136 pPort[0] = f[4];
3137 pPort[1] = f[5];
3139 nsocket = socket(AF_INET,SOCK_STREAM,0);
3140 if (nsocket == -1)
3141 goto lend;
3143 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3145 ERR("can't connect passive FTP data port.\n");
3146 closesocket(nsocket);
3147 goto lend;
3149 lpwfs->pasvSocket = nsocket;
3150 bSuccess = TRUE;
3152 else
3153 FTP_SetResponseError(nResCode);
3156 lend:
3157 return bSuccess;
3161 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3163 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3165 if (!FTP_DoPassive(lpwfs))
3166 return FALSE;
3168 else
3170 if (!FTP_SendPort(lpwfs))
3171 return FALSE;
3173 return TRUE;
3177 /***********************************************************************
3178 * FTP_GetDataSocket (internal)
3180 * Either accepts an incoming data socket connection from the server
3181 * or just returns the already opened socket after a PASV command
3182 * in case of passive FTP.
3185 * RETURNS
3186 * TRUE on success
3187 * FALSE on failure
3190 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3192 struct sockaddr_in saddr;
3193 socklen_t addrlen = sizeof(struct sockaddr);
3195 TRACE("\n");
3196 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3198 *nDataSocket = lpwfs->pasvSocket;
3199 lpwfs->pasvSocket = -1;
3201 else
3203 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3204 closesocket(lpwfs->lstnSocket);
3205 lpwfs->lstnSocket = -1;
3207 return *nDataSocket != -1;
3211 /***********************************************************************
3212 * FTP_SendData (internal)
3214 * Send data to the server
3216 * RETURNS
3217 * TRUE on success
3218 * FALSE on failure
3221 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3223 BY_HANDLE_FILE_INFORMATION fi;
3224 DWORD nBytesRead = 0;
3225 DWORD nBytesSent = 0;
3226 DWORD nTotalSent = 0;
3227 DWORD nBytesToSend, nLen;
3228 int nRC = 1;
3229 time_t s_long_time, e_long_time;
3230 LONG nSeconds;
3231 CHAR *lpszBuffer;
3233 TRACE("\n");
3234 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3236 /* Get the size of the file. */
3237 GetFileInformationByHandle(hFile, &fi);
3238 time(&s_long_time);
3242 nBytesToSend = nBytesRead - nBytesSent;
3244 if (nBytesToSend <= 0)
3246 /* Read data from file. */
3247 nBytesSent = 0;
3248 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3249 ERR("Failed reading from file\n");
3251 if (nBytesRead > 0)
3252 nBytesToSend = nBytesRead;
3253 else
3254 break;
3257 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3258 DATA_PACKET_SIZE : nBytesToSend;
3259 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3261 if (nRC != -1)
3263 nBytesSent += nRC;
3264 nTotalSent += nRC;
3267 /* Do some computation to display the status. */
3268 time(&e_long_time);
3269 nSeconds = e_long_time - s_long_time;
3270 if( nSeconds / 60 > 0 )
3272 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3273 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3274 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3276 else
3278 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3279 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3280 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3282 } while (nRC != -1);
3284 TRACE("file transfer complete!\n");
3286 heap_free(lpszBuffer);
3287 return nTotalSent;
3291 /***********************************************************************
3292 * FTP_SendRetrieve (internal)
3294 * Send request to retrieve a file
3296 * RETURNS
3297 * Number of bytes to be received on success
3298 * 0 on failure
3301 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3303 INT nResCode;
3304 BOOL ret;
3306 TRACE("\n");
3307 if (!(ret = FTP_InitListenSocket(lpwfs)))
3308 goto lend;
3310 if (!(ret = FTP_SendType(lpwfs, dwType)))
3311 goto lend;
3313 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3314 goto lend;
3316 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3317 goto lend;
3319 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3320 if ((nResCode != 125) && (nResCode != 150)) {
3321 /* That means that we got an error getting the file. */
3322 FTP_SetResponseError(nResCode);
3323 ret = FALSE;
3326 lend:
3327 if (!ret && lpwfs->lstnSocket != -1)
3329 closesocket(lpwfs->lstnSocket);
3330 lpwfs->lstnSocket = -1;
3333 return ret;
3337 /***********************************************************************
3338 * FTP_RetrieveData (internal)
3340 * Retrieve data from server
3342 * RETURNS
3343 * TRUE on success
3344 * FALSE on failure
3347 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3349 DWORD nBytesWritten;
3350 INT nRC = 0;
3351 CHAR *lpszBuffer;
3353 TRACE("\n");
3355 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3356 if (NULL == lpszBuffer)
3358 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3359 return FALSE;
3362 while (nRC != -1)
3364 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3365 if (nRC != -1)
3367 /* other side closed socket. */
3368 if (nRC == 0)
3369 goto recv_end;
3370 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3374 TRACE("Data transfer complete\n");
3376 recv_end:
3377 heap_free(lpszBuffer);
3378 return (nRC != -1);
3381 /***********************************************************************
3382 * FTPFINDNEXT_Destroy (internal)
3384 * Deallocate session handle
3386 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3388 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3389 DWORD i;
3391 TRACE("\n");
3393 WININET_Release(&lpwfn->lpFtpSession->hdr);
3395 for (i = 0; i < lpwfn->size; i++)
3397 heap_free(lpwfn->lpafp[i].lpszName);
3399 heap_free(lpwfn->lpafp);
3402 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3404 WIN32_FIND_DATAW *find_data = data;
3405 DWORD res = ERROR_SUCCESS;
3407 TRACE("index(%d) size(%d)\n", find->index, find->size);
3409 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3411 if (find->index < find->size) {
3412 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3413 find->index++;
3415 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3416 }else {
3417 res = ERROR_NO_MORE_FILES;
3420 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3422 INTERNET_ASYNC_RESULT iar;
3424 iar.dwResult = (res == ERROR_SUCCESS);
3425 iar.dwError = res;
3427 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3428 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3429 sizeof(INTERNET_ASYNC_RESULT));
3432 return res;
3435 typedef struct {
3436 task_header_t hdr;
3437 WIN32_FIND_DATAW *find_data;
3438 } find_next_task_t;
3440 static void FTPFINDNEXT_AsyncFindNextFileProc(task_header_t *hdr)
3442 find_next_task_t *task = (find_next_task_t*)hdr;
3444 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)task->hdr.hdr, task->find_data);
3447 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3449 switch(option) {
3450 case INTERNET_OPTION_HANDLE_TYPE:
3451 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3453 if (*size < sizeof(ULONG))
3454 return ERROR_INSUFFICIENT_BUFFER;
3456 *size = sizeof(DWORD);
3457 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3458 return ERROR_SUCCESS;
3461 return INET_QueryOption(hdr, option, buffer, size, unicode);
3464 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3466 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3468 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3470 find_next_task_t *task;
3472 task = alloc_async_task(&find->hdr, FTPFINDNEXT_AsyncFindNextFileProc, sizeof(*task));
3473 task->find_data = data;
3475 INTERNET_AsyncCall(&task->hdr);
3476 return ERROR_SUCCESS;
3479 return FTPFINDNEXT_FindNextFileProc(find, data);
3482 static const object_vtbl_t FTPFINDNEXTVtbl = {
3483 FTPFINDNEXT_Destroy,
3484 NULL,
3485 FTPFINDNEXT_QueryOption,
3486 INET_SetOption,
3487 NULL,
3488 NULL,
3489 NULL,
3490 NULL,
3491 FTPFINDNEXT_FindNextFileW
3494 /***********************************************************************
3495 * FTP_ReceiveFileList (internal)
3497 * Read file list from server
3499 * RETURNS
3500 * Handle to file list on success
3501 * NULL on failure
3504 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3505 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3507 DWORD dwSize = 0;
3508 LPFILEPROPERTIESW lpafp = NULL;
3509 LPWININETFTPFINDNEXTW lpwfn = NULL;
3511 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3513 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3515 if(lpFindFileData)
3516 FTP_ConvertFileProp(lpafp, lpFindFileData);
3518 lpwfn = alloc_object(&lpwfs->hdr, &FTPFINDNEXTVtbl, sizeof(WININETFTPFINDNEXTW));
3519 if (lpwfn)
3521 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3522 lpwfn->hdr.dwContext = dwContext;
3523 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3524 lpwfn->size = dwSize;
3525 lpwfn->lpafp = lpafp;
3527 WININET_AddRef( &lpwfs->hdr );
3528 lpwfn->lpFtpSession = lpwfs;
3529 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3533 TRACE("Matched %d files\n", dwSize);
3534 return lpwfn ? lpwfn->hdr.hInternet : NULL;
3538 /***********************************************************************
3539 * FTP_ConvertFileProp (internal)
3541 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3543 * RETURNS
3544 * TRUE on success
3545 * FALSE on failure
3548 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3550 BOOL bSuccess = FALSE;
3552 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3554 if (lpafp)
3556 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3557 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3558 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3560 /* Not all fields are filled in */
3561 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3562 lpFindFileData->nFileSizeLow = lpafp->nSize;
3564 if (lpafp->bIsDirectory)
3565 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3567 if (lpafp->lpszName)
3568 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3570 bSuccess = TRUE;
3573 return bSuccess;
3576 /***********************************************************************
3577 * FTP_ParseNextFile (internal)
3579 * Parse the next line in file listing
3581 * RETURNS
3582 * TRUE on success
3583 * FALSE on failure
3585 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3587 static const char szSpace[] = " \t";
3588 DWORD nBufLen;
3589 char *pszLine;
3590 char *pszToken;
3591 char *pszTmp;
3592 BOOL found = FALSE;
3593 int i;
3595 lpfp->lpszName = NULL;
3596 do {
3597 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3598 return FALSE;
3600 pszToken = strtok(pszLine, szSpace);
3601 /* ls format
3602 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3604 * For instance:
3605 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3607 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3608 if(!FTP_ParsePermission(pszToken, lpfp))
3609 lpfp->bIsDirectory = FALSE;
3610 for(i=0; i<=3; i++) {
3611 if(!(pszToken = strtok(NULL, szSpace)))
3612 break;
3614 if(!pszToken) continue;
3615 if(lpfp->bIsDirectory) {
3616 TRACE("Is directory\n");
3617 lpfp->nSize = 0;
3619 else {
3620 TRACE("Size: %s\n", pszToken);
3621 lpfp->nSize = atol(pszToken);
3624 lpfp->tmLastModified.wSecond = 0;
3625 lpfp->tmLastModified.wMinute = 0;
3626 lpfp->tmLastModified.wHour = 0;
3627 lpfp->tmLastModified.wDay = 0;
3628 lpfp->tmLastModified.wMonth = 0;
3629 lpfp->tmLastModified.wYear = 0;
3631 /* Determine month */
3632 pszToken = strtok(NULL, szSpace);
3633 if(!pszToken) continue;
3634 if(strlen(pszToken) >= 3) {
3635 pszToken[3] = 0;
3636 if((pszTmp = StrStrIA(szMonths, pszToken)))
3637 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3639 /* Determine day */
3640 pszToken = strtok(NULL, szSpace);
3641 if(!pszToken) continue;
3642 lpfp->tmLastModified.wDay = atoi(pszToken);
3643 /* Determine time or year */
3644 pszToken = strtok(NULL, szSpace);
3645 if(!pszToken) continue;
3646 if((pszTmp = strchr(pszToken, ':'))) {
3647 SYSTEMTIME curr_time;
3648 *pszTmp = 0;
3649 pszTmp++;
3650 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3651 lpfp->tmLastModified.wHour = atoi(pszToken);
3652 GetLocalTime( &curr_time );
3653 lpfp->tmLastModified.wYear = curr_time.wYear;
3655 else {
3656 lpfp->tmLastModified.wYear = atoi(pszToken);
3657 lpfp->tmLastModified.wHour = 12;
3659 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3660 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3661 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3663 pszToken = strtok(NULL, szSpace);
3664 if(!pszToken) continue;
3665 lpfp->lpszName = heap_strdupAtoW(pszToken);
3666 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3668 /* NT way of parsing ... :
3670 07-13-03 08:55PM <DIR> sakpatch
3671 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3673 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3674 int mon, mday, year, hour, min;
3675 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3677 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3678 lpfp->tmLastModified.wDay = mday;
3679 lpfp->tmLastModified.wMonth = mon;
3680 lpfp->tmLastModified.wYear = year;
3682 /* Hacky and bad Y2K protection :-) */
3683 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3685 pszToken = strtok(NULL, szSpace);
3686 if(!pszToken) continue;
3687 sscanf(pszToken, "%d:%d", &hour, &min);
3688 lpfp->tmLastModified.wHour = hour;
3689 lpfp->tmLastModified.wMinute = min;
3690 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3691 lpfp->tmLastModified.wHour += 12;
3693 lpfp->tmLastModified.wSecond = 0;
3695 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3696 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3697 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3699 pszToken = strtok(NULL, szSpace);
3700 if(!pszToken) continue;
3701 if(!strcasecmp(pszToken, "<DIR>")) {
3702 lpfp->bIsDirectory = TRUE;
3703 lpfp->nSize = 0;
3704 TRACE("Is directory\n");
3706 else {
3707 lpfp->bIsDirectory = FALSE;
3708 lpfp->nSize = atol(pszToken);
3709 TRACE("Size: %d\n", lpfp->nSize);
3712 pszToken = strtok(NULL, szSpace);
3713 if(!pszToken) continue;
3714 lpfp->lpszName = heap_strdupAtoW(pszToken);
3715 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3717 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3718 else if(pszToken[0] == '+') {
3719 FIXME("EPLF Format not implemented\n");
3722 if(lpfp->lpszName) {
3723 if((lpszSearchFile == NULL) ||
3724 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3725 found = TRUE;
3726 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3728 else {
3729 heap_free(lpfp->lpszName);
3730 lpfp->lpszName = NULL;
3733 } while(!found);
3734 return TRUE;
3737 /***********************************************************************
3738 * FTP_ParseDirectory (internal)
3740 * Parse string of directory information
3742 * RETURNS
3743 * TRUE on success
3744 * FALSE on failure
3746 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3747 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3749 BOOL bSuccess = TRUE;
3750 INT sizeFilePropArray = 500;/*20; */
3751 INT indexFilePropArray = -1;
3753 TRACE("\n");
3755 /* Allocate initial file properties array */
3756 *lpafp = heap_alloc_zero(sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3757 if (!*lpafp)
3758 return FALSE;
3760 do {
3761 if (indexFilePropArray+1 >= sizeFilePropArray)
3763 LPFILEPROPERTIESW tmpafp;
3765 sizeFilePropArray *= 2;
3766 tmpafp = heap_realloc_zero(*lpafp, sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3767 if (NULL == tmpafp)
3769 bSuccess = FALSE;
3770 break;
3773 *lpafp = tmpafp;
3775 indexFilePropArray++;
3776 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3778 if (bSuccess && indexFilePropArray)
3780 if (indexFilePropArray < sizeFilePropArray - 1)
3782 LPFILEPROPERTIESW tmpafp;
3784 tmpafp = heap_realloc(*lpafp, sizeof(FILEPROPERTIESW)*indexFilePropArray);
3785 if (NULL != tmpafp)
3786 *lpafp = tmpafp;
3788 *dwfp = indexFilePropArray;
3790 else
3792 heap_free(*lpafp);
3793 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3794 bSuccess = FALSE;
3797 return bSuccess;
3801 /***********************************************************************
3802 * FTP_ParsePermission (internal)
3804 * Parse permission string of directory information
3806 * RETURNS
3807 * TRUE on success
3808 * FALSE on failure
3811 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3813 BOOL bSuccess = TRUE;
3814 unsigned short nPermission = 0;
3815 INT nPos = 1;
3816 INT nLast = 9;
3818 TRACE("\n");
3819 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3821 bSuccess = FALSE;
3822 return bSuccess;
3825 lpfp->bIsDirectory = (*lpszPermission == 'd');
3828 switch (nPos)
3830 case 1:
3831 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3832 break;
3833 case 2:
3834 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3835 break;
3836 case 3:
3837 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3838 break;
3839 case 4:
3840 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3841 break;
3842 case 5:
3843 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3844 break;
3845 case 6:
3846 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3847 break;
3848 case 7:
3849 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3850 break;
3851 case 8:
3852 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3853 break;
3854 case 9:
3855 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3856 break;
3858 nPos++;
3859 }while (nPos <= nLast);
3861 lpfp->permissions = nPermission;
3862 return bSuccess;
3866 /***********************************************************************
3867 * FTP_SetResponseError (internal)
3869 * Set the appropriate error code for a given response from the server
3871 * RETURNS
3874 static DWORD FTP_SetResponseError(DWORD dwResponse)
3876 DWORD dwCode = 0;
3878 switch(dwResponse)
3880 case 425: /* Cannot open data connection. */
3881 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3882 break;
3884 case 426: /* Connection closed, transer aborted. */
3885 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3886 break;
3888 case 530: /* Not logged in. Login incorrect. */
3889 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3890 break;
3892 case 421: /* Service not available - Server may be shutting down. */
3893 case 450: /* File action not taken. File may be busy. */
3894 case 451: /* Action aborted. Server error. */
3895 case 452: /* Action not taken. Insufficient storage space on server. */
3896 case 500: /* Syntax error. Command unrecognized. */
3897 case 501: /* Syntax error. Error in parameters or arguments. */
3898 case 502: /* Command not implemented. */
3899 case 503: /* Bad sequence of commands. */
3900 case 504: /* Command not implemented for that parameter. */
3901 case 532: /* Need account for storing files */
3902 case 550: /* File action not taken. File not found or no access. */
3903 case 551: /* Requested action aborted. Page type unknown */
3904 case 552: /* Action aborted. Exceeded storage allocation */
3905 case 553: /* Action not taken. File name not allowed. */
3907 default:
3908 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3909 break;
3912 INTERNET_SetLastError(dwCode);
3913 return dwCode;