imm32: Ignore some messages in ImmTranslateMessage.
[wine.git] / dlls / wininet / ftp.c
blobf3f586cd6e101cceb285435b480136ce1491f107
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 = strdupAtoW(lpszLocalFile);
228 lpwzNewRemoteFile = strdupAtoW(lpszNewRemoteFile);
229 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
230 dwFlags, dwContext);
231 free(lpwzLocalFile);
232 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 free(task->local_file);
255 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 = wcsdup(lpszLocalFile);
312 task->remote_file = wcsdup(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 = strdupAtoW(lpszDirectory);
421 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
422 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 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 = wcsdup(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 = strdupAtoW(lpszDirectory);
568 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
569 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 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 = wcsdup(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 = strdupAtoW(lpszSearchFile);
714 lpFindFileDataW = lpFindFileData?&wfd:NULL;
715 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
716 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 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 = wcsdup(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 = 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 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 = malloc(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 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 WCHAR *lpszResponseBuffer = 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 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 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 NULL,
1297 FTPFILE_ReadFile,
1298 FTPFILE_WriteFile,
1299 FTPFILE_QueryDataAvailable,
1300 NULL,
1301 FTPFILE_LockRequestFile
1304 /***********************************************************************
1305 * FTP_FtpOpenFileW (Internal)
1307 * Open a remote file for writing or reading
1309 * RETURNS
1310 * HINTERNET handle on success
1311 * NULL on failure
1314 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1315 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1316 DWORD_PTR dwContext)
1318 INT nDataSocket;
1319 BOOL bSuccess = FALSE;
1320 ftp_file_t *lpwh = NULL;
1321 appinfo_t *hIC = NULL;
1323 TRACE("\n");
1325 /* Clear any error information */
1326 INTERNET_SetLastError(0);
1328 if (GENERIC_READ == fdwAccess)
1330 /* Set up socket to retrieve data */
1331 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1333 else if (GENERIC_WRITE == fdwAccess)
1335 /* Set up socket to send data */
1336 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1339 /* Get data socket to server */
1340 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1342 lpwh = alloc_object(&lpwfs->hdr, &FTPFILEVtbl, sizeof(ftp_file_t));
1343 lpwh->hdr.htype = WH_HFILE;
1344 lpwh->hdr.dwFlags = dwFlags;
1345 lpwh->hdr.dwContext = dwContext;
1346 lpwh->nDataSocket = nDataSocket;
1347 lpwh->cache_file = NULL;
1348 lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1349 lpwh->session_deleted = FALSE;
1351 WININET_AddRef( &lpwfs->hdr );
1352 lpwh->lpFtpSession = lpwfs;
1353 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1355 /* Indicate that a download is currently in progress */
1356 lpwfs->download_in_progress = lpwh;
1359 if (lpwfs->lstnSocket != -1)
1361 closesocket(lpwfs->lstnSocket);
1362 lpwfs->lstnSocket = -1;
1365 if (bSuccess && fdwAccess == GENERIC_READ)
1367 WCHAR filename[MAX_PATH + 1];
1368 URL_COMPONENTSW uc;
1369 DWORD len;
1371 memset(&uc, 0, sizeof(uc));
1372 uc.dwStructSize = sizeof(uc);
1373 uc.nScheme = INTERNET_SCHEME_FTP;
1374 uc.lpszHostName = lpwfs->servername;
1375 uc.nPort = lpwfs->serverport;
1376 uc.lpszUserName = lpwfs->lpszUserName;
1377 uc.lpszUrlPath = wcsdup(lpszFileName);
1379 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1381 WCHAR *url = malloc(len * sizeof(WCHAR));
1383 if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1385 lpwh->cache_file = wcsdup(filename);
1386 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1387 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1388 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1390 WARN("Could not create cache file: %lu\n", GetLastError());
1391 free(lpwh->cache_file);
1392 lpwh->cache_file = NULL;
1395 free(url);
1397 free(uc.lpszUrlPath);
1400 hIC = lpwfs->lpAppInfo;
1401 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1403 INTERNET_ASYNC_RESULT iar;
1405 if (lpwh)
1407 iar.dwResult = (DWORD_PTR)lpwh->hdr.hInternet;
1408 iar.dwError = ERROR_SUCCESS;
1409 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1410 &iar, sizeof(INTERNET_ASYNC_RESULT));
1413 if(bSuccess) {
1414 FTP_ReceiveRequestData(lpwh, TRUE);
1415 }else {
1416 iar.dwResult = 0;
1417 iar.dwError = INTERNET_GetLastError();
1418 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1419 &iar, sizeof(INTERNET_ASYNC_RESULT));
1423 if(!bSuccess)
1424 return FALSE;
1426 return lpwh->hdr.hInternet;
1430 /***********************************************************************
1431 * FtpOpenFileA (WININET.@)
1433 * Open a remote file for writing or reading
1435 * RETURNS
1436 * HINTERNET handle on success
1437 * NULL on failure
1440 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1441 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1442 DWORD_PTR dwContext)
1444 LPWSTR lpwzFileName;
1445 HINTERNET ret;
1447 lpwzFileName = strdupAtoW(lpszFileName);
1448 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1449 free(lpwzFileName);
1450 return ret;
1453 typedef struct {
1454 task_header_t hdr;
1455 WCHAR *file_name;
1456 DWORD access;
1457 DWORD flags;
1458 DWORD_PTR context;
1459 } open_file_task_t;
1461 static void AsyncFtpOpenFileProc(task_header_t *hdr)
1463 open_file_task_t *task = (open_file_task_t*)hdr;
1464 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1466 TRACE("%p\n", session);
1468 FTP_FtpOpenFileW(session, task->file_name, task->access, task->flags, task->context);
1469 free(task->file_name);
1472 /***********************************************************************
1473 * FtpOpenFileW (WININET.@)
1475 * Open a remote file for writing or reading
1477 * RETURNS
1478 * HINTERNET handle on success
1479 * NULL on failure
1482 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1483 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1484 DWORD_PTR dwContext)
1486 ftp_session_t *lpwfs;
1487 appinfo_t *hIC = NULL;
1488 HINTERNET r = NULL;
1490 TRACE("(%p,%s,0x%08lx,0x%08lx,0x%08Ix)\n", hFtpSession,
1491 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1493 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1494 if (!lpwfs)
1496 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1497 return FALSE;
1500 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1502 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1503 goto lend;
1506 if ((!lpszFileName) ||
1507 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1508 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1510 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1511 goto lend;
1514 if (lpwfs->download_in_progress != NULL)
1516 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1517 goto lend;
1520 hIC = lpwfs->lpAppInfo;
1521 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1523 open_file_task_t *task;
1525 task = alloc_async_task(&lpwfs->hdr, AsyncFtpOpenFileProc, sizeof(*task));
1526 task->file_name = wcsdup(lpszFileName);
1527 task->access = fdwAccess;
1528 task->flags = dwFlags;
1529 task->context = dwContext;
1531 INTERNET_AsyncCall(&task->hdr);
1532 r = NULL;
1534 else
1536 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1539 lend:
1540 WININET_Release( &lpwfs->hdr );
1542 return r;
1546 /***********************************************************************
1547 * FtpGetFileA (WININET.@)
1549 * Retrieve file from the FTP server
1551 * RETURNS
1552 * TRUE on success
1553 * FALSE on failure
1556 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1557 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1558 DWORD_PTR dwContext)
1560 LPWSTR lpwzRemoteFile;
1561 LPWSTR lpwzNewFile;
1562 BOOL ret;
1564 lpwzRemoteFile = strdupAtoW(lpszRemoteFile);
1565 lpwzNewFile = strdupAtoW(lpszNewFile);
1566 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1567 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1568 free(lpwzRemoteFile);
1569 free(lpwzNewFile);
1570 return ret;
1573 typedef struct {
1574 task_header_t hdr;
1575 WCHAR *remote_file;
1576 WCHAR *new_file;
1577 BOOL fail_if_exists;
1578 DWORD local_attr;
1579 DWORD flags;
1580 DWORD_PTR context;
1581 } get_file_task_t;
1583 static void AsyncFtpGetFileProc(task_header_t *hdr)
1585 get_file_task_t *task = (get_file_task_t*)hdr;
1586 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1588 TRACE("%p\n", session);
1590 FTP_FtpGetFileW(session, task->remote_file, task->new_file, task->fail_if_exists,
1591 task->local_attr, task->flags, task->context);
1592 free(task->remote_file);
1593 free(task->new_file);
1597 /***********************************************************************
1598 * FtpGetFileW (WININET.@)
1600 * Retrieve file from the FTP server
1602 * RETURNS
1603 * TRUE on success
1604 * FALSE on failure
1607 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1608 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1609 DWORD_PTR dwContext)
1611 ftp_session_t *lpwfs;
1612 appinfo_t *hIC = NULL;
1613 BOOL r = FALSE;
1615 if (!lpszRemoteFile || !lpszNewFile)
1617 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1618 return FALSE;
1621 lpwfs = (ftp_session_t*) get_handle_object( hInternet );
1622 if (!lpwfs)
1624 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1625 return FALSE;
1628 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1630 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1631 goto lend;
1634 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1636 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1637 goto lend;
1640 if (lpwfs->download_in_progress != NULL)
1642 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1643 goto lend;
1646 hIC = lpwfs->lpAppInfo;
1647 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1649 get_file_task_t *task;
1651 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetFileProc, sizeof(*task));
1652 task->remote_file = wcsdup(lpszRemoteFile);
1653 task->new_file = wcsdup(lpszNewFile);
1654 task->local_attr = dwLocalFlagsAttribute;
1655 task->fail_if_exists = fFailIfExists;
1656 task->flags = dwInternetFlags;
1657 task->context = dwContext;
1659 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1661 else
1663 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1664 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1667 lend:
1668 WININET_Release( &lpwfs->hdr );
1670 return r;
1674 /***********************************************************************
1675 * FTP_FtpGetFileW (Internal)
1677 * Retrieve file from the FTP server
1679 * RETURNS
1680 * TRUE on success
1681 * FALSE on failure
1684 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1685 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1686 DWORD_PTR dwContext)
1688 BOOL bSuccess = FALSE;
1689 HANDLE hFile;
1690 appinfo_t *hIC = NULL;
1692 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1694 /* Clear any error information */
1695 INTERNET_SetLastError(0);
1697 /* Ensure we can write to lpszNewfile by opening it */
1698 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1699 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1700 if (INVALID_HANDLE_VALUE == hFile)
1701 return FALSE;
1703 /* Set up socket to retrieve data */
1704 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1706 INT nDataSocket;
1708 /* Get data socket to server */
1709 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1711 INT nResCode;
1713 /* Receive data */
1714 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1715 closesocket(nDataSocket);
1717 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1718 if (nResCode)
1720 if (nResCode == 226)
1721 bSuccess = TRUE;
1722 else
1723 FTP_SetResponseError(nResCode);
1728 if (lpwfs->lstnSocket != -1)
1730 closesocket(lpwfs->lstnSocket);
1731 lpwfs->lstnSocket = -1;
1734 CloseHandle(hFile);
1736 hIC = lpwfs->lpAppInfo;
1737 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1739 INTERNET_ASYNC_RESULT iar;
1741 iar.dwResult = (DWORD)bSuccess;
1742 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1743 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1744 &iar, sizeof(INTERNET_ASYNC_RESULT));
1747 if (!bSuccess) DeleteFileW(lpszNewFile);
1748 return bSuccess;
1751 /***********************************************************************
1752 * FtpGetFileSize (WININET.@)
1754 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1756 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1758 if (lpdwFileSizeHigh)
1759 *lpdwFileSizeHigh = 0;
1761 return 0;
1764 /***********************************************************************
1765 * FtpDeleteFileA (WININET.@)
1767 * Delete a file on the ftp server
1769 * RETURNS
1770 * TRUE on success
1771 * FALSE on failure
1774 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1776 LPWSTR lpwzFileName;
1777 BOOL ret;
1779 lpwzFileName = strdupAtoW(lpszFileName);
1780 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1781 free(lpwzFileName);
1782 return ret;
1785 typedef struct {
1786 task_header_t hdr;
1787 WCHAR *file_name;
1788 } delete_file_task_t;
1790 static void AsyncFtpDeleteFileProc(task_header_t *hdr)
1792 delete_file_task_t *task = (delete_file_task_t*)hdr;
1793 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1795 TRACE("%p\n", session);
1797 FTP_FtpDeleteFileW(session, task->file_name);
1798 free(task->file_name);
1801 /***********************************************************************
1802 * FtpDeleteFileW (WININET.@)
1804 * Delete a file on the ftp server
1806 * RETURNS
1807 * TRUE on success
1808 * FALSE on failure
1811 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1813 ftp_session_t *lpwfs;
1814 appinfo_t *hIC = NULL;
1815 BOOL r = FALSE;
1817 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1818 if (!lpwfs)
1820 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1821 return FALSE;
1824 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1826 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1827 goto lend;
1830 if (lpwfs->download_in_progress != NULL)
1832 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1833 goto lend;
1836 if (!lpszFileName)
1838 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1839 goto lend;
1842 hIC = lpwfs->lpAppInfo;
1843 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1845 delete_file_task_t *task;
1847 task = alloc_async_task(&lpwfs->hdr, AsyncFtpDeleteFileProc, sizeof(*task));
1848 task->file_name = wcsdup(lpszFileName);
1850 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1852 else
1854 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1857 lend:
1858 WININET_Release( &lpwfs->hdr );
1860 return r;
1863 /***********************************************************************
1864 * FTP_FtpDeleteFileW (Internal)
1866 * Delete a file on the ftp server
1868 * RETURNS
1869 * TRUE on success
1870 * FALSE on failure
1873 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1875 INT nResCode;
1876 BOOL bSuccess = FALSE;
1877 appinfo_t *hIC = NULL;
1879 TRACE("%p\n", lpwfs);
1881 /* Clear any error information */
1882 INTERNET_SetLastError(0);
1884 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1885 goto lend;
1887 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1888 if (nResCode)
1890 if (nResCode == 250)
1891 bSuccess = TRUE;
1892 else
1893 FTP_SetResponseError(nResCode);
1895 lend:
1896 hIC = lpwfs->lpAppInfo;
1897 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1899 INTERNET_ASYNC_RESULT iar;
1901 iar.dwResult = (DWORD)bSuccess;
1902 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1903 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1904 &iar, sizeof(INTERNET_ASYNC_RESULT));
1907 return bSuccess;
1911 /***********************************************************************
1912 * FtpRemoveDirectoryA (WININET.@)
1914 * Remove a directory on the ftp server
1916 * RETURNS
1917 * TRUE on success
1918 * FALSE on failure
1921 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1923 LPWSTR lpwzDirectory;
1924 BOOL ret;
1926 lpwzDirectory = strdupAtoW(lpszDirectory);
1927 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1928 free(lpwzDirectory);
1929 return ret;
1932 static void AsyncFtpRemoveDirectoryProc(task_header_t *hdr)
1934 directory_task_t *task = (directory_task_t*)hdr;
1935 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1937 TRACE("%p\n", session);
1939 FTP_FtpRemoveDirectoryW(session, task->directory);
1940 free(task->directory);
1943 /***********************************************************************
1944 * FtpRemoveDirectoryW (WININET.@)
1946 * Remove a directory on the ftp server
1948 * RETURNS
1949 * TRUE on success
1950 * FALSE on failure
1953 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1955 ftp_session_t *lpwfs;
1956 appinfo_t *hIC = NULL;
1957 BOOL r = FALSE;
1959 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1960 if (!lpwfs)
1962 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1963 return FALSE;
1966 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1968 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1969 goto lend;
1972 if (lpwfs->download_in_progress != NULL)
1974 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1975 goto lend;
1978 if (!lpszDirectory)
1980 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1981 goto lend;
1984 hIC = lpwfs->lpAppInfo;
1985 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1987 directory_task_t *task;
1989 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRemoveDirectoryProc, sizeof(*task));
1990 task->directory = wcsdup(lpszDirectory);
1992 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1994 else
1996 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1999 lend:
2000 WININET_Release( &lpwfs->hdr );
2002 return r;
2005 /***********************************************************************
2006 * FTP_FtpRemoveDirectoryW (Internal)
2008 * Remove a directory on the ftp server
2010 * RETURNS
2011 * TRUE on success
2012 * FALSE on failure
2015 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
2017 INT nResCode;
2018 BOOL bSuccess = FALSE;
2019 appinfo_t *hIC = NULL;
2021 TRACE("\n");
2023 /* Clear any error information */
2024 INTERNET_SetLastError(0);
2026 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2027 goto lend;
2029 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2030 if (nResCode)
2032 if (nResCode == 250)
2033 bSuccess = TRUE;
2034 else
2035 FTP_SetResponseError(nResCode);
2038 lend:
2039 hIC = lpwfs->lpAppInfo;
2040 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2042 INTERNET_ASYNC_RESULT iar;
2044 iar.dwResult = (DWORD)bSuccess;
2045 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2046 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2047 &iar, sizeof(INTERNET_ASYNC_RESULT));
2050 return bSuccess;
2054 /***********************************************************************
2055 * FtpRenameFileA (WININET.@)
2057 * Rename a file on the ftp server
2059 * RETURNS
2060 * TRUE on success
2061 * FALSE on failure
2064 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2066 LPWSTR lpwzSrc;
2067 LPWSTR lpwzDest;
2068 BOOL ret;
2070 lpwzSrc = strdupAtoW(lpszSrc);
2071 lpwzDest = strdupAtoW(lpszDest);
2072 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2073 free(lpwzSrc);
2074 free(lpwzDest);
2075 return ret;
2078 typedef struct {
2079 task_header_t hdr;
2080 WCHAR *src_file;
2081 WCHAR *dst_file;
2082 } rename_file_task_t;
2084 static void AsyncFtpRenameFileProc(task_header_t *hdr)
2086 rename_file_task_t *task = (rename_file_task_t*)hdr;
2087 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
2089 TRACE("%p\n", session);
2091 FTP_FtpRenameFileW(session, task->src_file, task->dst_file);
2092 free(task->src_file);
2093 free(task->dst_file);
2096 /***********************************************************************
2097 * FtpRenameFileW (WININET.@)
2099 * Rename a file on the ftp server
2101 * RETURNS
2102 * TRUE on success
2103 * FALSE on failure
2106 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2108 ftp_session_t *lpwfs;
2109 appinfo_t *hIC = NULL;
2110 BOOL r = FALSE;
2112 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
2113 if (!lpwfs)
2115 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2116 return FALSE;
2119 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2121 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2122 goto lend;
2125 if (lpwfs->download_in_progress != NULL)
2127 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2128 goto lend;
2131 if (!lpszSrc || !lpszDest)
2133 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2134 goto lend;
2137 hIC = lpwfs->lpAppInfo;
2138 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2140 rename_file_task_t *task;
2142 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRenameFileProc, sizeof(*task));
2143 task->src_file = wcsdup(lpszSrc);
2144 task->dst_file = wcsdup(lpszDest);
2146 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
2148 else
2150 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2153 lend:
2154 WININET_Release( &lpwfs->hdr );
2156 return r;
2159 /***********************************************************************
2160 * FTP_FtpRenameFileW (Internal)
2162 * Rename a file on the ftp server
2164 * RETURNS
2165 * TRUE on success
2166 * FALSE on failure
2169 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2171 INT nResCode;
2172 BOOL bSuccess = FALSE;
2173 appinfo_t *hIC = NULL;
2175 TRACE("\n");
2177 /* Clear any error information */
2178 INTERNET_SetLastError(0);
2180 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2181 goto lend;
2183 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2184 if (nResCode == 350)
2186 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2187 goto lend;
2189 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2192 if (nResCode == 250)
2193 bSuccess = TRUE;
2194 else
2195 FTP_SetResponseError(nResCode);
2197 lend:
2198 hIC = lpwfs->lpAppInfo;
2199 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2201 INTERNET_ASYNC_RESULT iar;
2203 iar.dwResult = (DWORD)bSuccess;
2204 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2205 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2206 &iar, sizeof(INTERNET_ASYNC_RESULT));
2209 return bSuccess;
2212 /***********************************************************************
2213 * FtpCommandA (WININET.@)
2215 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2216 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2218 BOOL r;
2219 WCHAR *cmdW;
2221 TRACE("%p %d 0x%08lx %s 0x%08Ix %p\n", hConnect, fExpectResponse, dwFlags,
2222 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2224 if (fExpectResponse)
2226 FIXME("data connection not supported\n");
2227 return FALSE;
2230 if (!lpszCommand || !lpszCommand[0])
2232 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2233 return FALSE;
2236 if (!(cmdW = strdupAtoW(lpszCommand)))
2238 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2239 return FALSE;
2242 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2244 free(cmdW);
2245 return r;
2248 /***********************************************************************
2249 * FtpCommandW (WININET.@)
2251 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2252 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2254 BOOL r = FALSE;
2255 ftp_session_t *lpwfs;
2256 LPSTR cmd = NULL;
2257 DWORD len, nBytesSent= 0;
2258 INT nResCode, nRC = 0;
2260 TRACE("%p %d 0x%08lx %s 0x%08Ix %p\n", hConnect, fExpectResponse, dwFlags,
2261 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2263 if (!lpszCommand || !lpszCommand[0])
2265 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2266 return FALSE;
2269 if (fExpectResponse)
2271 FIXME("data connection not supported\n");
2272 return FALSE;
2275 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
2276 if (!lpwfs)
2278 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2279 return FALSE;
2282 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2284 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2285 goto lend;
2288 if (lpwfs->download_in_progress != NULL)
2290 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2291 goto lend;
2294 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2295 if ((cmd = malloc(len)))
2296 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2297 else
2299 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2300 goto lend;
2303 strcat(cmd, szCRLF);
2304 len--;
2306 TRACE("Sending (%s) len(%ld)\n", debugstr_a(cmd), len);
2307 while ((nBytesSent < len) && (nRC != -1))
2309 nRC = sock_send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2310 if (nRC != -1)
2312 nBytesSent += nRC;
2313 TRACE("Sent %d bytes\n", nRC);
2317 if (nBytesSent)
2319 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2320 if (nResCode > 0 && nResCode < 400)
2321 r = TRUE;
2322 else
2323 FTP_SetResponseError(nResCode);
2326 lend:
2327 WININET_Release( &lpwfs->hdr );
2328 free(cmd);
2329 return r;
2333 /***********************************************************************
2334 * FTPSESSION_Destroy (internal)
2336 * Deallocate session handle
2338 static void FTPSESSION_Destroy(object_header_t *hdr)
2340 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2342 TRACE("\n");
2344 WININET_Release(&lpwfs->lpAppInfo->hdr);
2346 free(lpwfs->lpszPassword);
2347 free(lpwfs->lpszUserName);
2348 free(lpwfs->servername);
2351 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2353 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2355 TRACE("\n");
2357 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2358 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2360 if (lpwfs->download_in_progress != NULL)
2361 lpwfs->download_in_progress->session_deleted = TRUE;
2363 if (lpwfs->sndSocket != -1)
2364 closesocket(lpwfs->sndSocket);
2366 if (lpwfs->lstnSocket != -1)
2367 closesocket(lpwfs->lstnSocket);
2369 if (lpwfs->pasvSocket != -1)
2370 closesocket(lpwfs->pasvSocket);
2372 INTERNET_SendCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2373 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2376 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2378 switch(option) {
2379 case INTERNET_OPTION_HANDLE_TYPE:
2380 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2382 if (*size < sizeof(ULONG))
2383 return ERROR_INSUFFICIENT_BUFFER;
2385 *size = sizeof(DWORD);
2386 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2387 return ERROR_SUCCESS;
2390 return INET_QueryOption(hdr, option, buffer, size, unicode);
2393 static const object_vtbl_t FTPSESSIONVtbl = {
2394 FTPSESSION_Destroy,
2395 FTPSESSION_CloseConnection,
2396 FTPSESSION_QueryOption,
2397 INET_SetOption,
2398 NULL,
2399 NULL,
2400 NULL,
2401 NULL,
2402 NULL
2406 /***********************************************************************
2407 * FTP_Connect (internal)
2409 * Connect to a ftp server
2411 * RETURNS
2412 * HINTERNET a session handle on success
2413 * NULL on failure
2415 * NOTES:
2417 * Windows uses 'anonymous' as the username, when given a NULL username
2418 * and a NULL password. The password is first looked up in:
2420 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2422 * If this entry is not present it uses the current username as the password.
2426 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2427 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2428 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2429 DWORD dwInternalFlags)
2431 struct sockaddr_in socketAddr;
2432 INT nsocket = -1;
2433 socklen_t sock_namelen;
2434 BOOL bSuccess = FALSE;
2435 ftp_session_t *lpwfs = NULL;
2436 char szaddr[INET6_ADDRSTRLEN];
2438 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2439 hIC, debugstr_w(lpszServerName),
2440 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2442 assert( hIC->hdr.htype == WH_HINIT );
2444 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2446 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2447 return NULL;
2450 lpwfs = alloc_object(&hIC->hdr, &FTPSESSIONVtbl, sizeof(ftp_session_t));
2451 if (NULL == lpwfs)
2453 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2454 return NULL;
2457 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2458 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2459 else
2460 lpwfs->serverport = nServerPort;
2462 lpwfs->hdr.htype = WH_HFTPSESSION;
2463 lpwfs->hdr.dwFlags = dwFlags;
2464 lpwfs->hdr.dwContext = dwContext;
2465 lpwfs->hdr.dwInternalFlags |= dwInternalFlags;
2466 lpwfs->download_in_progress = NULL;
2467 lpwfs->sndSocket = -1;
2468 lpwfs->lstnSocket = -1;
2469 lpwfs->pasvSocket = -1;
2471 WININET_AddRef( &hIC->hdr );
2472 lpwfs->lpAppInfo = hIC;
2473 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2475 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
2476 if(wcschr(hIC->proxy, ' '))
2477 FIXME("Several proxies not implemented.\n");
2478 if(hIC->proxyBypass)
2479 FIXME("Proxy bypass is ignored.\n");
2481 if (!lpszUserName || !lpszUserName[0]) {
2482 HKEY key;
2483 WCHAR szPassword[MAX_PATH];
2484 DWORD len = sizeof(szPassword);
2486 lpwfs->lpszUserName = wcsdup(L"anonymous");
2488 RegOpenKeyW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", &key);
2489 if (RegQueryValueExW(key, L"EmailName", NULL, NULL, (LPBYTE)szPassword, &len)) {
2490 /* Nothing in the registry, get the username and use that as the password */
2491 if (!GetUserNameW(szPassword, &len)) {
2492 /* Should never get here, but use an empty password as failsafe */
2493 lstrcpyW(szPassword, L"");
2496 RegCloseKey(key);
2498 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2499 lpwfs->lpszPassword = wcsdup(szPassword);
2501 else {
2502 lpwfs->lpszUserName = wcsdup(lpszUserName);
2503 lpwfs->lpszPassword = wcsdup(lpszPassword ? lpszPassword : L"");
2505 lpwfs->servername = wcsdup(lpszServerName);
2507 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2508 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2510 INTERNET_ASYNC_RESULT iar;
2512 iar.dwResult = (DWORD_PTR)lpwfs->hdr.hInternet;
2513 iar.dwError = ERROR_SUCCESS;
2515 INTERNET_SendCallback(&hIC->hdr, dwContext,
2516 INTERNET_STATUS_HANDLE_CREATED, &iar,
2517 sizeof(INTERNET_ASYNC_RESULT));
2520 INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2521 (LPWSTR) lpszServerName, (lstrlenW(lpszServerName)+1) * sizeof(WCHAR));
2523 sock_namelen = sizeof(socketAddr);
2524 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen, szaddr))
2526 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2527 goto lerror;
2530 if (socketAddr.sin_family != AF_INET)
2532 WARN("unsupported address family %d\n", socketAddr.sin_family);
2533 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2534 goto lerror;
2537 INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2538 szaddr, strlen(szaddr)+1);
2540 init_winsock();
2541 nsocket = socket(AF_INET,SOCK_STREAM,0);
2542 if (nsocket == -1)
2544 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2545 goto lerror;
2548 INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2549 szaddr, strlen(szaddr)+1);
2551 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2553 ERR("Unable to connect (%d)\n", WSAGetLastError());
2554 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2555 closesocket(nsocket);
2557 else
2559 TRACE("Connected to server\n");
2560 lpwfs->sndSocket = nsocket;
2561 INTERNET_SendCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2562 szaddr, strlen(szaddr)+1);
2564 sock_namelen = sizeof(lpwfs->socketAddress);
2565 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2567 if (FTP_ConnectToHost(lpwfs))
2569 TRACE("Successfully logged into server\n");
2570 bSuccess = TRUE;
2574 lerror:
2575 if (!bSuccess)
2577 WININET_Release(&lpwfs->hdr);
2578 return NULL;
2581 return lpwfs->hdr.hInternet;
2585 /***********************************************************************
2586 * FTP_ConnectToHost (internal)
2588 * Connect to a ftp server
2590 * RETURNS
2591 * TRUE on success
2592 * NULL on failure
2595 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2597 INT nResCode;
2598 BOOL bSuccess = FALSE;
2600 TRACE("\n");
2601 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2603 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2604 goto lend;
2606 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2607 if (nResCode)
2609 /* Login successful... */
2610 if (nResCode == 230)
2611 bSuccess = TRUE;
2612 /* User name okay, need password... */
2613 else if (nResCode == 331)
2614 bSuccess = FTP_SendPassword(lpwfs);
2615 /* Need account for login... */
2616 else if (nResCode == 332)
2617 bSuccess = FTP_SendAccount(lpwfs);
2618 else
2619 FTP_SetResponseError(nResCode);
2622 TRACE("Returning %d\n", bSuccess);
2623 lend:
2624 return bSuccess;
2627 /***********************************************************************
2628 * FTP_GetNextLine (internal)
2630 * Parse next line in directory string listing
2632 * RETURNS
2633 * Pointer to beginning of next line
2634 * NULL on failure
2638 static LPSTR FTP_GetNextLine(INT nSocket, LPDWORD dwLen)
2640 struct timeval tv = {RESPONSE_TIMEOUT,0};
2641 FD_SET set;
2642 INT nRecv = 0;
2643 LPSTR lpszBuffer = INTERNET_GetResponseBuffer();
2645 TRACE("\n");
2647 FD_ZERO(&set);
2648 FD_SET(nSocket, &set);
2650 while (nRecv < MAX_REPLY_LEN)
2652 if (select(nSocket+1, &set, NULL, NULL, &tv) > 0)
2654 if (sock_recv(nSocket, &lpszBuffer[nRecv], 1, 0) <= 0)
2656 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2657 return NULL;
2660 if (lpszBuffer[nRecv] == '\n')
2662 lpszBuffer[nRecv] = '\0';
2663 *dwLen = nRecv - 1;
2664 TRACE(":%d %s\n", nRecv, lpszBuffer);
2665 return lpszBuffer;
2667 if (lpszBuffer[nRecv] != '\r')
2668 nRecv++;
2670 else
2672 INTERNET_SetLastError(ERROR_INTERNET_TIMEOUT);
2673 return NULL;
2677 return NULL;
2680 /***********************************************************************
2681 * FTP_SendCommandA (internal)
2683 * Send command to server
2685 * RETURNS
2686 * TRUE on success
2687 * NULL on failure
2690 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2691 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2693 DWORD len;
2694 CHAR *buf;
2695 DWORD nBytesSent = 0;
2696 int nRC = 0;
2697 DWORD dwParamLen;
2699 TRACE("%d: (%s) %d\n", ftpCmd, debugstr_a(lpszParam), nSocket);
2701 if (lpfnStatusCB)
2703 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2706 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2707 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2708 if (NULL == (buf = malloc(len + 1)))
2710 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2711 return FALSE;
2713 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2714 dwParamLen ? lpszParam : "", szCRLF);
2716 TRACE("Sending (%s) len(%ld)\n", debugstr_a(buf), len);
2717 while((nBytesSent < len) && (nRC != -1))
2719 nRC = sock_send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2720 nBytesSent += nRC;
2722 free(buf);
2724 if (lpfnStatusCB)
2726 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2727 &nBytesSent, sizeof(DWORD));
2730 TRACE("Sent %ld bytes\n", nBytesSent);
2731 return (nRC != -1);
2734 /***********************************************************************
2735 * FTP_SendCommand (internal)
2737 * Send command to server
2739 * RETURNS
2740 * TRUE on success
2741 * NULL on failure
2744 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2745 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2747 BOOL ret;
2748 char *lpszParamA = strdupWtoA(lpszParam);
2749 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2750 free(lpszParamA);
2751 return ret;
2754 /***********************************************************************
2755 * FTP_ReceiveResponse (internal)
2757 * Receive response from server
2759 * RETURNS
2760 * Reply code on success
2761 * 0 on failure
2764 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2766 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2767 DWORD nRecv;
2768 INT rc = 0;
2769 char firstprefix[5];
2770 BOOL multiline = FALSE;
2772 TRACE("socket(%d)\n", lpwfs->sndSocket);
2774 INTERNET_SendCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2776 while(1)
2778 if (!FTP_GetNextLine(lpwfs->sndSocket, &nRecv))
2779 goto lerror;
2781 if (nRecv >= 3)
2783 if(!multiline)
2785 if(lpszResponse[3] != '-')
2786 break;
2787 else
2788 { /* Start of multiline response. Loop until we get "nnn " */
2789 multiline = TRUE;
2790 memcpy(firstprefix, lpszResponse, 3);
2791 firstprefix[3] = ' ';
2792 firstprefix[4] = '\0';
2795 else
2797 if(!memcmp(firstprefix, lpszResponse, 4))
2798 break;
2803 if (nRecv >= 3)
2805 rc = atoi(lpszResponse);
2807 INTERNET_SendCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2808 &nRecv, sizeof(DWORD));
2811 lerror:
2812 TRACE("return %d\n", rc);
2813 return rc;
2817 /***********************************************************************
2818 * FTP_SendPassword (internal)
2820 * Send password to ftp server
2822 * RETURNS
2823 * TRUE on success
2824 * NULL on failure
2827 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2829 INT nResCode;
2830 BOOL bSuccess = FALSE;
2832 TRACE("\n");
2833 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2834 goto lend;
2836 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2837 if (nResCode)
2839 TRACE("Received reply code %d\n", nResCode);
2840 /* Login successful... */
2841 if (nResCode == 230)
2842 bSuccess = TRUE;
2843 /* Command not implemented, superfluous at the server site... */
2844 /* Need account for login... */
2845 else if (nResCode == 332)
2846 bSuccess = FTP_SendAccount(lpwfs);
2847 else
2848 FTP_SetResponseError(nResCode);
2851 lend:
2852 TRACE("Returning %d\n", bSuccess);
2853 return bSuccess;
2857 /***********************************************************************
2858 * FTP_SendAccount (internal)
2862 * RETURNS
2863 * TRUE on success
2864 * FALSE on failure
2867 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2869 INT nResCode;
2870 BOOL bSuccess = FALSE;
2872 TRACE("\n");
2873 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, L"noaccount", 0, 0, 0))
2874 goto lend;
2876 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2877 if (nResCode)
2878 bSuccess = TRUE;
2879 else
2880 FTP_SetResponseError(nResCode);
2882 lend:
2883 return bSuccess;
2887 /***********************************************************************
2888 * FTP_SendStore (internal)
2890 * Send request to upload file to ftp server
2892 * RETURNS
2893 * TRUE on success
2894 * FALSE on failure
2897 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2899 INT nResCode;
2900 BOOL bSuccess = FALSE;
2902 TRACE("\n");
2903 if (!FTP_InitListenSocket(lpwfs))
2904 goto lend;
2906 if (!FTP_SendType(lpwfs, dwType))
2907 goto lend;
2909 if (!FTP_SendPortOrPasv(lpwfs))
2910 goto lend;
2912 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2913 goto lend;
2914 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2915 if (nResCode)
2917 if (nResCode == 150 || nResCode == 125)
2918 bSuccess = TRUE;
2919 else
2920 FTP_SetResponseError(nResCode);
2923 lend:
2924 if (!bSuccess && lpwfs->lstnSocket != -1)
2926 closesocket(lpwfs->lstnSocket);
2927 lpwfs->lstnSocket = -1;
2930 return bSuccess;
2934 /***********************************************************************
2935 * FTP_InitListenSocket (internal)
2937 * Create a socket to listen for server response
2939 * RETURNS
2940 * TRUE on success
2941 * FALSE on failure
2944 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2946 BOOL bSuccess = FALSE;
2947 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2949 TRACE("\n");
2951 init_winsock();
2952 lpwfs->lstnSocket = socket(AF_INET, SOCK_STREAM, 0);
2953 if (lpwfs->lstnSocket == -1)
2955 TRACE("Unable to create listening socket\n");
2956 goto lend;
2959 /* We obtain our ip addr from the name of the command channel socket */
2960 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2962 /* and get the system to assign us a port */
2963 lpwfs->lstnSocketAddress.sin_port = htons(0);
2965 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2967 TRACE("Unable to bind socket\n");
2968 goto lend;
2971 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2973 TRACE("listen failed\n");
2974 goto lend;
2977 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2978 bSuccess = TRUE;
2980 lend:
2981 if (!bSuccess && lpwfs->lstnSocket != -1)
2983 closesocket(lpwfs->lstnSocket);
2984 lpwfs->lstnSocket = -1;
2987 return bSuccess;
2991 /***********************************************************************
2992 * FTP_SendType (internal)
2994 * Tell server type of data being transferred
2996 * RETURNS
2997 * TRUE on success
2998 * FALSE on failure
3000 * W98SE doesn't cache the type that's currently set
3001 * (i.e. it sends it always),
3002 * so we probably don't want to do that either.
3004 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
3006 INT nResCode;
3007 WCHAR type[] = L"I";
3008 BOOL bSuccess = FALSE;
3010 TRACE("\n");
3011 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
3012 type[0] = 'A';
3014 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
3015 goto lend;
3017 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
3018 if (nResCode)
3020 if (nResCode == 2)
3021 bSuccess = TRUE;
3022 else
3023 FTP_SetResponseError(nResCode);
3026 lend:
3027 return bSuccess;
3031 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
3032 /***********************************************************************
3033 * FTP_GetFileSize (internal)
3035 * Retrieves from the server the size of the given file
3037 * RETURNS
3038 * TRUE on success
3039 * FALSE on failure
3042 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
3044 INT nResCode;
3045 BOOL bSuccess = FALSE;
3047 TRACE("\n");
3049 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
3050 goto lend;
3052 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3053 if (nResCode)
3055 if (nResCode == 213) {
3056 /* Now parses the output to get the actual file size */
3057 int i;
3058 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3060 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3061 if (lpszResponseBuffer[i] == '\0') return FALSE;
3062 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3064 bSuccess = TRUE;
3065 } else {
3066 FTP_SetResponseError(nResCode);
3070 lend:
3071 return bSuccess;
3073 #endif
3076 /***********************************************************************
3077 * FTP_SendPort (internal)
3079 * Tell server which port to use
3081 * RETURNS
3082 * TRUE on success
3083 * FALSE on failure
3086 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3088 INT nResCode;
3089 WCHAR szIPAddress[64];
3090 BOOL bSuccess = FALSE;
3091 TRACE("\n");
3093 swprintf(szIPAddress, ARRAY_SIZE(szIPAddress), L"%d,%d,%d,%d,%d,%d",
3094 lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x000000FF,
3095 (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x0000FF00)>>8,
3096 (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0x00FF0000)>>16,
3097 (lpwfs->lstnSocketAddress.sin_addr.S_un.S_addr&0xFF000000)>>24,
3098 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3099 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3101 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3102 goto lend;
3104 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3105 if (nResCode)
3107 if (nResCode == 200)
3108 bSuccess = TRUE;
3109 else
3110 FTP_SetResponseError(nResCode);
3113 lend:
3114 return bSuccess;
3118 /***********************************************************************
3119 * FTP_DoPassive (internal)
3121 * Tell server that we want to do passive transfers
3122 * and connect data socket
3124 * RETURNS
3125 * TRUE on success
3126 * FALSE on failure
3129 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3131 INT nResCode;
3132 BOOL bSuccess = FALSE;
3134 TRACE("\n");
3135 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3136 goto lend;
3138 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3139 if (nResCode)
3141 if (nResCode == 227)
3143 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3144 LPSTR p;
3145 int f[6];
3146 int i;
3147 char *pAddr, *pPort;
3148 INT nsocket = -1;
3149 struct sockaddr_in dataSocketAddress;
3151 p = lpszResponseBuffer+4; /* skip status code */
3152 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3154 if (*p == '\0')
3156 ERR("no address found in response, aborting\n");
3157 goto lend;
3160 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3161 &f[4], &f[5]) != 6)
3163 ERR("unknown response address format '%s', aborting\n", p);
3164 goto lend;
3166 for (i=0; i < 6; i++)
3167 f[i] = f[i] & 0xff;
3169 dataSocketAddress = lpwfs->socketAddress;
3170 pAddr = (char *)&(dataSocketAddress.sin_addr.S_un.S_addr);
3171 pPort = (char *)&(dataSocketAddress.sin_port);
3172 pAddr[0] = f[0];
3173 pAddr[1] = f[1];
3174 pAddr[2] = f[2];
3175 pAddr[3] = f[3];
3176 pPort[0] = f[4];
3177 pPort[1] = f[5];
3179 nsocket = socket(AF_INET,SOCK_STREAM,0);
3180 if (nsocket == -1)
3181 goto lend;
3183 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3185 ERR("can't connect passive FTP data port.\n");
3186 closesocket(nsocket);
3187 goto lend;
3189 lpwfs->pasvSocket = nsocket;
3190 bSuccess = TRUE;
3192 else
3193 FTP_SetResponseError(nResCode);
3196 lend:
3197 return bSuccess;
3201 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3203 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3205 if (!FTP_DoPassive(lpwfs))
3206 return FALSE;
3208 else
3210 if (!FTP_SendPort(lpwfs))
3211 return FALSE;
3213 return TRUE;
3217 /***********************************************************************
3218 * FTP_GetDataSocket (internal)
3220 * Either accepts an incoming data socket connection from the server
3221 * or just returns the already opened socket after a PASV command
3222 * in case of passive FTP.
3225 * RETURNS
3226 * TRUE on success
3227 * FALSE on failure
3230 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3232 struct sockaddr_in saddr;
3233 socklen_t addrlen = sizeof(saddr);
3235 TRACE("\n");
3236 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3238 *nDataSocket = lpwfs->pasvSocket;
3239 lpwfs->pasvSocket = -1;
3241 else
3243 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3244 closesocket(lpwfs->lstnSocket);
3245 lpwfs->lstnSocket = -1;
3247 return *nDataSocket != -1;
3251 /***********************************************************************
3252 * FTP_SendData (internal)
3254 * Send data to the server
3256 * RETURNS
3257 * TRUE on success
3258 * FALSE on failure
3261 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3263 BY_HANDLE_FILE_INFORMATION fi;
3264 DWORD nBytesRead = 0;
3265 DWORD nBytesSent = 0;
3266 DWORD nTotalSent = 0;
3267 DWORD nBytesToSend, nLen;
3268 int nRC = 1;
3269 time_t s_long_time, e_long_time;
3270 LONG nSeconds;
3271 CHAR *lpszBuffer;
3273 TRACE("\n");
3274 lpszBuffer = calloc(DATA_PACKET_SIZE, 1);
3276 /* Get the size of the file. */
3277 GetFileInformationByHandle(hFile, &fi);
3278 time(&s_long_time);
3282 nBytesToSend = nBytesRead - nBytesSent;
3284 if (nBytesToSend <= 0)
3286 /* Read data from file. */
3287 nBytesSent = 0;
3288 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3289 ERR("Failed reading from file\n");
3291 if (nBytesRead > 0)
3292 nBytesToSend = nBytesRead;
3293 else
3294 break;
3297 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3298 DATA_PACKET_SIZE : nBytesToSend;
3299 nRC = sock_send(nDataSocket, lpszBuffer, nLen, 0);
3301 if (nRC != -1)
3303 nBytesSent += nRC;
3304 nTotalSent += nRC;
3307 /* Do some computation to display the status. */
3308 time(&e_long_time);
3309 nSeconds = e_long_time - s_long_time;
3310 if( nSeconds / 60 > 0 )
3312 TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld min %ld sec estimated remaining time %ld sec\n",
3313 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3314 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3316 else
3318 TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld sec estimated remaining time %ld sec\n",
3319 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3320 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3322 } while (nRC != -1);
3324 TRACE("file transfer complete!\n");
3326 free(lpszBuffer);
3327 return nTotalSent;
3331 /***********************************************************************
3332 * FTP_SendRetrieve (internal)
3334 * Send request to retrieve a file
3336 * RETURNS
3337 * Number of bytes to be received on success
3338 * 0 on failure
3341 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3343 INT nResCode;
3344 BOOL ret;
3346 TRACE("\n");
3347 if (!(ret = FTP_InitListenSocket(lpwfs)))
3348 goto lend;
3350 if (!(ret = FTP_SendType(lpwfs, dwType)))
3351 goto lend;
3353 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3354 goto lend;
3356 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3357 goto lend;
3359 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3360 if ((nResCode != 125) && (nResCode != 150)) {
3361 /* That means that we got an error getting the file. */
3362 FTP_SetResponseError(nResCode);
3363 ret = FALSE;
3366 lend:
3367 if (!ret && lpwfs->lstnSocket != -1)
3369 closesocket(lpwfs->lstnSocket);
3370 lpwfs->lstnSocket = -1;
3373 return ret;
3377 /***********************************************************************
3378 * FTP_RetrieveData (internal)
3380 * Retrieve data from server
3382 * RETURNS
3383 * TRUE on success
3384 * FALSE on failure
3387 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3389 DWORD nBytesWritten;
3390 INT nRC = 0;
3391 CHAR *lpszBuffer;
3393 TRACE("\n");
3395 lpszBuffer = calloc(DATA_PACKET_SIZE, 1);
3396 if (NULL == lpszBuffer)
3398 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3399 return FALSE;
3402 while (nRC != -1)
3404 nRC = sock_recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3405 if (nRC != -1)
3407 /* other side closed socket. */
3408 if (nRC == 0)
3409 goto recv_end;
3410 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3414 TRACE("Data transfer complete\n");
3416 recv_end:
3417 free(lpszBuffer);
3418 return (nRC != -1);
3421 /***********************************************************************
3422 * FTPFINDNEXT_Destroy (internal)
3424 * Deallocate session handle
3426 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3428 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3429 DWORD i;
3431 TRACE("\n");
3433 WININET_Release(&lpwfn->lpFtpSession->hdr);
3435 for (i = 0; i < lpwfn->size; i++)
3437 free(lpwfn->lpafp[i].lpszName);
3439 free(lpwfn->lpafp);
3442 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3444 WIN32_FIND_DATAW *find_data = data;
3445 DWORD res = ERROR_SUCCESS;
3447 TRACE("index(%ld) size(%ld)\n", find->index, find->size);
3449 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3451 if (find->index < find->size) {
3452 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3453 find->index++;
3455 TRACE("Name: %s\nSize: %ld\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3456 }else {
3457 res = ERROR_NO_MORE_FILES;
3460 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3462 INTERNET_ASYNC_RESULT iar;
3464 iar.dwResult = (res == ERROR_SUCCESS);
3465 iar.dwError = res;
3467 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3468 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3469 sizeof(INTERNET_ASYNC_RESULT));
3472 return res;
3475 typedef struct {
3476 task_header_t hdr;
3477 WIN32_FIND_DATAW *find_data;
3478 } find_next_task_t;
3480 static void FTPFINDNEXT_AsyncFindNextFileProc(task_header_t *hdr)
3482 find_next_task_t *task = (find_next_task_t*)hdr;
3484 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)task->hdr.hdr, task->find_data);
3487 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3489 switch(option) {
3490 case INTERNET_OPTION_HANDLE_TYPE:
3491 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3493 if (*size < sizeof(ULONG))
3494 return ERROR_INSUFFICIENT_BUFFER;
3496 *size = sizeof(DWORD);
3497 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3498 return ERROR_SUCCESS;
3501 return INET_QueryOption(hdr, option, buffer, size, unicode);
3504 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3506 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3508 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3510 find_next_task_t *task;
3512 task = alloc_async_task(&find->hdr, FTPFINDNEXT_AsyncFindNextFileProc, sizeof(*task));
3513 task->find_data = data;
3515 INTERNET_AsyncCall(&task->hdr);
3516 return ERROR_SUCCESS;
3519 return FTPFINDNEXT_FindNextFileProc(find, data);
3522 static const object_vtbl_t FTPFINDNEXTVtbl = {
3523 FTPFINDNEXT_Destroy,
3524 NULL,
3525 FTPFINDNEXT_QueryOption,
3526 INET_SetOption,
3527 NULL,
3528 NULL,
3529 NULL,
3530 NULL,
3531 FTPFINDNEXT_FindNextFileW
3534 /***********************************************************************
3535 * FTP_ReceiveFileList (internal)
3537 * Read file list from server
3539 * RETURNS
3540 * Handle to file list on success
3541 * NULL on failure
3544 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3545 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3547 DWORD dwSize = 0;
3548 LPFILEPROPERTIESW lpafp = NULL;
3549 LPWININETFTPFINDNEXTW lpwfn = NULL;
3551 TRACE("(%p,%d,%s,%p,%08Ix)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3553 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3555 if(lpFindFileData)
3556 FTP_ConvertFileProp(lpafp, lpFindFileData);
3558 lpwfn = alloc_object(&lpwfs->hdr, &FTPFINDNEXTVtbl, sizeof(WININETFTPFINDNEXTW));
3559 if (lpwfn)
3561 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3562 lpwfn->hdr.dwContext = dwContext;
3563 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3564 lpwfn->size = dwSize;
3565 lpwfn->lpafp = lpafp;
3567 WININET_AddRef( &lpwfs->hdr );
3568 lpwfn->lpFtpSession = lpwfs;
3569 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3573 TRACE("Matched %ld files\n", dwSize);
3574 return lpwfn ? lpwfn->hdr.hInternet : NULL;
3578 /***********************************************************************
3579 * FTP_ConvertFileProp (internal)
3581 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3583 * RETURNS
3584 * TRUE on success
3585 * FALSE on failure
3588 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3590 BOOL bSuccess = FALSE;
3592 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3594 if (lpafp)
3596 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3597 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3598 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3600 /* Not all fields are filled in */
3601 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3602 lpFindFileData->nFileSizeLow = lpafp->nSize;
3604 if (lpafp->bIsDirectory)
3605 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3607 if (lpafp->lpszName)
3608 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3610 bSuccess = TRUE;
3613 return bSuccess;
3616 /***********************************************************************
3617 * FTP_ParseNextFile (internal)
3619 * Parse the next line in file listing
3621 * RETURNS
3622 * TRUE on success
3623 * FALSE on failure
3625 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3627 static const char szSpace[] = " \t";
3628 DWORD nBufLen;
3629 char *pszLine;
3630 char *pszToken;
3631 char *pszTmp;
3632 BOOL found = FALSE;
3633 int i;
3635 lpfp->lpszName = NULL;
3636 do {
3637 if(!(pszLine = FTP_GetNextLine(nSocket, &nBufLen)))
3638 return FALSE;
3640 pszToken = strtok(pszLine, szSpace);
3641 /* ls format
3642 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3644 * For instance:
3645 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3647 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3648 if(!FTP_ParsePermission(pszToken, lpfp))
3649 lpfp->bIsDirectory = FALSE;
3650 for(i=0; i<=3; i++) {
3651 if(!(pszToken = strtok(NULL, szSpace)))
3652 break;
3654 if(!pszToken) continue;
3655 if(lpfp->bIsDirectory) {
3656 TRACE("Is directory\n");
3657 lpfp->nSize = 0;
3659 else {
3660 TRACE("Size: %s\n", pszToken);
3661 lpfp->nSize = atol(pszToken);
3664 lpfp->tmLastModified.wSecond = 0;
3665 lpfp->tmLastModified.wMinute = 0;
3666 lpfp->tmLastModified.wHour = 0;
3667 lpfp->tmLastModified.wDay = 0;
3668 lpfp->tmLastModified.wMonth = 0;
3669 lpfp->tmLastModified.wYear = 0;
3671 /* Determine month */
3672 pszToken = strtok(NULL, szSpace);
3673 if(!pszToken) continue;
3674 if(strlen(pszToken) >= 3) {
3675 pszToken[3] = 0;
3676 if((pszTmp = StrStrIA(szMonths, pszToken)))
3677 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3679 /* Determine day */
3680 pszToken = strtok(NULL, szSpace);
3681 if(!pszToken) continue;
3682 lpfp->tmLastModified.wDay = atoi(pszToken);
3683 /* Determine time or year */
3684 pszToken = strtok(NULL, szSpace);
3685 if(!pszToken) continue;
3686 if((pszTmp = strchr(pszToken, ':'))) {
3687 SYSTEMTIME curr_time;
3688 *pszTmp = 0;
3689 pszTmp++;
3690 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3691 lpfp->tmLastModified.wHour = atoi(pszToken);
3692 GetLocalTime( &curr_time );
3693 lpfp->tmLastModified.wYear = curr_time.wYear;
3695 else {
3696 lpfp->tmLastModified.wYear = atoi(pszToken);
3697 lpfp->tmLastModified.wHour = 12;
3699 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3700 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3701 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3703 pszToken = strtok(NULL, szSpace);
3704 if(!pszToken) continue;
3705 lpfp->lpszName = strdupAtoW(pszToken);
3706 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3708 /* NT way of parsing ... :
3710 07-13-03 08:55PM <DIR> sakpatch
3711 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3713 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3714 int mon, mday, year, hour, min;
3715 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3717 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3718 lpfp->tmLastModified.wDay = mday;
3719 lpfp->tmLastModified.wMonth = mon;
3720 lpfp->tmLastModified.wYear = year;
3722 /* Hacky and bad Y2K protection :-) */
3723 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3725 pszToken = strtok(NULL, szSpace);
3726 if(!pszToken) continue;
3727 sscanf(pszToken, "%d:%d", &hour, &min);
3728 lpfp->tmLastModified.wHour = hour;
3729 lpfp->tmLastModified.wMinute = min;
3730 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3731 lpfp->tmLastModified.wHour += 12;
3733 lpfp->tmLastModified.wSecond = 0;
3735 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3736 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3737 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3739 pszToken = strtok(NULL, szSpace);
3740 if(!pszToken) continue;
3741 if(!stricmp(pszToken, "<DIR>")) {
3742 lpfp->bIsDirectory = TRUE;
3743 lpfp->nSize = 0;
3744 TRACE("Is directory\n");
3746 else {
3747 lpfp->bIsDirectory = FALSE;
3748 lpfp->nSize = atol(pszToken);
3749 TRACE("Size: %ld\n", lpfp->nSize);
3752 pszToken = strtok(NULL, szSpace);
3753 if(!pszToken) continue;
3754 lpfp->lpszName = strdupAtoW(pszToken);
3755 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3757 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3758 else if(pszToken[0] == '+') {
3759 FIXME("EPLF Format not implemented\n");
3762 if(lpfp->lpszName) {
3763 if((lpszSearchFile == NULL) ||
3764 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3765 found = TRUE;
3766 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3768 else {
3769 free(lpfp->lpszName);
3770 lpfp->lpszName = NULL;
3773 } while(!found);
3774 return TRUE;
3777 /***********************************************************************
3778 * FTP_ParseDirectory (internal)
3780 * Parse string of directory information
3782 * RETURNS
3783 * TRUE on success
3784 * FALSE on failure
3786 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3787 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3789 BOOL bSuccess = TRUE;
3790 INT sizeFilePropArray = 500;/*20; */
3791 INT indexFilePropArray = -1;
3793 TRACE("\n");
3795 /* Allocate initial file properties array */
3796 *lpafp = calloc(sizeFilePropArray, sizeof(FILEPROPERTIESW));
3797 if (!*lpafp)
3798 return FALSE;
3800 do {
3801 if (indexFilePropArray+1 >= sizeFilePropArray)
3803 LPFILEPROPERTIESW tmpafp;
3805 tmpafp = realloc(*lpafp, sizeof(FILEPROPERTIESW) * sizeFilePropArray * 2);
3806 if (NULL == tmpafp)
3808 bSuccess = FALSE;
3809 break;
3811 memset(tmpafp + sizeFilePropArray, 0, sizeof(FILEPROPERTIESW) * sizeFilePropArray);
3813 *lpafp = tmpafp;
3814 sizeFilePropArray *= 2;
3816 indexFilePropArray++;
3817 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3819 if (bSuccess && indexFilePropArray)
3821 if (indexFilePropArray < sizeFilePropArray - 1)
3823 LPFILEPROPERTIESW tmpafp;
3825 tmpafp = realloc(*lpafp, sizeof(FILEPROPERTIESW) * indexFilePropArray);
3826 if (NULL != tmpafp)
3827 *lpafp = tmpafp;
3829 *dwfp = indexFilePropArray;
3831 else
3833 free(*lpafp);
3834 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3835 bSuccess = FALSE;
3838 return bSuccess;
3842 /***********************************************************************
3843 * FTP_ParsePermission (internal)
3845 * Parse permission string of directory information
3847 * RETURNS
3848 * TRUE on success
3849 * FALSE on failure
3852 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3854 BOOL bSuccess = TRUE;
3855 unsigned short nPermission = 0;
3856 INT nPos = 1;
3857 INT nLast = 9;
3859 TRACE("\n");
3860 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3862 bSuccess = FALSE;
3863 return bSuccess;
3866 lpfp->bIsDirectory = (*lpszPermission == 'd');
3869 switch (nPos)
3871 case 1:
3872 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3873 break;
3874 case 2:
3875 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3876 break;
3877 case 3:
3878 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3879 break;
3880 case 4:
3881 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3882 break;
3883 case 5:
3884 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3885 break;
3886 case 6:
3887 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3888 break;
3889 case 7:
3890 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3891 break;
3892 case 8:
3893 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3894 break;
3895 case 9:
3896 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3897 break;
3899 nPos++;
3900 }while (nPos <= nLast);
3902 lpfp->permissions = nPermission;
3903 return bSuccess;
3907 /***********************************************************************
3908 * FTP_SetResponseError (internal)
3910 * Set the appropriate error code for a given response from the server
3912 * RETURNS
3915 static DWORD FTP_SetResponseError(DWORD dwResponse)
3917 DWORD dwCode = 0;
3919 switch(dwResponse)
3921 case 425: /* Cannot open data connection. */
3922 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3923 break;
3925 case 426: /* Connection closed, transfer aborted. */
3926 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3927 break;
3929 case 530: /* Not logged in. Login incorrect. */
3930 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3931 break;
3933 case 421: /* Service not available - Server may be shutting down. */
3934 case 450: /* File action not taken. File may be busy. */
3935 case 451: /* Action aborted. Server error. */
3936 case 452: /* Action not taken. Insufficient storage space on server. */
3937 case 500: /* Syntax error. Command unrecognized. */
3938 case 501: /* Syntax error. Error in parameters or arguments. */
3939 case 502: /* Command not implemented. */
3940 case 503: /* Bad sequence of commands. */
3941 case 504: /* Command not implemented for that parameter. */
3942 case 532: /* Need account for storing files */
3943 case 550: /* File action not taken. File not found or no access. */
3944 case 551: /* Requested action aborted. Page type unknown */
3945 case 552: /* Action aborted. Exceeded storage allocation */
3946 case 553: /* Action not taken. File name not allowed. */
3948 default:
3949 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3950 break;
3953 INTERNET_SetLastError(dwCode);
3954 return dwCode;