ddraw/tests: Move the capability tests for enumerated devices.
[wine.git] / dlls / wininet / ftp.c
blob80efeb2bf37fae040f2d76a040ebe0efbf9b9e81
1 /*
2 * WININET - Ftp implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2004 Mike McCormack for CodeWeavers
6 * Copyright 2004 Kevin Koltzau
7 * Copyright 2007 Hans Leidekker
9 * Ulrich Czekalla
10 * Noureddine Jemmali
12 * Copyright 2000 Andreas Mohr
13 * Copyright 2002 Jaco Greeff
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "ws2tcpip.h"
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <time.h>
37 #include <assert.h>
39 #include "windef.h"
40 #include "winbase.h"
41 #include "wingdi.h"
42 #include "winuser.h"
43 #include "wininet.h"
44 #include "winnls.h"
45 #include "winerror.h"
46 #include "winreg.h"
47 #include "winternl.h"
48 #include "shlwapi.h"
50 #include "wine/debug.h"
51 #include "internet.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
55 #define RESPONSE_TIMEOUT 30
57 typedef struct _ftp_session_t ftp_session_t;
59 typedef struct
61 object_header_t hdr;
62 ftp_session_t *lpFtpSession;
63 BOOL session_deleted;
64 int nDataSocket;
65 WCHAR *cache_file;
66 HANDLE cache_file_handle;
67 } ftp_file_t;
69 struct _ftp_session_t
71 object_header_t hdr;
72 appinfo_t *lpAppInfo;
73 int sndSocket;
74 int lstnSocket;
75 int pasvSocket; /* data socket connected by us in case of passive FTP */
76 ftp_file_t *download_in_progress;
77 struct sockaddr_in socketAddress;
78 struct sockaddr_in lstnSocketAddress;
79 LPWSTR servername;
80 INTERNET_PORT serverport;
81 LPWSTR lpszPassword;
82 LPWSTR lpszUserName;
85 typedef struct
87 BOOL bIsDirectory;
88 LPWSTR lpszName;
89 DWORD nSize;
90 SYSTEMTIME tmLastModified;
91 unsigned short permissions;
92 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
94 typedef struct
96 object_header_t hdr;
97 ftp_session_t *lpFtpSession;
98 DWORD index;
99 DWORD size;
100 LPFILEPROPERTIESW lpafp;
101 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
103 #define DATA_PACKET_SIZE 0x2000
104 #define szCRLF "\r\n"
105 #define MAX_BACKLOG 5
107 /* Testing shows that Windows only accepts dwFlags where the last
108 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
110 #define FTP_CONDITION_MASK 0x0007
112 typedef enum {
113 /* FTP commands with arguments. */
114 FTP_CMD_ACCT,
115 FTP_CMD_CWD,
116 FTP_CMD_DELE,
117 FTP_CMD_MKD,
118 FTP_CMD_PASS,
119 FTP_CMD_PORT,
120 FTP_CMD_RETR,
121 FTP_CMD_RMD,
122 FTP_CMD_RNFR,
123 FTP_CMD_RNTO,
124 FTP_CMD_STOR,
125 FTP_CMD_TYPE,
126 FTP_CMD_USER,
127 FTP_CMD_SIZE,
129 /* FTP commands without arguments. */
130 FTP_CMD_ABOR,
131 FTP_CMD_LIST,
132 FTP_CMD_NLST,
133 FTP_CMD_PASV,
134 FTP_CMD_PWD,
135 FTP_CMD_QUIT,
136 } FTP_COMMAND;
138 static const CHAR *const szFtpCommands[] = {
139 "ACCT",
140 "CWD",
141 "DELE",
142 "MKD",
143 "PASS",
144 "PORT",
145 "RETR",
146 "RMD",
147 "RNFR",
148 "RNTO",
149 "STOR",
150 "TYPE",
151 "USER",
152 "SIZE",
153 "ABOR",
154 "LIST",
155 "NLST",
156 "PASV",
157 "PWD",
158 "QUIT",
161 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
163 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
164 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext);
165 static BOOL FTP_SendStore(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
166 static BOOL FTP_GetDataSocket(ftp_session_t*, LPINT nDataSocket);
167 static BOOL FTP_SendData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
168 static INT FTP_ReceiveResponse(ftp_session_t*, DWORD_PTR dwContext);
169 static BOOL FTP_SendRetrieve(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
170 static BOOL FTP_RetrieveFileData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
171 static BOOL FTP_InitListenSocket(ftp_session_t*);
172 static BOOL FTP_ConnectToHost(ftp_session_t*);
173 static BOOL FTP_SendPassword(ftp_session_t*);
174 static BOOL FTP_SendAccount(ftp_session_t*);
175 static BOOL FTP_SendType(ftp_session_t*, DWORD dwType);
176 static BOOL FTP_SendPort(ftp_session_t*);
177 static BOOL FTP_DoPassive(ftp_session_t*);
178 static BOOL FTP_SendPortOrPasv(ftp_session_t*);
179 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
180 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
181 static BOOL FTP_ParseDirectory(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
182 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
183 static HINTERNET FTP_ReceiveFileList(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
184 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
185 static DWORD FTP_SetResponseError(DWORD dwResponse);
186 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
187 static BOOL FTP_FtpPutFileW(ftp_session_t*, LPCWSTR lpszLocalFile,
188 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
189 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
190 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
191 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t*,
192 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
193 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t*, LPWSTR lpszCurrentDirectory,
194 LPDWORD lpdwCurrentDirectory);
195 static BOOL FTP_FtpRenameFileW(ftp_session_t*, LPCWSTR lpszSrc, LPCWSTR lpszDest);
196 static BOOL FTP_FtpRemoveDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
197 static BOOL FTP_FtpDeleteFileW(ftp_session_t*, LPCWSTR lpszFileName);
198 static BOOL FTP_FtpGetFileW(ftp_session_t*, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
199 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
200 DWORD_PTR dwContext);
202 /* A temporary helper until we get rid of INTERNET_GetLastError calls */
203 static BOOL res_to_le(DWORD res)
205 if(res != ERROR_SUCCESS)
206 INTERNET_SetLastError(res);
207 return res == ERROR_SUCCESS;
210 /***********************************************************************
211 * FtpPutFileA (WININET.@)
213 * Uploads a file to the FTP server
215 * RETURNS
216 * TRUE on success
217 * FALSE on failure
220 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
221 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
223 LPWSTR lpwzLocalFile;
224 LPWSTR lpwzNewRemoteFile;
225 BOOL ret;
227 lpwzLocalFile = heap_strdupAtoW(lpszLocalFile);
228 lpwzNewRemoteFile = heap_strdupAtoW(lpszNewRemoteFile);
229 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
230 dwFlags, dwContext);
231 heap_free(lpwzLocalFile);
232 heap_free(lpwzNewRemoteFile);
233 return ret;
236 typedef struct {
237 task_header_t hdr;
238 WCHAR *local_file;
239 WCHAR *remote_file;
240 DWORD flags;
241 DWORD_PTR context;
242 } put_file_task_t;
244 static void AsyncFtpPutFileProc(task_header_t *hdr)
246 put_file_task_t *task = (put_file_task_t*)hdr;
247 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
249 TRACE("%p\n", session);
251 FTP_FtpPutFileW(session, task->local_file, task->remote_file,
252 task->flags, task->context);
254 heap_free(task->local_file);
255 heap_free(task->remote_file);
258 /***********************************************************************
259 * FtpPutFileW (WININET.@)
261 * Uploads a file to the FTP server
263 * RETURNS
264 * TRUE on success
265 * FALSE on failure
268 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
269 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
271 ftp_session_t *lpwfs;
272 appinfo_t *hIC = NULL;
273 BOOL r = FALSE;
275 if (!lpszLocalFile || !lpszNewRemoteFile)
277 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
278 return FALSE;
281 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
282 if (!lpwfs)
284 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
285 return FALSE;
288 if (WH_HFTPSESSION != lpwfs->hdr.htype)
290 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
291 goto lend;
294 if (lpwfs->download_in_progress != NULL)
296 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
297 goto lend;
300 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
302 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
303 goto lend;
306 hIC = lpwfs->lpAppInfo;
307 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
309 put_file_task_t *task = alloc_async_task(&lpwfs->hdr, AsyncFtpPutFileProc, sizeof(*task));
311 task->local_file = heap_strdupW(lpszLocalFile);
312 task->remote_file = heap_strdupW(lpszNewRemoteFile);
313 task->flags = dwFlags;
314 task->context = dwContext;
316 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
318 else
320 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
321 lpszNewRemoteFile, dwFlags, dwContext);
324 lend:
325 WININET_Release( &lpwfs->hdr );
327 return r;
330 /***********************************************************************
331 * FTP_FtpPutFileW (Internal)
333 * Uploads a file to the FTP server
335 * RETURNS
336 * TRUE on success
337 * FALSE on failure
340 static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
341 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
343 HANDLE hFile;
344 BOOL bSuccess = FALSE;
345 appinfo_t *hIC = NULL;
346 INT nResCode;
348 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
350 /* Clear any error information */
351 INTERNET_SetLastError(0);
353 /* Open file to be uploaded */
354 if (INVALID_HANDLE_VALUE ==
355 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
356 /* Let CreateFile set the appropriate error */
357 return FALSE;
359 hIC = lpwfs->lpAppInfo;
361 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
363 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
365 INT nDataSocket;
367 /* Get data socket to server */
368 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
370 FTP_SendData(lpwfs, nDataSocket, hFile);
371 closesocket(nDataSocket);
372 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
373 if (nResCode)
375 if (nResCode == 226)
376 bSuccess = TRUE;
377 else
378 FTP_SetResponseError(nResCode);
383 if (lpwfs->lstnSocket != -1)
385 closesocket(lpwfs->lstnSocket);
386 lpwfs->lstnSocket = -1;
389 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
391 INTERNET_ASYNC_RESULT iar;
393 iar.dwResult = (DWORD)bSuccess;
394 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
395 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
396 &iar, sizeof(INTERNET_ASYNC_RESULT));
399 CloseHandle(hFile);
401 return bSuccess;
405 /***********************************************************************
406 * FtpSetCurrentDirectoryA (WININET.@)
408 * Change the working directory on the FTP server
410 * RETURNS
411 * TRUE on success
412 * FALSE on failure
415 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
417 LPWSTR lpwzDirectory;
418 BOOL ret;
420 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
421 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
422 heap_free(lpwzDirectory);
423 return ret;
426 typedef struct {
427 task_header_t hdr;
428 WCHAR *directory;
429 } directory_task_t;
431 static void AsyncFtpSetCurrentDirectoryProc(task_header_t *hdr)
433 directory_task_t *task = (directory_task_t*)hdr;
434 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
436 TRACE("%p\n", session);
438 FTP_FtpSetCurrentDirectoryW(session, task->directory);
439 heap_free(task->directory);
442 /***********************************************************************
443 * FtpSetCurrentDirectoryW (WININET.@)
445 * Change the working directory on the FTP server
447 * RETURNS
448 * TRUE on success
449 * FALSE on failure
452 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
454 ftp_session_t *lpwfs = NULL;
455 appinfo_t *hIC = NULL;
456 BOOL r = FALSE;
458 if (!lpszDirectory)
460 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
461 goto lend;
464 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
465 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
467 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
468 goto lend;
471 if (lpwfs->download_in_progress != NULL)
473 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
474 goto lend;
477 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
479 hIC = lpwfs->lpAppInfo;
480 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
482 directory_task_t *task;
484 task = alloc_async_task(&lpwfs->hdr, AsyncFtpSetCurrentDirectoryProc, sizeof(*task));
485 task->directory = heap_strdupW(lpszDirectory);
487 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
489 else
491 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
494 lend:
495 if( lpwfs )
496 WININET_Release( &lpwfs->hdr );
498 return r;
502 /***********************************************************************
503 * FTP_FtpSetCurrentDirectoryW (Internal)
505 * Change the working directory on the FTP server
507 * RETURNS
508 * TRUE on success
509 * FALSE on failure
512 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
514 INT nResCode;
515 appinfo_t *hIC = NULL;
516 BOOL bSuccess = FALSE;
518 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
520 /* Clear any error information */
521 INTERNET_SetLastError(0);
523 hIC = lpwfs->lpAppInfo;
524 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
525 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
526 goto lend;
528 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
530 if (nResCode)
532 if (nResCode == 250)
533 bSuccess = TRUE;
534 else
535 FTP_SetResponseError(nResCode);
538 lend:
539 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
541 INTERNET_ASYNC_RESULT iar;
543 iar.dwResult = bSuccess;
544 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
545 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
546 &iar, sizeof(INTERNET_ASYNC_RESULT));
548 return bSuccess;
552 /***********************************************************************
553 * FtpCreateDirectoryA (WININET.@)
555 * Create new directory on the FTP server
557 * RETURNS
558 * TRUE on success
559 * FALSE on failure
562 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
564 LPWSTR lpwzDirectory;
565 BOOL ret;
567 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
568 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
569 heap_free(lpwzDirectory);
570 return ret;
574 static void AsyncFtpCreateDirectoryProc(task_header_t *hdr)
576 directory_task_t *task = (directory_task_t*)hdr;
577 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
579 TRACE(" %p\n", session);
581 FTP_FtpCreateDirectoryW(session, task->directory);
582 heap_free(task->directory);
585 /***********************************************************************
586 * FtpCreateDirectoryW (WININET.@)
588 * Create new directory on the FTP server
590 * RETURNS
591 * TRUE on success
592 * FALSE on failure
595 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
597 ftp_session_t *lpwfs;
598 appinfo_t *hIC = NULL;
599 BOOL r = FALSE;
601 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
602 if (!lpwfs)
604 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
605 return FALSE;
608 if (WH_HFTPSESSION != lpwfs->hdr.htype)
610 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
611 goto lend;
614 if (lpwfs->download_in_progress != NULL)
616 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
617 goto lend;
620 if (!lpszDirectory)
622 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
623 goto lend;
626 hIC = lpwfs->lpAppInfo;
627 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
629 directory_task_t *task;
631 task = alloc_async_task(&lpwfs->hdr, AsyncFtpCreateDirectoryProc, sizeof(*task));
632 task->directory = heap_strdupW(lpszDirectory);
634 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
636 else
638 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
640 lend:
641 WININET_Release( &lpwfs->hdr );
643 return r;
647 /***********************************************************************
648 * FTP_FtpCreateDirectoryW (Internal)
650 * Create new directory on the FTP server
652 * RETURNS
653 * TRUE on success
654 * FALSE on failure
657 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
659 INT nResCode;
660 BOOL bSuccess = FALSE;
661 appinfo_t *hIC = NULL;
663 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
665 /* Clear any error information */
666 INTERNET_SetLastError(0);
668 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
669 goto lend;
671 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
672 if (nResCode)
674 if (nResCode == 257)
675 bSuccess = TRUE;
676 else
677 FTP_SetResponseError(nResCode);
680 lend:
681 hIC = lpwfs->lpAppInfo;
682 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
684 INTERNET_ASYNC_RESULT iar;
686 iar.dwResult = (DWORD)bSuccess;
687 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
688 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
689 &iar, sizeof(INTERNET_ASYNC_RESULT));
692 return bSuccess;
695 /***********************************************************************
696 * FtpFindFirstFileA (WININET.@)
698 * Search the specified directory
700 * RETURNS
701 * HINTERNET on success
702 * NULL on failure
705 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
706 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
708 LPWSTR lpwzSearchFile;
709 WIN32_FIND_DATAW wfd;
710 LPWIN32_FIND_DATAW lpFindFileDataW;
711 HINTERNET ret;
713 lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
714 lpFindFileDataW = lpFindFileData?&wfd:NULL;
715 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
716 heap_free(lpwzSearchFile);
718 if (ret && lpFindFileData)
719 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
721 return ret;
724 typedef struct {
725 task_header_t hdr;
726 WCHAR *search_file;
727 WIN32_FIND_DATAW *find_file_data;
728 DWORD flags;
729 DWORD_PTR context;
730 } find_first_file_task_t;
732 static void AsyncFtpFindFirstFileProc(task_header_t *hdr)
734 find_first_file_task_t *task = (find_first_file_task_t*)hdr;
735 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
737 TRACE("%p\n", session);
739 FTP_FtpFindFirstFileW(session, task->search_file, task->find_file_data, task->flags, task->context);
740 heap_free(task->search_file);
743 /***********************************************************************
744 * FtpFindFirstFileW (WININET.@)
746 * Search the specified directory
748 * RETURNS
749 * HINTERNET on success
750 * NULL on failure
753 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
754 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
756 ftp_session_t *lpwfs;
757 appinfo_t *hIC = NULL;
758 HINTERNET r = NULL;
760 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
761 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
763 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
764 goto lend;
767 if (lpwfs->download_in_progress != NULL)
769 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
770 goto lend;
773 hIC = lpwfs->lpAppInfo;
774 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
776 find_first_file_task_t *task;
778 task = alloc_async_task(&lpwfs->hdr, AsyncFtpFindFirstFileProc, sizeof(*task));
779 task->search_file = heap_strdupW(lpszSearchFile);
780 task->find_file_data = lpFindFileData;
781 task->flags = dwFlags;
782 task->context = dwContext;
784 INTERNET_AsyncCall(&task->hdr);
785 r = NULL;
787 else
789 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
790 dwFlags, dwContext);
792 lend:
793 if( lpwfs )
794 WININET_Release( &lpwfs->hdr );
796 return r;
800 /***********************************************************************
801 * FTP_FtpFindFirstFileW (Internal)
803 * Search the specified directory
805 * RETURNS
806 * HINTERNET on success
807 * NULL on failure
810 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
811 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
813 INT nResCode;
814 appinfo_t *hIC = NULL;
815 HINTERNET hFindNext = NULL;
816 LPWSTR lpszSearchPath = NULL;
818 TRACE("\n");
820 /* Clear any error information */
821 INTERNET_SetLastError(0);
823 if (!FTP_InitListenSocket(lpwfs))
824 goto lend;
826 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
827 goto lend;
829 if (!FTP_SendPortOrPasv(lpwfs))
830 goto lend;
832 /* split search path into file and path */
833 if (lpszSearchFile)
835 LPCWSTR name = lpszSearchFile, p;
836 if ((p = wcsrchr( name, '\\' ))) name = p + 1;
837 if ((p = wcsrchr( name, '/' ))) name = p + 1;
838 if (name != lpszSearchFile)
840 lpszSearchPath = heap_strndupW(lpszSearchFile, name - lpszSearchFile);
841 lpszSearchFile = name;
845 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, lpszSearchPath,
846 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
847 goto lend;
849 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
850 if (nResCode)
852 if (nResCode == 125 || nResCode == 150)
854 INT nDataSocket;
856 /* Get data socket to server */
857 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
859 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
860 closesocket(nDataSocket);
861 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
862 if (nResCode != 226 && nResCode != 250)
863 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
866 else
867 FTP_SetResponseError(nResCode);
870 lend:
871 heap_free(lpszSearchPath);
873 if (lpwfs->lstnSocket != -1)
875 closesocket(lpwfs->lstnSocket);
876 lpwfs->lstnSocket = -1;
879 hIC = lpwfs->lpAppInfo;
880 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
882 INTERNET_ASYNC_RESULT iar;
884 if (hFindNext)
886 iar.dwResult = (DWORD_PTR)hFindNext;
887 iar.dwError = ERROR_SUCCESS;
888 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
889 &iar, sizeof(INTERNET_ASYNC_RESULT));
892 iar.dwResult = (DWORD_PTR)hFindNext;
893 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
894 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
895 &iar, sizeof(INTERNET_ASYNC_RESULT));
898 return hFindNext;
902 /***********************************************************************
903 * FtpGetCurrentDirectoryA (WININET.@)
905 * Retrieves the current directory
907 * RETURNS
908 * TRUE on success
909 * FALSE on failure
912 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
913 LPDWORD lpdwCurrentDirectory)
915 WCHAR *dir = NULL;
916 DWORD len;
917 BOOL ret;
919 if(lpdwCurrentDirectory) {
920 len = *lpdwCurrentDirectory;
921 if(lpszCurrentDirectory)
923 dir = heap_alloc(len * sizeof(WCHAR));
924 if (NULL == dir)
926 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
927 return FALSE;
931 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
933 if (ret && lpszCurrentDirectory)
934 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
936 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
937 heap_free(dir);
938 return ret;
941 typedef struct {
942 task_header_t hdr;
943 WCHAR *directory;
944 DWORD *directory_len;
945 } get_current_dir_task_t;
947 static void AsyncFtpGetCurrentDirectoryProc(task_header_t *hdr)
949 get_current_dir_task_t *task = (get_current_dir_task_t*)hdr;
950 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
952 TRACE("%p\n", session);
954 FTP_FtpGetCurrentDirectoryW(session, task->directory, task->directory_len);
957 /***********************************************************************
958 * FtpGetCurrentDirectoryW (WININET.@)
960 * Retrieves the current directory
962 * RETURNS
963 * TRUE on success
964 * FALSE on failure
967 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
968 LPDWORD lpdwCurrentDirectory)
970 ftp_session_t *lpwfs;
971 appinfo_t *hIC = NULL;
972 BOOL r = FALSE;
974 TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
976 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
977 if (NULL == lpwfs)
979 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
980 goto lend;
983 if (WH_HFTPSESSION != lpwfs->hdr.htype)
985 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
986 goto lend;
989 if (!lpdwCurrentDirectory)
991 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
992 goto lend;
995 if (lpszCurrentDirectory == NULL)
997 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
998 goto lend;
1001 if (lpwfs->download_in_progress != NULL)
1003 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1004 goto lend;
1007 hIC = lpwfs->lpAppInfo;
1008 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1010 get_current_dir_task_t *task;
1012 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetCurrentDirectoryProc, sizeof(*task));
1013 task->directory = lpszCurrentDirectory;
1014 task->directory_len = lpdwCurrentDirectory;
1016 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1018 else
1020 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
1021 lpdwCurrentDirectory);
1024 lend:
1025 if( lpwfs )
1026 WININET_Release( &lpwfs->hdr );
1028 return r;
1032 /***********************************************************************
1033 * FTP_FtpGetCurrentDirectoryW (Internal)
1035 * Retrieves the current directory
1037 * RETURNS
1038 * TRUE on success
1039 * FALSE on failure
1042 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
1043 LPDWORD lpdwCurrentDirectory)
1045 INT nResCode;
1046 appinfo_t *hIC = NULL;
1047 BOOL bSuccess = FALSE;
1049 /* Clear any error information */
1050 INTERNET_SetLastError(0);
1052 hIC = lpwfs->lpAppInfo;
1053 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1054 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1055 goto lend;
1057 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1058 if (nResCode)
1060 if (nResCode == 257) /* Extract directory name */
1062 DWORD firstpos, lastpos, len;
1063 LPWSTR lpszResponseBuffer = heap_strdupAtoW(INTERNET_GetResponseBuffer());
1065 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1067 if ('"' == lpszResponseBuffer[lastpos])
1069 if (!firstpos)
1070 firstpos = lastpos;
1071 else
1072 break;
1075 len = lastpos - firstpos;
1076 if (*lpdwCurrentDirectory >= len)
1078 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1079 lpszCurrentDirectory[len - 1] = 0;
1080 *lpdwCurrentDirectory = len;
1081 bSuccess = TRUE;
1083 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1085 heap_free(lpszResponseBuffer);
1087 else
1088 FTP_SetResponseError(nResCode);
1091 lend:
1092 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1094 INTERNET_ASYNC_RESULT iar;
1096 iar.dwResult = bSuccess;
1097 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1098 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1099 &iar, sizeof(INTERNET_ASYNC_RESULT));
1102 return bSuccess;
1106 /***********************************************************************
1107 * FTPFILE_Destroy(internal)
1109 * Closes the file transfer handle. This also 'cleans' the data queue of
1110 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1113 static void FTPFILE_Destroy(object_header_t *hdr)
1115 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1116 ftp_session_t *lpwfs = lpwh->lpFtpSession;
1117 INT nResCode;
1119 TRACE("\n");
1121 if (lpwh->cache_file_handle != INVALID_HANDLE_VALUE)
1122 CloseHandle(lpwh->cache_file_handle);
1124 heap_free(lpwh->cache_file);
1126 if (!lpwh->session_deleted)
1127 lpwfs->download_in_progress = NULL;
1129 if (lpwh->nDataSocket != -1)
1130 closesocket(lpwh->nDataSocket);
1132 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1133 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1135 WININET_Release(&lpwh->lpFtpSession->hdr);
1138 static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1140 switch(option) {
1141 case INTERNET_OPTION_HANDLE_TYPE:
1142 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1144 if (*size < sizeof(ULONG))
1145 return ERROR_INSUFFICIENT_BUFFER;
1147 *size = sizeof(DWORD);
1148 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1149 return ERROR_SUCCESS;
1150 case INTERNET_OPTION_DATAFILE_NAME:
1152 DWORD required;
1153 ftp_file_t *file = (ftp_file_t *)hdr;
1155 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1157 if (!file->cache_file)
1159 *size = 0;
1160 return ERROR_INTERNET_ITEM_NOT_FOUND;
1162 if (unicode)
1164 required = (lstrlenW(file->cache_file) + 1) * sizeof(WCHAR);
1165 if (*size < required)
1166 return ERROR_INSUFFICIENT_BUFFER;
1168 *size = required;
1169 memcpy(buffer, file->cache_file, *size);
1170 return ERROR_SUCCESS;
1172 else
1174 required = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, NULL, 0, NULL, NULL);
1175 if (required > *size)
1176 return ERROR_INSUFFICIENT_BUFFER;
1178 *size = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, buffer, *size, NULL, NULL);
1179 return ERROR_SUCCESS;
1183 return INET_QueryOption(hdr, option, buffer, size, unicode);
1186 static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read,
1187 DWORD flags, DWORD_PTR context)
1189 ftp_file_t *file = (ftp_file_t*)hdr;
1190 int res;
1191 DWORD error;
1193 if (file->nDataSocket == -1)
1194 return ERROR_INTERNET_DISCONNECTED;
1196 /* FIXME: FTP should use NETCON_ stuff */
1197 res = sock_recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1198 *read = res>0 ? res : 0;
1200 error = res >= 0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME */
1201 if (error == ERROR_SUCCESS && file->cache_file)
1203 DWORD bytes_written;
1205 if (!WriteFile(file->cache_file_handle, buffer, *read, &bytes_written, NULL))
1206 WARN("WriteFile failed: %lu\n", GetLastError());
1208 return error;
1211 static DWORD FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1213 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1214 int res;
1216 res = sock_send(lpwh->nDataSocket, buffer, size, 0);
1218 *written = res>0 ? res : 0;
1219 return res >= 0 ? ERROR_SUCCESS : WSAGetLastError();
1222 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
1224 INTERNET_ASYNC_RESULT iar;
1225 BYTE buffer[4096];
1226 int available;
1228 TRACE("%p\n", file);
1230 available = sock_recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1232 if(available != -1) {
1233 iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1234 iar.dwError = first_notif ? 0 : available;
1235 }else {
1236 iar.dwResult = 0;
1237 iar.dwError = INTERNET_GetLastError();
1240 INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1241 sizeof(INTERNET_ASYNC_RESULT));
1244 static void FTPFILE_AsyncQueryDataAvailableProc(task_header_t *task)
1246 ftp_file_t *file = (ftp_file_t*)task->hdr;
1248 FTP_ReceiveRequestData(file, FALSE);
1251 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1253 ftp_file_t *file = (ftp_file_t*) hdr;
1254 ULONG unread = 0;
1255 int retval;
1257 TRACE("(%p %p %lx %Ix)\n", file, available, flags, ctx);
1259 retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
1260 if (!retval)
1261 TRACE("%ld bytes of queued, but unread data\n", unread);
1263 *available = unread;
1265 if(!unread) {
1266 BYTE byte;
1268 *available = 0;
1270 retval = sock_recv(file->nDataSocket, &byte, 1, MSG_PEEK);
1271 if(retval > 0) {
1272 task_header_t *task;
1274 task = alloc_async_task(&file->hdr, FTPFILE_AsyncQueryDataAvailableProc, sizeof(*task));
1275 INTERNET_AsyncCall(task);
1277 return ERROR_IO_PENDING;
1281 return ERROR_SUCCESS;
1284 static DWORD FTPFILE_LockRequestFile(object_header_t *hdr, req_file_t **ret)
1286 ftp_file_t *file = (ftp_file_t*)hdr;
1287 FIXME("%p\n", file);
1288 return ERROR_NOT_SUPPORTED;
1291 static const object_vtbl_t FTPFILEVtbl = {
1292 FTPFILE_Destroy,
1293 NULL,
1294 FTPFILE_QueryOption,
1295 INET_SetOption,
1296 FTPFILE_ReadFile,
1297 FTPFILE_WriteFile,
1298 FTPFILE_QueryDataAvailable,
1299 NULL,
1300 FTPFILE_LockRequestFile
1303 /***********************************************************************
1304 * FTP_FtpOpenFileW (Internal)
1306 * Open a remote file for writing or reading
1308 * RETURNS
1309 * HINTERNET handle on success
1310 * NULL on failure
1313 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1314 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1315 DWORD_PTR dwContext)
1317 INT nDataSocket;
1318 BOOL bSuccess = FALSE;
1319 ftp_file_t *lpwh = NULL;
1320 appinfo_t *hIC = NULL;
1322 TRACE("\n");
1324 /* Clear any error information */
1325 INTERNET_SetLastError(0);
1327 if (GENERIC_READ == fdwAccess)
1329 /* Set up socket to retrieve data */
1330 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1332 else if (GENERIC_WRITE == fdwAccess)
1334 /* Set up socket to send data */
1335 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1338 /* Get data socket to server */
1339 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1341 lpwh = alloc_object(&lpwfs->hdr, &FTPFILEVtbl, sizeof(ftp_file_t));
1342 lpwh->hdr.htype = WH_HFILE;
1343 lpwh->hdr.dwFlags = dwFlags;
1344 lpwh->hdr.dwContext = dwContext;
1345 lpwh->nDataSocket = nDataSocket;
1346 lpwh->cache_file = NULL;
1347 lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1348 lpwh->session_deleted = FALSE;
1350 WININET_AddRef( &lpwfs->hdr );
1351 lpwh->lpFtpSession = lpwfs;
1352 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1354 /* Indicate that a download is currently in progress */
1355 lpwfs->download_in_progress = lpwh;
1358 if (lpwfs->lstnSocket != -1)
1360 closesocket(lpwfs->lstnSocket);
1361 lpwfs->lstnSocket = -1;
1364 if (bSuccess && fdwAccess == GENERIC_READ)
1366 WCHAR filename[MAX_PATH + 1];
1367 URL_COMPONENTSW uc;
1368 DWORD len;
1370 memset(&uc, 0, sizeof(uc));
1371 uc.dwStructSize = sizeof(uc);
1372 uc.nScheme = INTERNET_SCHEME_FTP;
1373 uc.lpszHostName = lpwfs->servername;
1374 uc.nPort = lpwfs->serverport;
1375 uc.lpszUserName = lpwfs->lpszUserName;
1376 uc.lpszUrlPath = heap_strdupW(lpszFileName);
1378 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1380 WCHAR *url = heap_alloc(len * sizeof(WCHAR));
1382 if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1384 lpwh->cache_file = heap_strdupW(filename);
1385 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1386 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1387 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1389 WARN("Could not create cache file: %lu\n", GetLastError());
1390 heap_free(lpwh->cache_file);
1391 lpwh->cache_file = NULL;
1394 heap_free(url);
1396 heap_free(uc.lpszUrlPath);
1399 hIC = lpwfs->lpAppInfo;
1400 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1402 INTERNET_ASYNC_RESULT iar;
1404 if (lpwh)
1406 iar.dwResult = (DWORD_PTR)lpwh->hdr.hInternet;
1407 iar.dwError = ERROR_SUCCESS;
1408 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1409 &iar, sizeof(INTERNET_ASYNC_RESULT));
1412 if(bSuccess) {
1413 FTP_ReceiveRequestData(lpwh, TRUE);
1414 }else {
1415 iar.dwResult = 0;
1416 iar.dwError = INTERNET_GetLastError();
1417 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1418 &iar, sizeof(INTERNET_ASYNC_RESULT));
1422 if(!bSuccess)
1423 return FALSE;
1425 return lpwh->hdr.hInternet;
1429 /***********************************************************************
1430 * FtpOpenFileA (WININET.@)
1432 * Open a remote file for writing or reading
1434 * RETURNS
1435 * HINTERNET handle on success
1436 * NULL on failure
1439 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1440 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1441 DWORD_PTR dwContext)
1443 LPWSTR lpwzFileName;
1444 HINTERNET ret;
1446 lpwzFileName = heap_strdupAtoW(lpszFileName);
1447 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1448 heap_free(lpwzFileName);
1449 return ret;
1452 typedef struct {
1453 task_header_t hdr;
1454 WCHAR *file_name;
1455 DWORD access;
1456 DWORD flags;
1457 DWORD_PTR context;
1458 } open_file_task_t;
1460 static void AsyncFtpOpenFileProc(task_header_t *hdr)
1462 open_file_task_t *task = (open_file_task_t*)hdr;
1463 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1465 TRACE("%p\n", session);
1467 FTP_FtpOpenFileW(session, task->file_name, task->access, task->flags, task->context);
1468 heap_free(task->file_name);
1471 /***********************************************************************
1472 * FtpOpenFileW (WININET.@)
1474 * Open a remote file for writing or reading
1476 * RETURNS
1477 * HINTERNET handle on success
1478 * NULL on failure
1481 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1482 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1483 DWORD_PTR dwContext)
1485 ftp_session_t *lpwfs;
1486 appinfo_t *hIC = NULL;
1487 HINTERNET r = NULL;
1489 TRACE("(%p,%s,0x%08lx,0x%08lx,0x%08Ix)\n", hFtpSession,
1490 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1492 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1493 if (!lpwfs)
1495 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1496 return FALSE;
1499 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1501 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1502 goto lend;
1505 if ((!lpszFileName) ||
1506 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1507 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1509 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1510 goto lend;
1513 if (lpwfs->download_in_progress != NULL)
1515 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1516 goto lend;
1519 hIC = lpwfs->lpAppInfo;
1520 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1522 open_file_task_t *task;
1524 task = alloc_async_task(&lpwfs->hdr, AsyncFtpOpenFileProc, sizeof(*task));
1525 task->file_name = heap_strdupW(lpszFileName);
1526 task->access = fdwAccess;
1527 task->flags = dwFlags;
1528 task->context = dwContext;
1530 INTERNET_AsyncCall(&task->hdr);
1531 r = NULL;
1533 else
1535 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1538 lend:
1539 WININET_Release( &lpwfs->hdr );
1541 return r;
1545 /***********************************************************************
1546 * FtpGetFileA (WININET.@)
1548 * Retrieve file from the FTP server
1550 * RETURNS
1551 * TRUE on success
1552 * FALSE on failure
1555 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1556 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1557 DWORD_PTR dwContext)
1559 LPWSTR lpwzRemoteFile;
1560 LPWSTR lpwzNewFile;
1561 BOOL ret;
1563 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1564 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1565 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1566 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1567 heap_free(lpwzRemoteFile);
1568 heap_free(lpwzNewFile);
1569 return ret;
1572 typedef struct {
1573 task_header_t hdr;
1574 WCHAR *remote_file;
1575 WCHAR *new_file;
1576 BOOL fail_if_exists;
1577 DWORD local_attr;
1578 DWORD flags;
1579 DWORD_PTR context;
1580 } get_file_task_t;
1582 static void AsyncFtpGetFileProc(task_header_t *hdr)
1584 get_file_task_t *task = (get_file_task_t*)hdr;
1585 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1587 TRACE("%p\n", session);
1589 FTP_FtpGetFileW(session, task->remote_file, task->new_file, task->fail_if_exists,
1590 task->local_attr, task->flags, task->context);
1591 heap_free(task->remote_file);
1592 heap_free(task->new_file);
1596 /***********************************************************************
1597 * FtpGetFileW (WININET.@)
1599 * Retrieve file from the FTP server
1601 * RETURNS
1602 * TRUE on success
1603 * FALSE on failure
1606 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1607 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1608 DWORD_PTR dwContext)
1610 ftp_session_t *lpwfs;
1611 appinfo_t *hIC = NULL;
1612 BOOL r = FALSE;
1614 if (!lpszRemoteFile || !lpszNewFile)
1616 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1617 return FALSE;
1620 lpwfs = (ftp_session_t*) get_handle_object( hInternet );
1621 if (!lpwfs)
1623 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1624 return FALSE;
1627 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1629 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1630 goto lend;
1633 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1635 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1636 goto lend;
1639 if (lpwfs->download_in_progress != NULL)
1641 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1642 goto lend;
1645 hIC = lpwfs->lpAppInfo;
1646 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1648 get_file_task_t *task;
1650 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetFileProc, sizeof(*task));
1651 task->remote_file = heap_strdupW(lpszRemoteFile);
1652 task->new_file = heap_strdupW(lpszNewFile);
1653 task->local_attr = dwLocalFlagsAttribute;
1654 task->fail_if_exists = fFailIfExists;
1655 task->flags = dwInternetFlags;
1656 task->context = dwContext;
1658 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1660 else
1662 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1663 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1666 lend:
1667 WININET_Release( &lpwfs->hdr );
1669 return r;
1673 /***********************************************************************
1674 * FTP_FtpGetFileW (Internal)
1676 * Retrieve file from the FTP server
1678 * RETURNS
1679 * TRUE on success
1680 * FALSE on failure
1683 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1684 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1685 DWORD_PTR dwContext)
1687 BOOL bSuccess = FALSE;
1688 HANDLE hFile;
1689 appinfo_t *hIC = NULL;
1691 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1693 /* Clear any error information */
1694 INTERNET_SetLastError(0);
1696 /* Ensure we can write to lpszNewfile by opening it */
1697 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1698 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1699 if (INVALID_HANDLE_VALUE == hFile)
1700 return FALSE;
1702 /* Set up socket to retrieve data */
1703 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1705 INT nDataSocket;
1707 /* Get data socket to server */
1708 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1710 INT nResCode;
1712 /* Receive data */
1713 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1714 closesocket(nDataSocket);
1716 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1717 if (nResCode)
1719 if (nResCode == 226)
1720 bSuccess = TRUE;
1721 else
1722 FTP_SetResponseError(nResCode);
1727 if (lpwfs->lstnSocket != -1)
1729 closesocket(lpwfs->lstnSocket);
1730 lpwfs->lstnSocket = -1;
1733 CloseHandle(hFile);
1735 hIC = lpwfs->lpAppInfo;
1736 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1738 INTERNET_ASYNC_RESULT iar;
1740 iar.dwResult = (DWORD)bSuccess;
1741 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1742 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1743 &iar, sizeof(INTERNET_ASYNC_RESULT));
1746 if (!bSuccess) DeleteFileW(lpszNewFile);
1747 return bSuccess;
1750 /***********************************************************************
1751 * FtpGetFileSize (WININET.@)
1753 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1755 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1757 if (lpdwFileSizeHigh)
1758 *lpdwFileSizeHigh = 0;
1760 return 0;
1763 /***********************************************************************
1764 * FtpDeleteFileA (WININET.@)
1766 * Delete a file on the ftp server
1768 * RETURNS
1769 * TRUE on success
1770 * FALSE on failure
1773 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1775 LPWSTR lpwzFileName;
1776 BOOL ret;
1778 lpwzFileName = heap_strdupAtoW(lpszFileName);
1779 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1780 heap_free(lpwzFileName);
1781 return ret;
1784 typedef struct {
1785 task_header_t hdr;
1786 WCHAR *file_name;
1787 } delete_file_task_t;
1789 static void AsyncFtpDeleteFileProc(task_header_t *hdr)
1791 delete_file_task_t *task = (delete_file_task_t*)hdr;
1792 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1794 TRACE("%p\n", session);
1796 FTP_FtpDeleteFileW(session, task->file_name);
1797 heap_free(task->file_name);
1800 /***********************************************************************
1801 * FtpDeleteFileW (WININET.@)
1803 * Delete a file on the ftp server
1805 * RETURNS
1806 * TRUE on success
1807 * FALSE on failure
1810 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1812 ftp_session_t *lpwfs;
1813 appinfo_t *hIC = NULL;
1814 BOOL r = FALSE;
1816 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1817 if (!lpwfs)
1819 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1820 return FALSE;
1823 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1825 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1826 goto lend;
1829 if (lpwfs->download_in_progress != NULL)
1831 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1832 goto lend;
1835 if (!lpszFileName)
1837 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1838 goto lend;
1841 hIC = lpwfs->lpAppInfo;
1842 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1844 delete_file_task_t *task;
1846 task = alloc_async_task(&lpwfs->hdr, AsyncFtpDeleteFileProc, sizeof(*task));
1847 task->file_name = heap_strdupW(lpszFileName);
1849 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1851 else
1853 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1856 lend:
1857 WININET_Release( &lpwfs->hdr );
1859 return r;
1862 /***********************************************************************
1863 * FTP_FtpDeleteFileW (Internal)
1865 * Delete a file on the ftp server
1867 * RETURNS
1868 * TRUE on success
1869 * FALSE on failure
1872 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1874 INT nResCode;
1875 BOOL bSuccess = FALSE;
1876 appinfo_t *hIC = NULL;
1878 TRACE("%p\n", lpwfs);
1880 /* Clear any error information */
1881 INTERNET_SetLastError(0);
1883 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1884 goto lend;
1886 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1887 if (nResCode)
1889 if (nResCode == 250)
1890 bSuccess = TRUE;
1891 else
1892 FTP_SetResponseError(nResCode);
1894 lend:
1895 hIC = lpwfs->lpAppInfo;
1896 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1898 INTERNET_ASYNC_RESULT iar;
1900 iar.dwResult = (DWORD)bSuccess;
1901 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1902 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1903 &iar, sizeof(INTERNET_ASYNC_RESULT));
1906 return bSuccess;
1910 /***********************************************************************
1911 * FtpRemoveDirectoryA (WININET.@)
1913 * Remove a directory on the ftp server
1915 * RETURNS
1916 * TRUE on success
1917 * FALSE on failure
1920 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1922 LPWSTR lpwzDirectory;
1923 BOOL ret;
1925 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1926 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1927 heap_free(lpwzDirectory);
1928 return ret;
1931 static void AsyncFtpRemoveDirectoryProc(task_header_t *hdr)
1933 directory_task_t *task = (directory_task_t*)hdr;
1934 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1936 TRACE("%p\n", session);
1938 FTP_FtpRemoveDirectoryW(session, task->directory);
1939 heap_free(task->directory);
1942 /***********************************************************************
1943 * FtpRemoveDirectoryW (WININET.@)
1945 * Remove a directory on the ftp server
1947 * RETURNS
1948 * TRUE on success
1949 * FALSE on failure
1952 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1954 ftp_session_t *lpwfs;
1955 appinfo_t *hIC = NULL;
1956 BOOL r = FALSE;
1958 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1959 if (!lpwfs)
1961 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1962 return FALSE;
1965 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1967 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1968 goto lend;
1971 if (lpwfs->download_in_progress != NULL)
1973 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1974 goto lend;
1977 if (!lpszDirectory)
1979 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1980 goto lend;
1983 hIC = lpwfs->lpAppInfo;
1984 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1986 directory_task_t *task;
1988 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRemoveDirectoryProc, sizeof(*task));
1989 task->directory = heap_strdupW(lpszDirectory);
1991 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1993 else
1995 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1998 lend:
1999 WININET_Release( &lpwfs->hdr );
2001 return r;
2004 /***********************************************************************
2005 * FTP_FtpRemoveDirectoryW (Internal)
2007 * Remove a directory on the ftp server
2009 * RETURNS
2010 * TRUE on success
2011 * FALSE on failure
2014 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
2016 INT nResCode;
2017 BOOL bSuccess = FALSE;
2018 appinfo_t *hIC = NULL;
2020 TRACE("\n");
2022 /* Clear any error information */
2023 INTERNET_SetLastError(0);
2025 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2026 goto lend;
2028 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2029 if (nResCode)
2031 if (nResCode == 250)
2032 bSuccess = TRUE;
2033 else
2034 FTP_SetResponseError(nResCode);
2037 lend:
2038 hIC = lpwfs->lpAppInfo;
2039 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2041 INTERNET_ASYNC_RESULT iar;
2043 iar.dwResult = (DWORD)bSuccess;
2044 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2045 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2046 &iar, sizeof(INTERNET_ASYNC_RESULT));
2049 return bSuccess;
2053 /***********************************************************************
2054 * FtpRenameFileA (WININET.@)
2056 * Rename a file on the ftp server
2058 * RETURNS
2059 * TRUE on success
2060 * FALSE on failure
2063 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2065 LPWSTR lpwzSrc;
2066 LPWSTR lpwzDest;
2067 BOOL ret;
2069 lpwzSrc = heap_strdupAtoW(lpszSrc);
2070 lpwzDest = heap_strdupAtoW(lpszDest);
2071 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2072 heap_free(lpwzSrc);
2073 heap_free(lpwzDest);
2074 return ret;
2077 typedef struct {
2078 task_header_t hdr;
2079 WCHAR *src_file;
2080 WCHAR *dst_file;
2081 } rename_file_task_t;
2083 static void AsyncFtpRenameFileProc(task_header_t *hdr)
2085 rename_file_task_t *task = (rename_file_task_t*)hdr;
2086 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
2088 TRACE("%p\n", session);
2090 FTP_FtpRenameFileW(session, task->src_file, task->dst_file);
2091 heap_free(task->src_file);
2092 heap_free(task->dst_file);
2095 /***********************************************************************
2096 * FtpRenameFileW (WININET.@)
2098 * Rename a file on the ftp server
2100 * RETURNS
2101 * TRUE on success
2102 * FALSE on failure
2105 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2107 ftp_session_t *lpwfs;
2108 appinfo_t *hIC = NULL;
2109 BOOL r = FALSE;
2111 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
2112 if (!lpwfs)
2114 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2115 return FALSE;
2118 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2120 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2121 goto lend;
2124 if (lpwfs->download_in_progress != NULL)
2126 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2127 goto lend;
2130 if (!lpszSrc || !lpszDest)
2132 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2133 goto lend;
2136 hIC = lpwfs->lpAppInfo;
2137 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2139 rename_file_task_t *task;
2141 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRenameFileProc, sizeof(*task));
2142 task->src_file = heap_strdupW(lpszSrc);
2143 task->dst_file = heap_strdupW(lpszDest);
2145 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
2147 else
2149 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2152 lend:
2153 WININET_Release( &lpwfs->hdr );
2155 return r;
2158 /***********************************************************************
2159 * FTP_FtpRenameFileW (Internal)
2161 * Rename a file on the ftp server
2163 * RETURNS
2164 * TRUE on success
2165 * FALSE on failure
2168 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2170 INT nResCode;
2171 BOOL bSuccess = FALSE;
2172 appinfo_t *hIC = NULL;
2174 TRACE("\n");
2176 /* Clear any error information */
2177 INTERNET_SetLastError(0);
2179 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2180 goto lend;
2182 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2183 if (nResCode == 350)
2185 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2186 goto lend;
2188 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2191 if (nResCode == 250)
2192 bSuccess = TRUE;
2193 else
2194 FTP_SetResponseError(nResCode);
2196 lend:
2197 hIC = lpwfs->lpAppInfo;
2198 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2200 INTERNET_ASYNC_RESULT iar;
2202 iar.dwResult = (DWORD)bSuccess;
2203 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2204 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2205 &iar, sizeof(INTERNET_ASYNC_RESULT));
2208 return bSuccess;
2211 /***********************************************************************
2212 * FtpCommandA (WININET.@)
2214 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2215 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2217 BOOL r;
2218 WCHAR *cmdW;
2220 TRACE("%p %d 0x%08lx %s 0x%08Ix %p\n", hConnect, fExpectResponse, dwFlags,
2221 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2223 if (fExpectResponse)
2225 FIXME("data connection not supported\n");
2226 return FALSE;
2229 if (!lpszCommand || !lpszCommand[0])
2231 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2232 return FALSE;
2235 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2237 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2238 return FALSE;
2241 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2243 heap_free(cmdW);
2244 return r;
2247 /***********************************************************************
2248 * FtpCommandW (WININET.@)
2250 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2251 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2253 BOOL r = FALSE;
2254 ftp_session_t *lpwfs;
2255 LPSTR cmd = NULL;
2256 DWORD len, nBytesSent= 0;
2257 INT nResCode, nRC = 0;
2259 TRACE("%p %d 0x%08lx %s 0x%08Ix %p\n", hConnect, fExpectResponse, dwFlags,
2260 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2262 if (!lpszCommand || !lpszCommand[0])
2264 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2265 return FALSE;
2268 if (fExpectResponse)
2270 FIXME("data connection not supported\n");
2271 return FALSE;
2274 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
2275 if (!lpwfs)
2277 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2278 return FALSE;
2281 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2283 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2284 goto lend;
2287 if (lpwfs->download_in_progress != NULL)
2289 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2290 goto lend;
2293 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2294 if ((cmd = heap_alloc(len)))
2295 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2296 else
2298 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2299 goto lend;
2302 strcat(cmd, szCRLF);
2303 len--;
2305 TRACE("Sending (%s) len(%ld)\n", debugstr_a(cmd), len);
2306 while ((nBytesSent < len) && (nRC != -1))
2308 nRC = sock_send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2309 if (nRC != -1)
2311 nBytesSent += nRC;
2312 TRACE("Sent %d bytes\n", nRC);
2316 if (nBytesSent)
2318 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2319 if (nResCode > 0 && nResCode < 400)
2320 r = TRUE;
2321 else
2322 FTP_SetResponseError(nResCode);
2325 lend:
2326 WININET_Release( &lpwfs->hdr );
2327 heap_free( cmd );
2328 return r;
2332 /***********************************************************************
2333 * FTPSESSION_Destroy (internal)
2335 * Deallocate session handle
2337 static void FTPSESSION_Destroy(object_header_t *hdr)
2339 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2341 TRACE("\n");
2343 WININET_Release(&lpwfs->lpAppInfo->hdr);
2345 heap_free(lpwfs->lpszPassword);
2346 heap_free(lpwfs->lpszUserName);
2347 heap_free(lpwfs->servername);
2350 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2352 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2354 TRACE("\n");
2356 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2357 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2359 if (lpwfs->download_in_progress != NULL)
2360 lpwfs->download_in_progress->session_deleted = TRUE;
2362 if (lpwfs->sndSocket != -1)
2363 closesocket(lpwfs->sndSocket);
2365 if (lpwfs->lstnSocket != -1)
2366 closesocket(lpwfs->lstnSocket);
2368 if (lpwfs->pasvSocket != -1)
2369 closesocket(lpwfs->pasvSocket);
2371 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2372 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2375 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2377 switch(option) {
2378 case INTERNET_OPTION_HANDLE_TYPE:
2379 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2381 if (*size < sizeof(ULONG))
2382 return ERROR_INSUFFICIENT_BUFFER;
2384 *size = sizeof(DWORD);
2385 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2386 return ERROR_SUCCESS;
2389 return INET_QueryOption(hdr, option, buffer, size, unicode);
2392 static const object_vtbl_t FTPSESSIONVtbl = {
2393 FTPSESSION_Destroy,
2394 FTPSESSION_CloseConnection,
2395 FTPSESSION_QueryOption,
2396 INET_SetOption,
2397 NULL,
2398 NULL,
2399 NULL,
2400 NULL
2404 /***********************************************************************
2405 * FTP_Connect (internal)
2407 * Connect to a ftp server
2409 * RETURNS
2410 * HINTERNET a session handle on success
2411 * NULL on failure
2413 * NOTES:
2415 * Windows uses 'anonymous' as the username, when given a NULL username
2416 * and a NULL password. The password is first looked up in:
2418 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2420 * If this entry is not present it uses the current username as the password.
2424 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2425 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2426 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2427 DWORD dwInternalFlags)
2429 struct sockaddr_in socketAddr;
2430 INT nsocket = -1;
2431 socklen_t sock_namelen;
2432 BOOL bSuccess = FALSE;
2433 ftp_session_t *lpwfs = NULL;
2434 char szaddr[INET6_ADDRSTRLEN];
2436 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2437 hIC, debugstr_w(lpszServerName),
2438 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2440 assert( hIC->hdr.htype == WH_HINIT );
2442 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2444 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2445 return NULL;
2448 lpwfs = alloc_object(&hIC->hdr, &FTPSESSIONVtbl, sizeof(ftp_session_t));
2449 if (NULL == lpwfs)
2451 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2452 return NULL;
2455 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2456 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2457 else
2458 lpwfs->serverport = nServerPort;
2460 lpwfs->hdr.htype = WH_HFTPSESSION;
2461 lpwfs->hdr.dwFlags = dwFlags;
2462 lpwfs->hdr.dwContext = dwContext;
2463 lpwfs->hdr.dwInternalFlags |= dwInternalFlags;
2464 lpwfs->download_in_progress = NULL;
2465 lpwfs->sndSocket = -1;
2466 lpwfs->lstnSocket = -1;
2467 lpwfs->pasvSocket = -1;
2469 WININET_AddRef( &hIC->hdr );
2470 lpwfs->lpAppInfo = hIC;
2471 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2473 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
2474 if(wcschr(hIC->proxy, ' '))
2475 FIXME("Several proxies not implemented.\n");
2476 if(hIC->proxyBypass)
2477 FIXME("Proxy bypass is ignored.\n");
2479 if (!lpszUserName || !lpszUserName[0]) {
2480 HKEY key;
2481 WCHAR szPassword[MAX_PATH];
2482 DWORD len = sizeof(szPassword);
2484 lpwfs->lpszUserName = heap_strdupW(L"anonymous");
2486 RegOpenKeyW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", &key);
2487 if (RegQueryValueExW(key, L"EmailName", NULL, NULL, (LPBYTE)szPassword, &len)) {
2488 /* Nothing in the registry, get the username and use that as the password */
2489 if (!GetUserNameW(szPassword, &len)) {
2490 /* Should never get here, but use an empty password as failsafe */
2491 lstrcpyW(szPassword, L"");
2494 RegCloseKey(key);
2496 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2497 lpwfs->lpszPassword = heap_strdupW(szPassword);
2499 else {
2500 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2501 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : L"");
2503 lpwfs->servername = heap_strdupW(lpszServerName);
2505 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2506 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2508 INTERNET_ASYNC_RESULT iar;
2510 iar.dwResult = (DWORD_PTR)lpwfs->hdr.hInternet;
2511 iar.dwError = ERROR_SUCCESS;
2513 INTERNET_SendCallback(&hIC->hdr, dwContext,
2514 INTERNET_STATUS_HANDLE_CREATED, &iar,
2515 sizeof(INTERNET_ASYNC_RESULT));
2518 INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2519 (LPWSTR) lpszServerName, (lstrlenW(lpszServerName)+1) * sizeof(WCHAR));
2521 sock_namelen = sizeof(socketAddr);
2522 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen, szaddr))
2524 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2525 goto lerror;
2528 if (socketAddr.sin_family != AF_INET)
2530 WARN("unsupported address family %d\n", socketAddr.sin_family);
2531 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2532 goto lerror;
2535 INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2536 szaddr, strlen(szaddr)+1);
2538 init_winsock();
2539 nsocket = socket(AF_INET,SOCK_STREAM,0);
2540 if (nsocket == -1)
2542 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2543 goto lerror;
2546 INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2547 szaddr, strlen(szaddr)+1);
2549 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2551 ERR("Unable to connect (%d)\n", WSAGetLastError());
2552 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2553 closesocket(nsocket);
2555 else
2557 TRACE("Connected to server\n");
2558 lpwfs->sndSocket = nsocket;
2559 INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2560 szaddr, strlen(szaddr)+1);
2562 sock_namelen = sizeof(lpwfs->socketAddress);
2563 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2565 if (FTP_ConnectToHost(lpwfs))
2567 TRACE("Successfully logged into server\n");
2568 bSuccess = TRUE;
2572 lerror:
2573 if (!bSuccess)
2575 WININET_Release(&lpwfs->hdr);
2576 return NULL;
2579 return lpwfs->hdr.hInternet;
2583 /***********************************************************************
2584 * FTP_ConnectToHost (internal)
2586 * Connect to a ftp server
2588 * RETURNS
2589 * TRUE on success
2590 * NULL on failure
2593 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2595 INT nResCode;
2596 BOOL bSuccess = FALSE;
2598 TRACE("\n");
2599 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2601 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2602 goto lend;
2604 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2605 if (nResCode)
2607 /* Login successful... */
2608 if (nResCode == 230)
2609 bSuccess = TRUE;
2610 /* User name okay, need password... */
2611 else if (nResCode == 331)
2612 bSuccess = FTP_SendPassword(lpwfs);
2613 /* Need account for login... */
2614 else if (nResCode == 332)
2615 bSuccess = FTP_SendAccount(lpwfs);
2616 else
2617 FTP_SetResponseError(nResCode);
2620 TRACE("Returning %d\n", bSuccess);
2621 lend:
2622 return bSuccess;
2625 /***********************************************************************
2626 * FTP_GetNextLine (internal)
2628 * Parse next line in directory string listing
2630 * RETURNS
2631 * Pointer to beginning of next line
2632 * NULL on failure
2636 static LPSTR FTP_GetNextLine(INT nSocket, LPDWORD dwLen)
2638 struct timeval tv = {RESPONSE_TIMEOUT,0};
2639 FD_SET set;
2640 INT nRecv = 0;
2641 LPSTR lpszBuffer = INTERNET_GetResponseBuffer();
2643 TRACE("\n");
2645 FD_ZERO(&set);
2646 FD_SET(nSocket, &set);
2648 while (nRecv < MAX_REPLY_LEN)
2650 if (select(nSocket+1, &set, NULL, NULL, &tv) > 0)
2652 if (sock_recv(nSocket, &lpszBuffer[nRecv], 1, 0) <= 0)
2654 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2655 return NULL;
2658 if (lpszBuffer[nRecv] == '\n')
2660 lpszBuffer[nRecv] = '\0';
2661 *dwLen = nRecv - 1;
2662 TRACE(":%d %s\n", nRecv, lpszBuffer);
2663 return lpszBuffer;
2665 if (lpszBuffer[nRecv] != '\r')
2666 nRecv++;
2668 else
2670 INTERNET_SetLastError(ERROR_INTERNET_TIMEOUT);
2671 return NULL;
2675 return NULL;
2678 /***********************************************************************
2679 * FTP_SendCommandA (internal)
2681 * Send command to server
2683 * RETURNS
2684 * TRUE on success
2685 * NULL on failure
2688 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2689 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2691 DWORD len;
2692 CHAR *buf;
2693 DWORD nBytesSent = 0;
2694 int nRC = 0;
2695 DWORD dwParamLen;
2697 TRACE("%d: (%s) %d\n", ftpCmd, debugstr_a(lpszParam), nSocket);
2699 if (lpfnStatusCB)
2701 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2704 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2705 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2706 if (NULL == (buf = heap_alloc(len+1)))
2708 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2709 return FALSE;
2711 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2712 dwParamLen ? lpszParam : "", szCRLF);
2714 TRACE("Sending (%s) len(%ld)\n", debugstr_a(buf), len);
2715 while((nBytesSent < len) && (nRC != -1))
2717 nRC = sock_send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2718 nBytesSent += nRC;
2720 heap_free(buf);
2722 if (lpfnStatusCB)
2724 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2725 &nBytesSent, sizeof(DWORD));
2728 TRACE("Sent %ld bytes\n", nBytesSent);
2729 return (nRC != -1);
2732 /***********************************************************************
2733 * FTP_SendCommand (internal)
2735 * Send command to server
2737 * RETURNS
2738 * TRUE on success
2739 * NULL on failure
2742 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2743 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2745 BOOL ret;
2746 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2747 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2748 heap_free(lpszParamA);
2749 return ret;
2752 /***********************************************************************
2753 * FTP_ReceiveResponse (internal)
2755 * Receive response from server
2757 * RETURNS
2758 * Reply code on success
2759 * 0 on failure
2762 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2764 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2765 DWORD nRecv;
2766 INT rc = 0;
2767 char firstprefix[5];
2768 BOOL multiline = FALSE;
2770 TRACE("socket(%d)\n", lpwfs->sndSocket);
2772 INTERNET_SendCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2774 while(1)
2776 if (!FTP_GetNextLine(lpwfs->sndSocket, &nRecv))
2777 goto lerror;
2779 if (nRecv >= 3)
2781 if(!multiline)
2783 if(lpszResponse[3] != '-')
2784 break;
2785 else
2786 { /* Start of multiline response. Loop until we get "nnn " */
2787 multiline = TRUE;
2788 memcpy(firstprefix, lpszResponse, 3);
2789 firstprefix[3] = ' ';
2790 firstprefix[4] = '\0';
2793 else
2795 if(!memcmp(firstprefix, lpszResponse, 4))
2796 break;
2801 if (nRecv >= 3)
2803 rc = atoi(lpszResponse);
2805 INTERNET_SendCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2806 &nRecv, sizeof(DWORD));
2809 lerror:
2810 TRACE("return %d\n", rc);
2811 return rc;
2815 /***********************************************************************
2816 * FTP_SendPassword (internal)
2818 * Send password to ftp server
2820 * RETURNS
2821 * TRUE on success
2822 * NULL on failure
2825 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2827 INT nResCode;
2828 BOOL bSuccess = FALSE;
2830 TRACE("\n");
2831 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2832 goto lend;
2834 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2835 if (nResCode)
2837 TRACE("Received reply code %d\n", nResCode);
2838 /* Login successful... */
2839 if (nResCode == 230)
2840 bSuccess = TRUE;
2841 /* Command not implemented, superfluous at the server site... */
2842 /* Need account for login... */
2843 else if (nResCode == 332)
2844 bSuccess = FTP_SendAccount(lpwfs);
2845 else
2846 FTP_SetResponseError(nResCode);
2849 lend:
2850 TRACE("Returning %d\n", bSuccess);
2851 return bSuccess;
2855 /***********************************************************************
2856 * FTP_SendAccount (internal)
2860 * RETURNS
2861 * TRUE on success
2862 * FALSE on failure
2865 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2867 INT nResCode;
2868 BOOL bSuccess = FALSE;
2870 TRACE("\n");
2871 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, L"noaccount", 0, 0, 0))
2872 goto lend;
2874 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2875 if (nResCode)
2876 bSuccess = TRUE;
2877 else
2878 FTP_SetResponseError(nResCode);
2880 lend:
2881 return bSuccess;
2885 /***********************************************************************
2886 * FTP_SendStore (internal)
2888 * Send request to upload file to ftp server
2890 * RETURNS
2891 * TRUE on success
2892 * FALSE on failure
2895 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2897 INT nResCode;
2898 BOOL bSuccess = FALSE;
2900 TRACE("\n");
2901 if (!FTP_InitListenSocket(lpwfs))
2902 goto lend;
2904 if (!FTP_SendType(lpwfs, dwType))
2905 goto lend;
2907 if (!FTP_SendPortOrPasv(lpwfs))
2908 goto lend;
2910 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2911 goto lend;
2912 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2913 if (nResCode)
2915 if (nResCode == 150 || nResCode == 125)
2916 bSuccess = TRUE;
2917 else
2918 FTP_SetResponseError(nResCode);
2921 lend:
2922 if (!bSuccess && lpwfs->lstnSocket != -1)
2924 closesocket(lpwfs->lstnSocket);
2925 lpwfs->lstnSocket = -1;
2928 return bSuccess;
2932 /***********************************************************************
2933 * FTP_InitListenSocket (internal)
2935 * Create a socket to listen for server response
2937 * RETURNS
2938 * TRUE on success
2939 * FALSE on failure
2942 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2944 BOOL bSuccess = FALSE;
2945 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2947 TRACE("\n");
2949 init_winsock();
2950 lpwfs->lstnSocket = socket(AF_INET, SOCK_STREAM, 0);
2951 if (lpwfs->lstnSocket == -1)
2953 TRACE("Unable to create listening socket\n");
2954 goto lend;
2957 /* We obtain our ip addr from the name of the command channel socket */
2958 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2960 /* and get the system to assign us a port */
2961 lpwfs->lstnSocketAddress.sin_port = htons(0);
2963 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2965 TRACE("Unable to bind socket\n");
2966 goto lend;
2969 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2971 TRACE("listen failed\n");
2972 goto lend;
2975 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2976 bSuccess = TRUE;
2978 lend:
2979 if (!bSuccess && lpwfs->lstnSocket != -1)
2981 closesocket(lpwfs->lstnSocket);
2982 lpwfs->lstnSocket = -1;
2985 return bSuccess;
2989 /***********************************************************************
2990 * FTP_SendType (internal)
2992 * Tell server type of data being transferred
2994 * RETURNS
2995 * TRUE on success
2996 * FALSE on failure
2998 * W98SE doesn't cache the type that's currently set
2999 * (i.e. it sends it always),
3000 * so we probably don't want to do that either.
3002 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
3004 INT nResCode;
3005 WCHAR type[] = L"I";
3006 BOOL bSuccess = FALSE;
3008 TRACE("\n");
3009 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
3010 type[0] = 'A';
3012 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
3013 goto lend;
3015 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
3016 if (nResCode)
3018 if (nResCode == 2)
3019 bSuccess = TRUE;
3020 else
3021 FTP_SetResponseError(nResCode);
3024 lend:
3025 return bSuccess;
3029 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
3030 /***********************************************************************
3031 * FTP_GetFileSize (internal)
3033 * Retrieves from the server the size of the given file
3035 * RETURNS
3036 * TRUE on success
3037 * FALSE on failure
3040 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
3042 INT nResCode;
3043 BOOL bSuccess = FALSE;
3045 TRACE("\n");
3047 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
3048 goto lend;
3050 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3051 if (nResCode)
3053 if (nResCode == 213) {
3054 /* Now parses the output to get the actual file size */
3055 int i;
3056 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3058 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3059 if (lpszResponseBuffer[i] == '\0') return FALSE;
3060 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3062 bSuccess = TRUE;
3063 } else {
3064 FTP_SetResponseError(nResCode);
3068 lend:
3069 return bSuccess;
3071 #endif
3074 /***********************************************************************
3075 * FTP_SendPort (internal)
3077 * Tell server which port to use
3079 * RETURNS
3080 * TRUE on success
3081 * FALSE on failure
3084 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3086 INT nResCode;
3087 WCHAR szIPAddress[64];
3088 BOOL bSuccess = FALSE;
3089 TRACE("\n");
3091 swprintf(szIPAddress, ARRAY_SIZE(szIPAddress), L"%d,%d,%d,%d,%d,%d",
3092 lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x000000FF,
3093 (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x0000FF00)>>8,
3094 (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x00FF0000)>>16,
3095 (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0xFF000000)>>24,
3096 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3097 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3099 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3100 goto lend;
3102 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3103 if (nResCode)
3105 if (nResCode == 200)
3106 bSuccess = TRUE;
3107 else
3108 FTP_SetResponseError(nResCode);
3111 lend:
3112 return bSuccess;
3116 /***********************************************************************
3117 * FTP_DoPassive (internal)
3119 * Tell server that we want to do passive transfers
3120 * and connect data socket
3122 * RETURNS
3123 * TRUE on success
3124 * FALSE on failure
3127 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3129 INT nResCode;
3130 BOOL bSuccess = FALSE;
3132 TRACE("\n");
3133 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3134 goto lend;
3136 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3137 if (nResCode)
3139 if (nResCode == 227)
3141 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3142 LPSTR p;
3143 int f[6];
3144 int i;
3145 char *pAddr, *pPort;
3146 INT nsocket = -1;
3147 struct sockaddr_in dataSocketAddress;
3149 p = lpszResponseBuffer+4; /* skip status code */
3150 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3152 if (*p == '\0')
3154 ERR("no address found in response, aborting\n");
3155 goto lend;
3158 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3159 &f[4], &f[5]) != 6)
3161 ERR("unknown response address format '%s', aborting\n", p);
3162 goto lend;
3164 for (i=0; i < 6; i++)
3165 f[i] = f[i] & 0xff;
3167 dataSocketAddress = lpwfs->socketAddress;
3168 pAddr = (char *)&(dataSocketAddress.sin_addr.S_un.S_addr);
3169 pPort = (char *)&(dataSocketAddress.sin_port);
3170 pAddr[0] = f[0];
3171 pAddr[1] = f[1];
3172 pAddr[2] = f[2];
3173 pAddr[3] = f[3];
3174 pPort[0] = f[4];
3175 pPort[1] = f[5];
3177 nsocket = socket(AF_INET,SOCK_STREAM,0);
3178 if (nsocket == -1)
3179 goto lend;
3181 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3183 ERR("can't connect passive FTP data port.\n");
3184 closesocket(nsocket);
3185 goto lend;
3187 lpwfs->pasvSocket = nsocket;
3188 bSuccess = TRUE;
3190 else
3191 FTP_SetResponseError(nResCode);
3194 lend:
3195 return bSuccess;
3199 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3201 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3203 if (!FTP_DoPassive(lpwfs))
3204 return FALSE;
3206 else
3208 if (!FTP_SendPort(lpwfs))
3209 return FALSE;
3211 return TRUE;
3215 /***********************************************************************
3216 * FTP_GetDataSocket (internal)
3218 * Either accepts an incoming data socket connection from the server
3219 * or just returns the already opened socket after a PASV command
3220 * in case of passive FTP.
3223 * RETURNS
3224 * TRUE on success
3225 * FALSE on failure
3228 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3230 struct sockaddr_in saddr;
3231 socklen_t addrlen = sizeof(saddr);
3233 TRACE("\n");
3234 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3236 *nDataSocket = lpwfs->pasvSocket;
3237 lpwfs->pasvSocket = -1;
3239 else
3241 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3242 closesocket(lpwfs->lstnSocket);
3243 lpwfs->lstnSocket = -1;
3245 return *nDataSocket != -1;
3249 /***********************************************************************
3250 * FTP_SendData (internal)
3252 * Send data to the server
3254 * RETURNS
3255 * TRUE on success
3256 * FALSE on failure
3259 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3261 BY_HANDLE_FILE_INFORMATION fi;
3262 DWORD nBytesRead = 0;
3263 DWORD nBytesSent = 0;
3264 DWORD nTotalSent = 0;
3265 DWORD nBytesToSend, nLen;
3266 int nRC = 1;
3267 time_t s_long_time, e_long_time;
3268 LONG nSeconds;
3269 CHAR *lpszBuffer;
3271 TRACE("\n");
3272 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3274 /* Get the size of the file. */
3275 GetFileInformationByHandle(hFile, &fi);
3276 time(&s_long_time);
3280 nBytesToSend = nBytesRead - nBytesSent;
3282 if (nBytesToSend <= 0)
3284 /* Read data from file. */
3285 nBytesSent = 0;
3286 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3287 ERR("Failed reading from file\n");
3289 if (nBytesRead > 0)
3290 nBytesToSend = nBytesRead;
3291 else
3292 break;
3295 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3296 DATA_PACKET_SIZE : nBytesToSend;
3297 nRC = sock_send(nDataSocket, lpszBuffer, nLen, 0);
3299 if (nRC != -1)
3301 nBytesSent += nRC;
3302 nTotalSent += nRC;
3305 /* Do some computation to display the status. */
3306 time(&e_long_time);
3307 nSeconds = e_long_time - s_long_time;
3308 if( nSeconds / 60 > 0 )
3310 TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld min %ld sec estimated remaining time %ld sec\n",
3311 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3312 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3314 else
3316 TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld sec estimated remaining time %ld sec\n",
3317 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3318 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3320 } while (nRC != -1);
3322 TRACE("file transfer complete!\n");
3324 heap_free(lpszBuffer);
3325 return nTotalSent;
3329 /***********************************************************************
3330 * FTP_SendRetrieve (internal)
3332 * Send request to retrieve a file
3334 * RETURNS
3335 * Number of bytes to be received on success
3336 * 0 on failure
3339 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3341 INT nResCode;
3342 BOOL ret;
3344 TRACE("\n");
3345 if (!(ret = FTP_InitListenSocket(lpwfs)))
3346 goto lend;
3348 if (!(ret = FTP_SendType(lpwfs, dwType)))
3349 goto lend;
3351 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3352 goto lend;
3354 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3355 goto lend;
3357 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3358 if ((nResCode != 125) && (nResCode != 150)) {
3359 /* That means that we got an error getting the file. */
3360 FTP_SetResponseError(nResCode);
3361 ret = FALSE;
3364 lend:
3365 if (!ret && lpwfs->lstnSocket != -1)
3367 closesocket(lpwfs->lstnSocket);
3368 lpwfs->lstnSocket = -1;
3371 return ret;
3375 /***********************************************************************
3376 * FTP_RetrieveData (internal)
3378 * Retrieve data from server
3380 * RETURNS
3381 * TRUE on success
3382 * FALSE on failure
3385 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3387 DWORD nBytesWritten;
3388 INT nRC = 0;
3389 CHAR *lpszBuffer;
3391 TRACE("\n");
3393 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3394 if (NULL == lpszBuffer)
3396 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3397 return FALSE;
3400 while (nRC != -1)
3402 nRC = sock_recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3403 if (nRC != -1)
3405 /* other side closed socket. */
3406 if (nRC == 0)
3407 goto recv_end;
3408 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3412 TRACE("Data transfer complete\n");
3414 recv_end:
3415 heap_free(lpszBuffer);
3416 return (nRC != -1);
3419 /***********************************************************************
3420 * FTPFINDNEXT_Destroy (internal)
3422 * Deallocate session handle
3424 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3426 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3427 DWORD i;
3429 TRACE("\n");
3431 WININET_Release(&lpwfn->lpFtpSession->hdr);
3433 for (i = 0; i < lpwfn->size; i++)
3435 heap_free(lpwfn->lpafp[i].lpszName);
3437 heap_free(lpwfn->lpafp);
3440 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3442 WIN32_FIND_DATAW *find_data = data;
3443 DWORD res = ERROR_SUCCESS;
3445 TRACE("index(%ld) size(%ld)\n", find->index, find->size);
3447 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3449 if (find->index < find->size) {
3450 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3451 find->index++;
3453 TRACE("Name: %s\nSize: %ld\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3454 }else {
3455 res = ERROR_NO_MORE_FILES;
3458 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3460 INTERNET_ASYNC_RESULT iar;
3462 iar.dwResult = (res == ERROR_SUCCESS);
3463 iar.dwError = res;
3465 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3466 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3467 sizeof(INTERNET_ASYNC_RESULT));
3470 return res;
3473 typedef struct {
3474 task_header_t hdr;
3475 WIN32_FIND_DATAW *find_data;
3476 } find_next_task_t;
3478 static void FTPFINDNEXT_AsyncFindNextFileProc(task_header_t *hdr)
3480 find_next_task_t *task = (find_next_task_t*)hdr;
3482 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)task->hdr.hdr, task->find_data);
3485 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3487 switch(option) {
3488 case INTERNET_OPTION_HANDLE_TYPE:
3489 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3491 if (*size < sizeof(ULONG))
3492 return ERROR_INSUFFICIENT_BUFFER;
3494 *size = sizeof(DWORD);
3495 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3496 return ERROR_SUCCESS;
3499 return INET_QueryOption(hdr, option, buffer, size, unicode);
3502 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3504 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3506 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3508 find_next_task_t *task;
3510 task = alloc_async_task(&find->hdr, FTPFINDNEXT_AsyncFindNextFileProc, sizeof(*task));
3511 task->find_data = data;
3513 INTERNET_AsyncCall(&task->hdr);
3514 return ERROR_SUCCESS;
3517 return FTPFINDNEXT_FindNextFileProc(find, data);
3520 static const object_vtbl_t FTPFINDNEXTVtbl = {
3521 FTPFINDNEXT_Destroy,
3522 NULL,
3523 FTPFINDNEXT_QueryOption,
3524 INET_SetOption,
3525 NULL,
3526 NULL,
3527 NULL,
3528 FTPFINDNEXT_FindNextFileW
3531 /***********************************************************************
3532 * FTP_ReceiveFileList (internal)
3534 * Read file list from server
3536 * RETURNS
3537 * Handle to file list on success
3538 * NULL on failure
3541 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3542 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3544 DWORD dwSize = 0;
3545 LPFILEPROPERTIESW lpafp = NULL;
3546 LPWININETFTPFINDNEXTW lpwfn = NULL;
3548 TRACE("(%p,%d,%s,%p,%08Ix)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3550 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3552 if(lpFindFileData)
3553 FTP_ConvertFileProp(lpafp, lpFindFileData);
3555 lpwfn = alloc_object(&lpwfs->hdr, &FTPFINDNEXTVtbl, sizeof(WININETFTPFINDNEXTW));
3556 if (lpwfn)
3558 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3559 lpwfn->hdr.dwContext = dwContext;
3560 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3561 lpwfn->size = dwSize;
3562 lpwfn->lpafp = lpafp;
3564 WININET_AddRef( &lpwfs->hdr );
3565 lpwfn->lpFtpSession = lpwfs;
3566 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3570 TRACE("Matched %ld files\n", dwSize);
3571 return lpwfn ? lpwfn->hdr.hInternet : NULL;
3575 /***********************************************************************
3576 * FTP_ConvertFileProp (internal)
3578 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3580 * RETURNS
3581 * TRUE on success
3582 * FALSE on failure
3585 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3587 BOOL bSuccess = FALSE;
3589 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3591 if (lpafp)
3593 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3594 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3595 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3597 /* Not all fields are filled in */
3598 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3599 lpFindFileData->nFileSizeLow = lpafp->nSize;
3601 if (lpafp->bIsDirectory)
3602 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3604 if (lpafp->lpszName)
3605 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3607 bSuccess = TRUE;
3610 return bSuccess;
3613 /***********************************************************************
3614 * FTP_ParseNextFile (internal)
3616 * Parse the next line in file listing
3618 * RETURNS
3619 * TRUE on success
3620 * FALSE on failure
3622 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3624 static const char szSpace[] = " \t";
3625 DWORD nBufLen;
3626 char *pszLine;
3627 char *pszToken;
3628 char *pszTmp;
3629 BOOL found = FALSE;
3630 int i;
3632 lpfp->lpszName = NULL;
3633 do {
3634 if(!(pszLine = FTP_GetNextLine(nSocket, &nBufLen)))
3635 return FALSE;
3637 pszToken = strtok(pszLine, szSpace);
3638 /* ls format
3639 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3641 * For instance:
3642 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3644 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3645 if(!FTP_ParsePermission(pszToken, lpfp))
3646 lpfp->bIsDirectory = FALSE;
3647 for(i=0; i<=3; i++) {
3648 if(!(pszToken = strtok(NULL, szSpace)))
3649 break;
3651 if(!pszToken) continue;
3652 if(lpfp->bIsDirectory) {
3653 TRACE("Is directory\n");
3654 lpfp->nSize = 0;
3656 else {
3657 TRACE("Size: %s\n", pszToken);
3658 lpfp->nSize = atol(pszToken);
3661 lpfp->tmLastModified.wSecond = 0;
3662 lpfp->tmLastModified.wMinute = 0;
3663 lpfp->tmLastModified.wHour = 0;
3664 lpfp->tmLastModified.wDay = 0;
3665 lpfp->tmLastModified.wMonth = 0;
3666 lpfp->tmLastModified.wYear = 0;
3668 /* Determine month */
3669 pszToken = strtok(NULL, szSpace);
3670 if(!pszToken) continue;
3671 if(strlen(pszToken) >= 3) {
3672 pszToken[3] = 0;
3673 if((pszTmp = StrStrIA(szMonths, pszToken)))
3674 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3676 /* Determine day */
3677 pszToken = strtok(NULL, szSpace);
3678 if(!pszToken) continue;
3679 lpfp->tmLastModified.wDay = atoi(pszToken);
3680 /* Determine time or year */
3681 pszToken = strtok(NULL, szSpace);
3682 if(!pszToken) continue;
3683 if((pszTmp = strchr(pszToken, ':'))) {
3684 SYSTEMTIME curr_time;
3685 *pszTmp = 0;
3686 pszTmp++;
3687 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3688 lpfp->tmLastModified.wHour = atoi(pszToken);
3689 GetLocalTime( &curr_time );
3690 lpfp->tmLastModified.wYear = curr_time.wYear;
3692 else {
3693 lpfp->tmLastModified.wYear = atoi(pszToken);
3694 lpfp->tmLastModified.wHour = 12;
3696 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3697 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3698 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3700 pszToken = strtok(NULL, szSpace);
3701 if(!pszToken) continue;
3702 lpfp->lpszName = heap_strdupAtoW(pszToken);
3703 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3705 /* NT way of parsing ... :
3707 07-13-03 08:55PM <DIR> sakpatch
3708 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3710 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3711 int mon, mday, year, hour, min;
3712 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3714 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3715 lpfp->tmLastModified.wDay = mday;
3716 lpfp->tmLastModified.wMonth = mon;
3717 lpfp->tmLastModified.wYear = year;
3719 /* Hacky and bad Y2K protection :-) */
3720 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3722 pszToken = strtok(NULL, szSpace);
3723 if(!pszToken) continue;
3724 sscanf(pszToken, "%d:%d", &hour, &min);
3725 lpfp->tmLastModified.wHour = hour;
3726 lpfp->tmLastModified.wMinute = min;
3727 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3728 lpfp->tmLastModified.wHour += 12;
3730 lpfp->tmLastModified.wSecond = 0;
3732 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3733 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3734 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3736 pszToken = strtok(NULL, szSpace);
3737 if(!pszToken) continue;
3738 if(!stricmp(pszToken, "<DIR>")) {
3739 lpfp->bIsDirectory = TRUE;
3740 lpfp->nSize = 0;
3741 TRACE("Is directory\n");
3743 else {
3744 lpfp->bIsDirectory = FALSE;
3745 lpfp->nSize = atol(pszToken);
3746 TRACE("Size: %ld\n", lpfp->nSize);
3749 pszToken = strtok(NULL, szSpace);
3750 if(!pszToken) continue;
3751 lpfp->lpszName = heap_strdupAtoW(pszToken);
3752 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3754 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3755 else if(pszToken[0] == '+') {
3756 FIXME("EPLF Format not implemented\n");
3759 if(lpfp->lpszName) {
3760 if((lpszSearchFile == NULL) ||
3761 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3762 found = TRUE;
3763 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3765 else {
3766 heap_free(lpfp->lpszName);
3767 lpfp->lpszName = NULL;
3770 } while(!found);
3771 return TRUE;
3774 /***********************************************************************
3775 * FTP_ParseDirectory (internal)
3777 * Parse string of directory information
3779 * RETURNS
3780 * TRUE on success
3781 * FALSE on failure
3783 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3784 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3786 BOOL bSuccess = TRUE;
3787 INT sizeFilePropArray = 500;/*20; */
3788 INT indexFilePropArray = -1;
3790 TRACE("\n");
3792 /* Allocate initial file properties array */
3793 *lpafp = heap_alloc_zero(sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3794 if (!*lpafp)
3795 return FALSE;
3797 do {
3798 if (indexFilePropArray+1 >= sizeFilePropArray)
3800 LPFILEPROPERTIESW tmpafp;
3802 sizeFilePropArray *= 2;
3803 tmpafp = heap_realloc_zero(*lpafp, sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3804 if (NULL == tmpafp)
3806 bSuccess = FALSE;
3807 break;
3810 *lpafp = tmpafp;
3812 indexFilePropArray++;
3813 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3815 if (bSuccess && indexFilePropArray)
3817 if (indexFilePropArray < sizeFilePropArray - 1)
3819 LPFILEPROPERTIESW tmpafp;
3821 tmpafp = heap_realloc(*lpafp, sizeof(FILEPROPERTIESW)*indexFilePropArray);
3822 if (NULL != tmpafp)
3823 *lpafp = tmpafp;
3825 *dwfp = indexFilePropArray;
3827 else
3829 heap_free(*lpafp);
3830 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3831 bSuccess = FALSE;
3834 return bSuccess;
3838 /***********************************************************************
3839 * FTP_ParsePermission (internal)
3841 * Parse permission string of directory information
3843 * RETURNS
3844 * TRUE on success
3845 * FALSE on failure
3848 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3850 BOOL bSuccess = TRUE;
3851 unsigned short nPermission = 0;
3852 INT nPos = 1;
3853 INT nLast = 9;
3855 TRACE("\n");
3856 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3858 bSuccess = FALSE;
3859 return bSuccess;
3862 lpfp->bIsDirectory = (*lpszPermission == 'd');
3865 switch (nPos)
3867 case 1:
3868 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3869 break;
3870 case 2:
3871 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3872 break;
3873 case 3:
3874 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3875 break;
3876 case 4:
3877 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3878 break;
3879 case 5:
3880 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3881 break;
3882 case 6:
3883 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3884 break;
3885 case 7:
3886 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3887 break;
3888 case 8:
3889 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3890 break;
3891 case 9:
3892 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3893 break;
3895 nPos++;
3896 }while (nPos <= nLast);
3898 lpfp->permissions = nPermission;
3899 return bSuccess;
3903 /***********************************************************************
3904 * FTP_SetResponseError (internal)
3906 * Set the appropriate error code for a given response from the server
3908 * RETURNS
3911 static DWORD FTP_SetResponseError(DWORD dwResponse)
3913 DWORD dwCode = 0;
3915 switch(dwResponse)
3917 case 425: /* Cannot open data connection. */
3918 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3919 break;
3921 case 426: /* Connection closed, transfer aborted. */
3922 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3923 break;
3925 case 530: /* Not logged in. Login incorrect. */
3926 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3927 break;
3929 case 421: /* Service not available - Server may be shutting down. */
3930 case 450: /* File action not taken. File may be busy. */
3931 case 451: /* Action aborted. Server error. */
3932 case 452: /* Action not taken. Insufficient storage space on server. */
3933 case 500: /* Syntax error. Command unrecognized. */
3934 case 501: /* Syntax error. Error in parameters or arguments. */
3935 case 502: /* Command not implemented. */
3936 case 503: /* Bad sequence of commands. */
3937 case 504: /* Command not implemented for that parameter. */
3938 case 532: /* Need account for storing files */
3939 case 550: /* File action not taken. File not found or no access. */
3940 case 551: /* Requested action aborted. Page type unknown */
3941 case 552: /* Action aborted. Exceeded storage allocation */
3942 case 553: /* Action not taken. File name not allowed. */
3944 default:
3945 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3946 break;
3949 INTERNET_SetLastError(dwCode);
3950 return dwCode;