oleaut32: Convert TLBImplType to be stored as an array.
[wine.git] / dlls / wininet / ftp.c
blobe0e0cde25f3fc0114cd1b13e479d2137b05c5b69
1 /*
2 * WININET - Ftp implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2004 Mike McCormack for CodeWeavers
6 * Copyright 2004 Kevin Koltzau
7 * Copyright 2007 Hans Leidekker
9 * Ulrich Czekalla
10 * Noureddine Jemmali
12 * Copyright 2000 Andreas Mohr
13 * Copyright 2002 Jaco Greeff
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "config.h"
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
34 #include <ws2tcpip.h>
35 #endif
37 #include <errno.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/types.h>
43 #ifdef HAVE_SYS_SOCKET_H
44 # include <sys/socket.h>
45 #endif
46 #ifdef HAVE_ARPA_INET_H
47 # include <arpa/inet.h>
48 #endif
49 #ifdef HAVE_UNISTD_H
50 # include <unistd.h>
51 #endif
52 #ifdef HAVE_SYS_IOCTL_H
53 # include <sys/ioctl.h>
54 #endif
55 #include <time.h>
56 #include <assert.h>
58 #include "windef.h"
59 #include "winbase.h"
60 #include "wingdi.h"
61 #include "winuser.h"
62 #include "wininet.h"
63 #include "winnls.h"
64 #include "winerror.h"
65 #include "winreg.h"
66 #include "winternl.h"
67 #include "shlwapi.h"
69 #include "wine/debug.h"
70 #include "internet.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
74 typedef struct _ftp_session_t ftp_session_t;
76 typedef struct
78 object_header_t hdr;
79 ftp_session_t *lpFtpSession;
80 BOOL session_deleted;
81 int nDataSocket;
82 WCHAR *cache_file;
83 HANDLE cache_file_handle;
84 } ftp_file_t;
86 struct _ftp_session_t
88 object_header_t hdr;
89 appinfo_t *lpAppInfo;
90 int sndSocket;
91 int lstnSocket;
92 int pasvSocket; /* data socket connected by us in case of passive FTP */
93 ftp_file_t *download_in_progress;
94 struct sockaddr_in socketAddress;
95 struct sockaddr_in lstnSocketAddress;
96 LPWSTR servername;
97 INTERNET_PORT serverport;
98 LPWSTR lpszPassword;
99 LPWSTR lpszUserName;
102 typedef struct
104 BOOL bIsDirectory;
105 LPWSTR lpszName;
106 DWORD nSize;
107 SYSTEMTIME tmLastModified;
108 unsigned short permissions;
109 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
111 typedef struct
113 object_header_t hdr;
114 ftp_session_t *lpFtpSession;
115 DWORD index;
116 DWORD size;
117 LPFILEPROPERTIESW lpafp;
118 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
120 #define DATA_PACKET_SIZE 0x2000
121 #define szCRLF "\r\n"
122 #define MAX_BACKLOG 5
124 /* Testing shows that Windows only accepts dwFlags where the last
125 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
127 #define FTP_CONDITION_MASK 0x0007
129 typedef enum {
130 /* FTP commands with arguments. */
131 FTP_CMD_ACCT,
132 FTP_CMD_CWD,
133 FTP_CMD_DELE,
134 FTP_CMD_MKD,
135 FTP_CMD_PASS,
136 FTP_CMD_PORT,
137 FTP_CMD_RETR,
138 FTP_CMD_RMD,
139 FTP_CMD_RNFR,
140 FTP_CMD_RNTO,
141 FTP_CMD_STOR,
142 FTP_CMD_TYPE,
143 FTP_CMD_USER,
144 FTP_CMD_SIZE,
146 /* FTP commands without arguments. */
147 FTP_CMD_ABOR,
148 FTP_CMD_LIST,
149 FTP_CMD_NLST,
150 FTP_CMD_PASV,
151 FTP_CMD_PWD,
152 FTP_CMD_QUIT,
153 } FTP_COMMAND;
155 static const CHAR *const szFtpCommands[] = {
156 "ACCT",
157 "CWD",
158 "DELE",
159 "MKD",
160 "PASS",
161 "PORT",
162 "RETR",
163 "RMD",
164 "RNFR",
165 "RNTO",
166 "STOR",
167 "TYPE",
168 "USER",
169 "SIZE",
170 "ABOR",
171 "LIST",
172 "NLST",
173 "PASV",
174 "PWD",
175 "QUIT",
178 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
179 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
181 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
182 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext);
183 static BOOL FTP_SendStore(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
184 static BOOL FTP_GetDataSocket(ftp_session_t*, LPINT nDataSocket);
185 static BOOL FTP_SendData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
186 static INT FTP_ReceiveResponse(ftp_session_t*, DWORD_PTR dwContext);
187 static BOOL FTP_SendRetrieve(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
188 static BOOL FTP_RetrieveFileData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
189 static BOOL FTP_InitListenSocket(ftp_session_t*);
190 static BOOL FTP_ConnectToHost(ftp_session_t*);
191 static BOOL FTP_SendPassword(ftp_session_t*);
192 static BOOL FTP_SendAccount(ftp_session_t*);
193 static BOOL FTP_SendType(ftp_session_t*, DWORD dwType);
194 static BOOL FTP_SendPort(ftp_session_t*);
195 static BOOL FTP_DoPassive(ftp_session_t*);
196 static BOOL FTP_SendPortOrPasv(ftp_session_t*);
197 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
198 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
199 static BOOL FTP_ParseDirectory(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
200 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
201 static HINTERNET FTP_ReceiveFileList(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
202 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
203 static DWORD FTP_SetResponseError(DWORD dwResponse);
204 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
205 static BOOL FTP_FtpPutFileW(ftp_session_t*, LPCWSTR lpszLocalFile,
206 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
207 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
208 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
209 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t*,
210 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
211 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t*, LPWSTR lpszCurrentDirectory,
212 LPDWORD lpdwCurrentDirectory);
213 static BOOL FTP_FtpRenameFileW(ftp_session_t*, LPCWSTR lpszSrc, LPCWSTR lpszDest);
214 static BOOL FTP_FtpRemoveDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
215 static BOOL FTP_FtpDeleteFileW(ftp_session_t*, LPCWSTR lpszFileName);
216 static BOOL FTP_FtpGetFileW(ftp_session_t*, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
217 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
218 DWORD_PTR dwContext);
220 /* A temporary helper until we get rid of INTERNET_GetLastError calls */
221 static BOOL res_to_le(DWORD res)
223 if(res != ERROR_SUCCESS)
224 INTERNET_SetLastError(res);
225 return res == ERROR_SUCCESS;
228 /***********************************************************************
229 * FtpPutFileA (WININET.@)
231 * Uploads a file to the FTP server
233 * RETURNS
234 * TRUE on success
235 * FALSE on failure
238 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
239 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
241 LPWSTR lpwzLocalFile;
242 LPWSTR lpwzNewRemoteFile;
243 BOOL ret;
245 lpwzLocalFile = heap_strdupAtoW(lpszLocalFile);
246 lpwzNewRemoteFile = heap_strdupAtoW(lpszNewRemoteFile);
247 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
248 dwFlags, dwContext);
249 HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
250 HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
251 return ret;
254 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
256 struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
257 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
259 TRACE("%p\n", lpwfs);
261 FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
262 req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
264 HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
265 HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
268 /***********************************************************************
269 * FtpPutFileW (WININET.@)
271 * Uploads a file to the FTP server
273 * RETURNS
274 * TRUE on success
275 * FALSE on failure
278 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
279 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
281 ftp_session_t *lpwfs;
282 appinfo_t *hIC = NULL;
283 BOOL r = FALSE;
285 if (!lpszLocalFile || !lpszNewRemoteFile)
287 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
288 return FALSE;
291 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
292 if (!lpwfs)
294 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
295 return FALSE;
298 if (WH_HFTPSESSION != lpwfs->hdr.htype)
300 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
301 goto lend;
304 if (lpwfs->download_in_progress != NULL)
306 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
307 goto lend;
310 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
312 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
313 goto lend;
316 hIC = lpwfs->lpAppInfo;
317 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
319 WORKREQUEST workRequest;
320 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
322 workRequest.asyncproc = AsyncFtpPutFileProc;
323 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
324 req->lpszLocalFile = heap_strdupW(lpszLocalFile);
325 req->lpszNewRemoteFile = heap_strdupW(lpszNewRemoteFile);
326 req->dwFlags = dwFlags;
327 req->dwContext = dwContext;
329 r = res_to_le(INTERNET_AsyncCall(&workRequest));
331 else
333 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
334 lpszNewRemoteFile, dwFlags, dwContext);
337 lend:
338 WININET_Release( &lpwfs->hdr );
340 return r;
343 /***********************************************************************
344 * FTP_FtpPutFileW (Internal)
346 * Uploads a file to the FTP server
348 * RETURNS
349 * TRUE on success
350 * FALSE on failure
353 static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
354 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
356 HANDLE hFile;
357 BOOL bSuccess = FALSE;
358 appinfo_t *hIC = NULL;
359 INT nResCode;
361 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
363 /* Clear any error information */
364 INTERNET_SetLastError(0);
366 /* Open file to be uploaded */
367 if (INVALID_HANDLE_VALUE ==
368 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
369 /* Let CreateFile set the appropriate error */
370 return FALSE;
372 hIC = lpwfs->lpAppInfo;
374 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
376 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
378 INT nDataSocket;
380 /* Get data socket to server */
381 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
383 FTP_SendData(lpwfs, nDataSocket, hFile);
384 closesocket(nDataSocket);
385 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
386 if (nResCode)
388 if (nResCode == 226)
389 bSuccess = TRUE;
390 else
391 FTP_SetResponseError(nResCode);
396 if (lpwfs->lstnSocket != -1)
398 closesocket(lpwfs->lstnSocket);
399 lpwfs->lstnSocket = -1;
402 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
404 INTERNET_ASYNC_RESULT iar;
406 iar.dwResult = (DWORD)bSuccess;
407 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
408 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
409 &iar, sizeof(INTERNET_ASYNC_RESULT));
412 CloseHandle(hFile);
414 return bSuccess;
418 /***********************************************************************
419 * FtpSetCurrentDirectoryA (WININET.@)
421 * Change the working directory on the FTP server
423 * RETURNS
424 * TRUE on success
425 * FALSE on failure
428 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
430 LPWSTR lpwzDirectory;
431 BOOL ret;
433 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
434 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
435 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
436 return ret;
440 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
442 struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
443 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
445 TRACE("%p\n", lpwfs);
447 FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
448 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
451 /***********************************************************************
452 * FtpSetCurrentDirectoryW (WININET.@)
454 * Change the working directory on the FTP server
456 * RETURNS
457 * TRUE on success
458 * FALSE on failure
461 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
463 ftp_session_t *lpwfs = NULL;
464 appinfo_t *hIC = NULL;
465 BOOL r = FALSE;
467 if (!lpszDirectory)
469 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
470 goto lend;
473 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
474 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
476 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
477 goto lend;
480 if (lpwfs->download_in_progress != NULL)
482 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
483 goto lend;
486 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
488 hIC = lpwfs->lpAppInfo;
489 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
491 WORKREQUEST workRequest;
492 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
494 workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
495 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
496 req = &workRequest.u.FtpSetCurrentDirectoryW;
497 req->lpszDirectory = heap_strdupW(lpszDirectory);
499 r = res_to_le(INTERNET_AsyncCall(&workRequest));
501 else
503 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
506 lend:
507 if( lpwfs )
508 WININET_Release( &lpwfs->hdr );
510 return r;
514 /***********************************************************************
515 * FTP_FtpSetCurrentDirectoryW (Internal)
517 * Change the working directory on the FTP server
519 * RETURNS
520 * TRUE on success
521 * FALSE on failure
524 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
526 INT nResCode;
527 appinfo_t *hIC = NULL;
528 DWORD bSuccess = FALSE;
530 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
532 /* Clear any error information */
533 INTERNET_SetLastError(0);
535 hIC = lpwfs->lpAppInfo;
536 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
537 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
538 goto lend;
540 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
542 if (nResCode)
544 if (nResCode == 250)
545 bSuccess = TRUE;
546 else
547 FTP_SetResponseError(nResCode);
550 lend:
551 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
553 INTERNET_ASYNC_RESULT iar;
555 iar.dwResult = bSuccess;
556 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
557 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
558 &iar, sizeof(INTERNET_ASYNC_RESULT));
560 return bSuccess;
564 /***********************************************************************
565 * FtpCreateDirectoryA (WININET.@)
567 * Create new directory on the FTP server
569 * RETURNS
570 * TRUE on success
571 * FALSE on failure
574 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
576 LPWSTR lpwzDirectory;
577 BOOL ret;
579 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
580 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
581 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
582 return ret;
586 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
588 struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
589 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
591 TRACE(" %p\n", lpwfs);
593 FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
594 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
597 /***********************************************************************
598 * FtpCreateDirectoryW (WININET.@)
600 * Create new directory on the FTP server
602 * RETURNS
603 * TRUE on success
604 * FALSE on failure
607 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
609 ftp_session_t *lpwfs;
610 appinfo_t *hIC = NULL;
611 BOOL r = FALSE;
613 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
614 if (!lpwfs)
616 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
617 return FALSE;
620 if (WH_HFTPSESSION != lpwfs->hdr.htype)
622 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
623 goto lend;
626 if (lpwfs->download_in_progress != NULL)
628 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
629 goto lend;
632 if (!lpszDirectory)
634 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
635 goto lend;
638 hIC = lpwfs->lpAppInfo;
639 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
641 WORKREQUEST workRequest;
642 struct WORKREQ_FTPCREATEDIRECTORYW *req;
644 workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
645 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
646 req = &workRequest.u.FtpCreateDirectoryW;
647 req->lpszDirectory = heap_strdupW(lpszDirectory);
649 r = res_to_le(INTERNET_AsyncCall(&workRequest));
651 else
653 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
655 lend:
656 WININET_Release( &lpwfs->hdr );
658 return r;
662 /***********************************************************************
663 * FTP_FtpCreateDirectoryW (Internal)
665 * Create new directory on the FTP server
667 * RETURNS
668 * TRUE on success
669 * FALSE on failure
672 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
674 INT nResCode;
675 BOOL bSuccess = FALSE;
676 appinfo_t *hIC = NULL;
678 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
680 /* Clear any error information */
681 INTERNET_SetLastError(0);
683 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
684 goto lend;
686 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
687 if (nResCode)
689 if (nResCode == 257)
690 bSuccess = TRUE;
691 else
692 FTP_SetResponseError(nResCode);
695 lend:
696 hIC = lpwfs->lpAppInfo;
697 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
699 INTERNET_ASYNC_RESULT iar;
701 iar.dwResult = (DWORD)bSuccess;
702 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
703 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
704 &iar, sizeof(INTERNET_ASYNC_RESULT));
707 return bSuccess;
710 /***********************************************************************
711 * FtpFindFirstFileA (WININET.@)
713 * Search the specified directory
715 * RETURNS
716 * HINTERNET on success
717 * NULL on failure
720 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
721 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
723 LPWSTR lpwzSearchFile;
724 WIN32_FIND_DATAW wfd;
725 LPWIN32_FIND_DATAW lpFindFileDataW;
726 HINTERNET ret;
728 lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
729 lpFindFileDataW = lpFindFileData?&wfd:NULL;
730 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
731 HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
733 if (ret && lpFindFileData)
734 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
736 return ret;
740 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
742 struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
743 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
745 TRACE("%p\n", lpwfs);
747 FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
748 req->lpFindFileData, req->dwFlags, req->dwContext);
749 HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
752 /***********************************************************************
753 * FtpFindFirstFileW (WININET.@)
755 * Search the specified directory
757 * RETURNS
758 * HINTERNET on success
759 * NULL on failure
762 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
763 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
765 ftp_session_t *lpwfs;
766 appinfo_t *hIC = NULL;
767 HINTERNET r = NULL;
769 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
770 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
772 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
773 goto lend;
776 if (lpwfs->download_in_progress != NULL)
778 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
779 goto lend;
782 hIC = lpwfs->lpAppInfo;
783 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
785 WORKREQUEST workRequest;
786 struct WORKREQ_FTPFINDFIRSTFILEW *req;
788 workRequest.asyncproc = AsyncFtpFindFirstFileProc;
789 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
790 req = &workRequest.u.FtpFindFirstFileW;
791 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : heap_strdupW(lpszSearchFile);
792 req->lpFindFileData = lpFindFileData;
793 req->dwFlags = dwFlags;
794 req->dwContext= dwContext;
796 INTERNET_AsyncCall(&workRequest);
797 r = NULL;
799 else
801 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
802 dwFlags, dwContext);
804 lend:
805 if( lpwfs )
806 WININET_Release( &lpwfs->hdr );
808 return r;
812 /***********************************************************************
813 * FTP_FtpFindFirstFileW (Internal)
815 * Search the specified directory
817 * RETURNS
818 * HINTERNET on success
819 * NULL on failure
822 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
823 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
825 INT nResCode;
826 appinfo_t *hIC = NULL;
827 HINTERNET hFindNext = NULL;
829 TRACE("\n");
831 /* Clear any error information */
832 INTERNET_SetLastError(0);
834 if (!FTP_InitListenSocket(lpwfs))
835 goto lend;
837 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
838 goto lend;
840 if (!FTP_SendPortOrPasv(lpwfs))
841 goto lend;
843 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
844 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
845 goto lend;
847 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
848 if (nResCode)
850 if (nResCode == 125 || nResCode == 150)
852 INT nDataSocket;
854 /* Get data socket to server */
855 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
857 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
858 closesocket(nDataSocket);
859 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
860 if (nResCode != 226 && nResCode != 250)
861 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
864 else
865 FTP_SetResponseError(nResCode);
868 lend:
869 if (lpwfs->lstnSocket != -1)
871 closesocket(lpwfs->lstnSocket);
872 lpwfs->lstnSocket = -1;
875 hIC = lpwfs->lpAppInfo;
876 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
878 INTERNET_ASYNC_RESULT iar;
880 if (hFindNext)
882 iar.dwResult = (DWORD_PTR)hFindNext;
883 iar.dwError = ERROR_SUCCESS;
884 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
885 &iar, sizeof(INTERNET_ASYNC_RESULT));
888 iar.dwResult = (DWORD_PTR)hFindNext;
889 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
890 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
891 &iar, sizeof(INTERNET_ASYNC_RESULT));
894 return hFindNext;
898 /***********************************************************************
899 * FtpGetCurrentDirectoryA (WININET.@)
901 * Retrieves the current directory
903 * RETURNS
904 * TRUE on success
905 * FALSE on failure
908 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
909 LPDWORD lpdwCurrentDirectory)
911 WCHAR *dir = NULL;
912 DWORD len;
913 BOOL ret;
915 if(lpdwCurrentDirectory) {
916 len = *lpdwCurrentDirectory;
917 if(lpszCurrentDirectory)
919 dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
920 if (NULL == dir)
922 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
923 return FALSE;
927 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
929 if (ret && lpszCurrentDirectory)
930 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
932 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
933 HeapFree(GetProcessHeap(), 0, dir);
934 return ret;
938 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
940 struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
941 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
943 TRACE("%p\n", lpwfs);
945 FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
948 /***********************************************************************
949 * FtpGetCurrentDirectoryW (WININET.@)
951 * Retrieves the current directory
953 * RETURNS
954 * TRUE on success
955 * FALSE on failure
958 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
959 LPDWORD lpdwCurrentDirectory)
961 ftp_session_t *lpwfs;
962 appinfo_t *hIC = NULL;
963 BOOL r = FALSE;
965 TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
967 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
968 if (NULL == lpwfs)
970 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
971 goto lend;
974 if (WH_HFTPSESSION != lpwfs->hdr.htype)
976 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
977 goto lend;
980 if (!lpdwCurrentDirectory)
982 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
983 goto lend;
986 if (lpszCurrentDirectory == NULL)
988 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
989 goto lend;
992 if (lpwfs->download_in_progress != NULL)
994 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
995 goto lend;
998 hIC = lpwfs->lpAppInfo;
999 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1001 WORKREQUEST workRequest;
1002 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
1004 workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
1005 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1006 req = &workRequest.u.FtpGetCurrentDirectoryW;
1007 req->lpszDirectory = lpszCurrentDirectory;
1008 req->lpdwDirectory = lpdwCurrentDirectory;
1010 r = res_to_le(INTERNET_AsyncCall(&workRequest));
1012 else
1014 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
1015 lpdwCurrentDirectory);
1018 lend:
1019 if( lpwfs )
1020 WININET_Release( &lpwfs->hdr );
1022 return r;
1026 /***********************************************************************
1027 * FTP_FtpGetCurrentDirectoryW (Internal)
1029 * Retrieves the current directory
1031 * RETURNS
1032 * TRUE on success
1033 * FALSE on failure
1036 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
1037 LPDWORD lpdwCurrentDirectory)
1039 INT nResCode;
1040 appinfo_t *hIC = NULL;
1041 DWORD bSuccess = FALSE;
1043 /* Clear any error information */
1044 INTERNET_SetLastError(0);
1046 hIC = lpwfs->lpAppInfo;
1047 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1048 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1049 goto lend;
1051 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1052 if (nResCode)
1054 if (nResCode == 257) /* Extract directory name */
1056 DWORD firstpos, lastpos, len;
1057 LPWSTR lpszResponseBuffer = heap_strdupAtoW(INTERNET_GetResponseBuffer());
1059 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1061 if ('"' == lpszResponseBuffer[lastpos])
1063 if (!firstpos)
1064 firstpos = lastpos;
1065 else
1066 break;
1069 len = lastpos - firstpos;
1070 if (*lpdwCurrentDirectory >= len)
1072 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1073 lpszCurrentDirectory[len - 1] = 0;
1074 *lpdwCurrentDirectory = len;
1075 bSuccess = TRUE;
1077 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1079 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
1081 else
1082 FTP_SetResponseError(nResCode);
1085 lend:
1086 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1088 INTERNET_ASYNC_RESULT iar;
1090 iar.dwResult = bSuccess;
1091 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1092 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1093 &iar, sizeof(INTERNET_ASYNC_RESULT));
1096 return bSuccess;
1100 /***********************************************************************
1101 * FTPFILE_Destroy(internal)
1103 * Closes the file transfer handle. This also 'cleans' the data queue of
1104 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1107 static void FTPFILE_Destroy(object_header_t *hdr)
1109 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1110 ftp_session_t *lpwfs = lpwh->lpFtpSession;
1111 INT nResCode;
1113 TRACE("\n");
1115 if (lpwh->cache_file_handle != INVALID_HANDLE_VALUE)
1116 CloseHandle(lpwh->cache_file_handle);
1118 HeapFree(GetProcessHeap(), 0, lpwh->cache_file);
1120 if (!lpwh->session_deleted)
1121 lpwfs->download_in_progress = NULL;
1123 if (lpwh->nDataSocket != -1)
1124 closesocket(lpwh->nDataSocket);
1126 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1127 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1129 WININET_Release(&lpwh->lpFtpSession->hdr);
1132 static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1134 switch(option) {
1135 case INTERNET_OPTION_HANDLE_TYPE:
1136 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1138 if (*size < sizeof(ULONG))
1139 return ERROR_INSUFFICIENT_BUFFER;
1141 *size = sizeof(DWORD);
1142 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1143 return ERROR_SUCCESS;
1144 case INTERNET_OPTION_DATAFILE_NAME:
1146 DWORD required;
1147 ftp_file_t *file = (ftp_file_t *)hdr;
1149 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1151 if (!file->cache_file)
1153 *size = 0;
1154 return ERROR_INTERNET_ITEM_NOT_FOUND;
1156 if (unicode)
1158 required = (lstrlenW(file->cache_file) + 1) * sizeof(WCHAR);
1159 if (*size < required)
1160 return ERROR_INSUFFICIENT_BUFFER;
1162 *size = required;
1163 memcpy(buffer, file->cache_file, *size);
1164 return ERROR_SUCCESS;
1166 else
1168 required = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, NULL, 0, NULL, NULL);
1169 if (required > *size)
1170 return ERROR_INSUFFICIENT_BUFFER;
1172 *size = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, buffer, *size, NULL, NULL);
1173 return ERROR_SUCCESS;
1177 return INET_QueryOption(hdr, option, buffer, size, unicode);
1180 static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
1182 ftp_file_t *file = (ftp_file_t*)hdr;
1183 int res;
1184 DWORD error;
1186 if (file->nDataSocket == -1)
1187 return ERROR_INTERNET_DISCONNECTED;
1189 /* FIXME: FTP should use NETCON_ stuff */
1190 res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1191 *read = res>0 ? res : 0;
1193 error = res >= 0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME */
1194 if (error == ERROR_SUCCESS && file->cache_file)
1196 DWORD bytes_written;
1198 if (!WriteFile(file->cache_file_handle, buffer, *read, &bytes_written, NULL))
1199 WARN("WriteFile failed: %u\n", GetLastError());
1201 return error;
1204 static DWORD FTPFILE_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
1205 DWORD flags, DWORD_PTR context)
1207 return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1210 static DWORD FTPFILE_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
1211 DWORD flags, DWORD_PTR context)
1213 return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1216 static DWORD FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1218 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1219 int res;
1221 res = send(lpwh->nDataSocket, buffer, size, 0);
1223 *written = res>0 ? res : 0;
1224 return res >= 0 ? ERROR_SUCCESS : sock_get_error(errno);
1227 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
1229 INTERNET_ASYNC_RESULT iar;
1230 BYTE buffer[4096];
1231 int available;
1233 TRACE("%p\n", file);
1235 available = recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1237 if(available != -1) {
1238 iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1239 iar.dwError = first_notif ? 0 : available;
1240 }else {
1241 iar.dwResult = 0;
1242 iar.dwError = INTERNET_GetLastError();
1245 INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1246 sizeof(INTERNET_ASYNC_RESULT));
1249 static void FTPFILE_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
1251 ftp_file_t *file = (ftp_file_t*)workRequest->hdr;
1253 FTP_ReceiveRequestData(file, FALSE);
1256 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1258 ftp_file_t *file = (ftp_file_t*) hdr;
1259 int retval, unread = 0;
1261 TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
1263 #ifdef FIONREAD
1264 retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
1265 if (!retval)
1266 TRACE("%d bytes of queued, but unread data\n", unread);
1267 #else
1268 FIXME("FIONREAD not available\n");
1269 #endif
1271 *available = unread;
1273 if(!unread) {
1274 BYTE byte;
1276 *available = 0;
1278 retval = recv(file->nDataSocket, &byte, 1, MSG_PEEK);
1279 if(retval > 0) {
1280 WORKREQUEST workRequest;
1282 *available = 0;
1283 workRequest.asyncproc = FTPFILE_AsyncQueryDataAvailableProc;
1284 workRequest.hdr = WININET_AddRef( &file->hdr );
1286 INTERNET_AsyncCall(&workRequest);
1288 return ERROR_IO_PENDING;
1292 return ERROR_SUCCESS;
1296 static const object_vtbl_t FTPFILEVtbl = {
1297 FTPFILE_Destroy,
1298 NULL,
1299 FTPFILE_QueryOption,
1300 NULL,
1301 FTPFILE_ReadFile,
1302 FTPFILE_ReadFileExA,
1303 FTPFILE_ReadFileExW,
1304 FTPFILE_WriteFile,
1305 FTPFILE_QueryDataAvailable,
1306 NULL
1309 /***********************************************************************
1310 * FTP_FtpOpenFileW (Internal)
1312 * Open a remote file for writing or reading
1314 * RETURNS
1315 * HINTERNET handle on success
1316 * NULL on failure
1319 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1320 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1321 DWORD_PTR dwContext)
1323 INT nDataSocket;
1324 BOOL bSuccess = FALSE;
1325 ftp_file_t *lpwh = NULL;
1326 appinfo_t *hIC = NULL;
1328 TRACE("\n");
1330 /* Clear any error information */
1331 INTERNET_SetLastError(0);
1333 if (GENERIC_READ == fdwAccess)
1335 /* Set up socket to retrieve data */
1336 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1338 else if (GENERIC_WRITE == fdwAccess)
1340 /* Set up socket to send data */
1341 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1344 /* Get data socket to server */
1345 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1347 lpwh = alloc_object(&lpwfs->hdr, &FTPFILEVtbl, sizeof(ftp_file_t));
1348 lpwh->hdr.htype = WH_HFILE;
1349 lpwh->hdr.dwFlags = dwFlags;
1350 lpwh->hdr.dwContext = dwContext;
1351 lpwh->nDataSocket = nDataSocket;
1352 lpwh->cache_file = NULL;
1353 lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1354 lpwh->session_deleted = FALSE;
1356 WININET_AddRef( &lpwfs->hdr );
1357 lpwh->lpFtpSession = lpwfs;
1358 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1360 /* Indicate that a download is currently in progress */
1361 lpwfs->download_in_progress = lpwh;
1364 if (lpwfs->lstnSocket != -1)
1366 closesocket(lpwfs->lstnSocket);
1367 lpwfs->lstnSocket = -1;
1370 if (bSuccess && fdwAccess == GENERIC_READ)
1372 WCHAR filename[MAX_PATH + 1];
1373 URL_COMPONENTSW uc;
1374 DWORD len;
1376 memset(&uc, 0, sizeof(uc));
1377 uc.dwStructSize = sizeof(uc);
1378 uc.nScheme = INTERNET_SCHEME_FTP;
1379 uc.lpszHostName = lpwfs->servername;
1380 uc.nPort = lpwfs->serverport;
1381 uc.lpszUserName = lpwfs->lpszUserName;
1382 uc.lpszUrlPath = heap_strdupW(lpszFileName);
1384 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1386 WCHAR *url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1388 if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1390 lpwh->cache_file = heap_strdupW(filename);
1391 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1392 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1393 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1395 WARN("Could not create cache file: %u\n", GetLastError());
1396 HeapFree(GetProcessHeap(), 0, lpwh->cache_file);
1397 lpwh->cache_file = NULL;
1400 HeapFree(GetProcessHeap(), 0, url);
1402 HeapFree(GetProcessHeap(), 0, uc.lpszUrlPath);
1405 hIC = lpwfs->lpAppInfo;
1406 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1408 INTERNET_ASYNC_RESULT iar;
1410 if (lpwh)
1412 iar.dwResult = (DWORD_PTR)lpwh->hdr.hInternet;
1413 iar.dwError = ERROR_SUCCESS;
1414 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1415 &iar, sizeof(INTERNET_ASYNC_RESULT));
1418 if(bSuccess) {
1419 FTP_ReceiveRequestData(lpwh, TRUE);
1420 }else {
1421 iar.dwResult = 0;
1422 iar.dwError = INTERNET_GetLastError();
1423 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1424 &iar, sizeof(INTERNET_ASYNC_RESULT));
1428 if(!bSuccess) {
1429 if(lpwh)
1430 WININET_Release( &lpwh->hdr );
1431 return FALSE;
1434 return lpwh->hdr.hInternet;
1438 /***********************************************************************
1439 * FtpOpenFileA (WININET.@)
1441 * Open a remote file for writing or reading
1443 * RETURNS
1444 * HINTERNET handle on success
1445 * NULL on failure
1448 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1449 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1450 DWORD_PTR dwContext)
1452 LPWSTR lpwzFileName;
1453 HINTERNET ret;
1455 lpwzFileName = heap_strdupAtoW(lpszFileName);
1456 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1457 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1458 return ret;
1462 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1464 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1465 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1467 TRACE("%p\n", lpwfs);
1469 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1470 req->dwAccess, req->dwFlags, req->dwContext);
1471 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1474 /***********************************************************************
1475 * FtpOpenFileW (WININET.@)
1477 * Open a remote file for writing or reading
1479 * RETURNS
1480 * HINTERNET handle on success
1481 * NULL on failure
1484 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1485 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1486 DWORD_PTR dwContext)
1488 ftp_session_t *lpwfs;
1489 appinfo_t *hIC = NULL;
1490 HINTERNET r = NULL;
1492 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1493 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1495 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1496 if (!lpwfs)
1498 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1499 return FALSE;
1502 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1504 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1505 goto lend;
1508 if ((!lpszFileName) ||
1509 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1510 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1512 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1513 goto lend;
1516 if (lpwfs->download_in_progress != NULL)
1518 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1519 goto lend;
1522 hIC = lpwfs->lpAppInfo;
1523 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1525 WORKREQUEST workRequest;
1526 struct WORKREQ_FTPOPENFILEW *req;
1528 workRequest.asyncproc = AsyncFtpOpenFileProc;
1529 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1530 req = &workRequest.u.FtpOpenFileW;
1531 req->lpszFilename = heap_strdupW(lpszFileName);
1532 req->dwAccess = fdwAccess;
1533 req->dwFlags = dwFlags;
1534 req->dwContext = dwContext;
1536 INTERNET_AsyncCall(&workRequest);
1537 r = NULL;
1539 else
1541 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1544 lend:
1545 WININET_Release( &lpwfs->hdr );
1547 return r;
1551 /***********************************************************************
1552 * FtpGetFileA (WININET.@)
1554 * Retrieve file from the FTP server
1556 * RETURNS
1557 * TRUE on success
1558 * FALSE on failure
1561 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1562 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1563 DWORD_PTR dwContext)
1565 LPWSTR lpwzRemoteFile;
1566 LPWSTR lpwzNewFile;
1567 BOOL ret;
1569 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1570 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1571 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1572 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1573 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1574 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1575 return ret;
1579 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1581 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1582 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1584 TRACE("%p\n", lpwfs);
1586 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1587 req->lpszNewFile, req->fFailIfExists,
1588 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1589 HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1590 HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1594 /***********************************************************************
1595 * FtpGetFileW (WININET.@)
1597 * Retrieve file from the FTP server
1599 * RETURNS
1600 * TRUE on success
1601 * FALSE on failure
1604 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1605 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1606 DWORD_PTR dwContext)
1608 ftp_session_t *lpwfs;
1609 appinfo_t *hIC = NULL;
1610 BOOL r = FALSE;
1612 if (!lpszRemoteFile || !lpszNewFile)
1614 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1615 return FALSE;
1618 lpwfs = (ftp_session_t*) get_handle_object( hInternet );
1619 if (!lpwfs)
1621 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1622 return FALSE;
1625 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1627 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1628 goto lend;
1631 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1633 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1634 goto lend;
1637 if (lpwfs->download_in_progress != NULL)
1639 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1640 goto lend;
1643 hIC = lpwfs->lpAppInfo;
1644 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1646 WORKREQUEST workRequest;
1647 struct WORKREQ_FTPGETFILEW *req;
1649 workRequest.asyncproc = AsyncFtpGetFileProc;
1650 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1651 req = &workRequest.u.FtpGetFileW;
1652 req->lpszRemoteFile = heap_strdupW(lpszRemoteFile);
1653 req->lpszNewFile = heap_strdupW(lpszNewFile);
1654 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1655 req->fFailIfExists = fFailIfExists;
1656 req->dwFlags = dwInternetFlags;
1657 req->dwContext = dwContext;
1659 r = res_to_le(INTERNET_AsyncCall(&workRequest));
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 SendAsyncCallback(&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 = heap_strdupAtoW(lpszFileName);
1780 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1781 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1782 return ret;
1785 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1787 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1788 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1790 TRACE("%p\n", lpwfs);
1792 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1793 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1796 /***********************************************************************
1797 * FtpDeleteFileW (WININET.@)
1799 * Delete a file on the ftp server
1801 * RETURNS
1802 * TRUE on success
1803 * FALSE on failure
1806 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1808 ftp_session_t *lpwfs;
1809 appinfo_t *hIC = NULL;
1810 BOOL r = FALSE;
1812 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1813 if (!lpwfs)
1815 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1816 return FALSE;
1819 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1821 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1822 goto lend;
1825 if (lpwfs->download_in_progress != NULL)
1827 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1828 goto lend;
1831 if (!lpszFileName)
1833 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1834 goto lend;
1837 hIC = lpwfs->lpAppInfo;
1838 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1840 WORKREQUEST workRequest;
1841 struct WORKREQ_FTPDELETEFILEW *req;
1843 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1844 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1845 req = &workRequest.u.FtpDeleteFileW;
1846 req->lpszFilename = heap_strdupW(lpszFileName);
1848 r = res_to_le(INTERNET_AsyncCall(&workRequest));
1850 else
1852 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1855 lend:
1856 WININET_Release( &lpwfs->hdr );
1858 return r;
1861 /***********************************************************************
1862 * FTP_FtpDeleteFileW (Internal)
1864 * Delete a file on the ftp server
1866 * RETURNS
1867 * TRUE on success
1868 * FALSE on failure
1871 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1873 INT nResCode;
1874 BOOL bSuccess = FALSE;
1875 appinfo_t *hIC = NULL;
1877 TRACE("%p\n", lpwfs);
1879 /* Clear any error information */
1880 INTERNET_SetLastError(0);
1882 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1883 goto lend;
1885 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1886 if (nResCode)
1888 if (nResCode == 250)
1889 bSuccess = TRUE;
1890 else
1891 FTP_SetResponseError(nResCode);
1893 lend:
1894 hIC = lpwfs->lpAppInfo;
1895 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1897 INTERNET_ASYNC_RESULT iar;
1899 iar.dwResult = (DWORD)bSuccess;
1900 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1901 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1902 &iar, sizeof(INTERNET_ASYNC_RESULT));
1905 return bSuccess;
1909 /***********************************************************************
1910 * FtpRemoveDirectoryA (WININET.@)
1912 * Remove a directory on the ftp server
1914 * RETURNS
1915 * TRUE on success
1916 * FALSE on failure
1919 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1921 LPWSTR lpwzDirectory;
1922 BOOL ret;
1924 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1925 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1926 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1927 return ret;
1930 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1932 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1933 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1935 TRACE("%p\n", lpwfs);
1937 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1938 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1941 /***********************************************************************
1942 * FtpRemoveDirectoryW (WININET.@)
1944 * Remove a directory on the ftp server
1946 * RETURNS
1947 * TRUE on success
1948 * FALSE on failure
1951 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1953 ftp_session_t *lpwfs;
1954 appinfo_t *hIC = NULL;
1955 BOOL r = FALSE;
1957 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1958 if (!lpwfs)
1960 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1961 return FALSE;
1964 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1966 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1967 goto lend;
1970 if (lpwfs->download_in_progress != NULL)
1972 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1973 goto lend;
1976 if (!lpszDirectory)
1978 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1979 goto lend;
1982 hIC = lpwfs->lpAppInfo;
1983 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1985 WORKREQUEST workRequest;
1986 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1988 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1989 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1990 req = &workRequest.u.FtpRemoveDirectoryW;
1991 req->lpszDirectory = heap_strdupW(lpszDirectory);
1993 r = res_to_le(INTERNET_AsyncCall(&workRequest));
1995 else
1997 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
2000 lend:
2001 WININET_Release( &lpwfs->hdr );
2003 return r;
2006 /***********************************************************************
2007 * FTP_FtpRemoveDirectoryW (Internal)
2009 * Remove a directory on the ftp server
2011 * RETURNS
2012 * TRUE on success
2013 * FALSE on failure
2016 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
2018 INT nResCode;
2019 BOOL bSuccess = FALSE;
2020 appinfo_t *hIC = NULL;
2022 TRACE("\n");
2024 /* Clear any error information */
2025 INTERNET_SetLastError(0);
2027 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2028 goto lend;
2030 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2031 if (nResCode)
2033 if (nResCode == 250)
2034 bSuccess = TRUE;
2035 else
2036 FTP_SetResponseError(nResCode);
2039 lend:
2040 hIC = lpwfs->lpAppInfo;
2041 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2043 INTERNET_ASYNC_RESULT iar;
2045 iar.dwResult = (DWORD)bSuccess;
2046 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2047 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2048 &iar, sizeof(INTERNET_ASYNC_RESULT));
2051 return bSuccess;
2055 /***********************************************************************
2056 * FtpRenameFileA (WININET.@)
2058 * Rename a file on the ftp server
2060 * RETURNS
2061 * TRUE on success
2062 * FALSE on failure
2065 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2067 LPWSTR lpwzSrc;
2068 LPWSTR lpwzDest;
2069 BOOL ret;
2071 lpwzSrc = heap_strdupAtoW(lpszSrc);
2072 lpwzDest = heap_strdupAtoW(lpszDest);
2073 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2074 HeapFree(GetProcessHeap(), 0, lpwzSrc);
2075 HeapFree(GetProcessHeap(), 0, lpwzDest);
2076 return ret;
2079 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
2081 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
2082 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
2084 TRACE("%p\n", lpwfs);
2086 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
2087 HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
2088 HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
2091 /***********************************************************************
2092 * FtpRenameFileW (WININET.@)
2094 * Rename a file on the ftp server
2096 * RETURNS
2097 * TRUE on success
2098 * FALSE on failure
2101 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2103 ftp_session_t *lpwfs;
2104 appinfo_t *hIC = NULL;
2105 BOOL r = FALSE;
2107 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
2108 if (!lpwfs)
2110 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2111 return FALSE;
2114 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2116 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2117 goto lend;
2120 if (lpwfs->download_in_progress != NULL)
2122 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2123 goto lend;
2126 if (!lpszSrc || !lpszDest)
2128 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2129 goto lend;
2132 hIC = lpwfs->lpAppInfo;
2133 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2135 WORKREQUEST workRequest;
2136 struct WORKREQ_FTPRENAMEFILEW *req;
2138 workRequest.asyncproc = AsyncFtpRenameFileProc;
2139 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
2140 req = &workRequest.u.FtpRenameFileW;
2141 req->lpszSrcFile = heap_strdupW(lpszSrc);
2142 req->lpszDestFile = heap_strdupW(lpszDest);
2144 r = res_to_le(INTERNET_AsyncCall(&workRequest));
2146 else
2148 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2151 lend:
2152 WININET_Release( &lpwfs->hdr );
2154 return r;
2157 /***********************************************************************
2158 * FTP_FtpRenameFileW (Internal)
2160 * Rename a file on the ftp server
2162 * RETURNS
2163 * TRUE on success
2164 * FALSE on failure
2167 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2169 INT nResCode;
2170 BOOL bSuccess = FALSE;
2171 appinfo_t *hIC = NULL;
2173 TRACE("\n");
2175 /* Clear any error information */
2176 INTERNET_SetLastError(0);
2178 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2179 goto lend;
2181 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2182 if (nResCode == 350)
2184 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2185 goto lend;
2187 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2190 if (nResCode == 250)
2191 bSuccess = TRUE;
2192 else
2193 FTP_SetResponseError(nResCode);
2195 lend:
2196 hIC = lpwfs->lpAppInfo;
2197 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2199 INTERNET_ASYNC_RESULT iar;
2201 iar.dwResult = (DWORD)bSuccess;
2202 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2203 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2204 &iar, sizeof(INTERNET_ASYNC_RESULT));
2207 return bSuccess;
2210 /***********************************************************************
2211 * FtpCommandA (WININET.@)
2213 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2214 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2216 BOOL r;
2217 WCHAR *cmdW;
2219 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2220 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2222 if (fExpectResponse)
2224 FIXME("data connection not supported\n");
2225 return FALSE;
2228 if (!lpszCommand || !lpszCommand[0])
2230 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2231 return FALSE;
2234 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2236 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2237 return FALSE;
2240 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2242 HeapFree(GetProcessHeap(), 0, cmdW);
2243 return r;
2246 /***********************************************************************
2247 * FtpCommandW (WININET.@)
2249 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2250 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2252 BOOL r = FALSE;
2253 ftp_session_t *lpwfs;
2254 LPSTR cmd = NULL;
2255 DWORD len, nBytesSent= 0;
2256 INT nResCode, nRC = 0;
2258 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2259 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2261 if (!lpszCommand || !lpszCommand[0])
2263 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2264 return FALSE;
2267 if (fExpectResponse)
2269 FIXME("data connection not supported\n");
2270 return FALSE;
2273 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
2274 if (!lpwfs)
2276 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2277 return FALSE;
2280 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2282 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2283 goto lend;
2286 if (lpwfs->download_in_progress != NULL)
2288 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2289 goto lend;
2292 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2293 if ((cmd = HeapAlloc(GetProcessHeap(), 0, len )))
2294 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2295 else
2297 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2298 goto lend;
2301 strcat(cmd, szCRLF);
2302 len--;
2304 TRACE("Sending (%s) len(%d)\n", cmd, len);
2305 while ((nBytesSent < len) && (nRC != -1))
2307 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2308 if (nRC != -1)
2310 nBytesSent += nRC;
2311 TRACE("Sent %d bytes\n", nRC);
2315 if (nBytesSent)
2317 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2318 if (nResCode > 0 && nResCode < 400)
2319 r = TRUE;
2320 else
2321 FTP_SetResponseError(nResCode);
2324 lend:
2325 WININET_Release( &lpwfs->hdr );
2326 HeapFree(GetProcessHeap(), 0, cmd);
2327 return r;
2331 /***********************************************************************
2332 * FTPSESSION_Destroy (internal)
2334 * Deallocate session handle
2336 static void FTPSESSION_Destroy(object_header_t *hdr)
2338 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2340 TRACE("\n");
2342 WININET_Release(&lpwfs->lpAppInfo->hdr);
2344 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2345 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2346 HeapFree(GetProcessHeap(), 0, lpwfs->servername);
2349 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2351 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2353 TRACE("\n");
2355 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2356 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2358 if (lpwfs->download_in_progress != NULL)
2359 lpwfs->download_in_progress->session_deleted = TRUE;
2361 if (lpwfs->sndSocket != -1)
2362 closesocket(lpwfs->sndSocket);
2364 if (lpwfs->lstnSocket != -1)
2365 closesocket(lpwfs->lstnSocket);
2367 if (lpwfs->pasvSocket != -1)
2368 closesocket(lpwfs->pasvSocket);
2370 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2371 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2374 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2376 switch(option) {
2377 case INTERNET_OPTION_HANDLE_TYPE:
2378 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2380 if (*size < sizeof(ULONG))
2381 return ERROR_INSUFFICIENT_BUFFER;
2383 *size = sizeof(DWORD);
2384 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2385 return ERROR_SUCCESS;
2388 return INET_QueryOption(hdr, option, buffer, size, unicode);
2391 static const object_vtbl_t FTPSESSIONVtbl = {
2392 FTPSESSION_Destroy,
2393 FTPSESSION_CloseConnection,
2394 FTPSESSION_QueryOption,
2395 NULL,
2396 NULL,
2397 NULL,
2398 NULL,
2399 NULL,
2400 NULL
2404 /***********************************************************************
2405 * FTP_Connect (internal)
2407 * Connect to a ftp server
2409 * RETURNS
2410 * HINTERNET a session handle on success
2411 * NULL on failure
2413 * NOTES:
2415 * Windows uses 'anonymous' as the username, when given a NULL username
2416 * and a NULL password. The password is first looked up in:
2418 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2420 * If this entry is not present it uses the current username as the password.
2424 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2425 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2426 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2427 DWORD dwInternalFlags)
2429 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2430 'M','i','c','r','o','s','o','f','t','\\',
2431 'W','i','n','d','o','w','s','\\',
2432 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2433 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2434 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2435 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2436 static const WCHAR szEmpty[] = {'\0'};
2437 struct sockaddr_in socketAddr;
2438 INT nsocket = -1;
2439 UINT sock_namelen;
2440 BOOL bSuccess = FALSE;
2441 ftp_session_t *lpwfs = NULL;
2442 char szaddr[INET_ADDRSTRLEN];
2444 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2445 hIC, debugstr_w(lpszServerName),
2446 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2448 assert( hIC->hdr.htype == WH_HINIT );
2450 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2452 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2453 return NULL;
2456 lpwfs = alloc_object(&hIC->hdr, &FTPSESSIONVtbl, sizeof(ftp_session_t));
2457 if (NULL == lpwfs)
2459 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2460 return NULL;
2463 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2464 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2465 else
2466 lpwfs->serverport = nServerPort;
2468 lpwfs->hdr.htype = WH_HFTPSESSION;
2469 lpwfs->hdr.dwFlags = dwFlags;
2470 lpwfs->hdr.dwContext = dwContext;
2471 lpwfs->hdr.dwInternalFlags |= dwInternalFlags;
2472 lpwfs->download_in_progress = NULL;
2473 lpwfs->sndSocket = -1;
2474 lpwfs->lstnSocket = -1;
2475 lpwfs->pasvSocket = -1;
2477 WININET_AddRef( &hIC->hdr );
2478 lpwfs->lpAppInfo = hIC;
2479 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2481 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
2482 if(strchrW(hIC->proxy, ' '))
2483 FIXME("Several proxies not implemented.\n");
2484 if(hIC->proxyBypass)
2485 FIXME("Proxy bypass is ignored.\n");
2487 if (!lpszUserName || !strlenW(lpszUserName)) {
2488 HKEY key;
2489 WCHAR szPassword[MAX_PATH];
2490 DWORD len = sizeof(szPassword);
2492 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2494 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2495 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2496 /* Nothing in the registry, get the username and use that as the password */
2497 if (!GetUserNameW(szPassword, &len)) {
2498 /* Should never get here, but use an empty password as failsafe */
2499 strcpyW(szPassword, szEmpty);
2502 RegCloseKey(key);
2504 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2505 lpwfs->lpszPassword = heap_strdupW(szPassword);
2507 else {
2508 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2509 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2511 lpwfs->servername = heap_strdupW(lpszServerName);
2513 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2514 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2516 INTERNET_ASYNC_RESULT iar;
2518 iar.dwResult = (DWORD_PTR)lpwfs->hdr.hInternet;
2519 iar.dwError = ERROR_SUCCESS;
2521 SendAsyncCallback(&hIC->hdr, dwContext,
2522 INTERNET_STATUS_HANDLE_CREATED, &iar,
2523 sizeof(INTERNET_ASYNC_RESULT));
2526 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2527 (LPWSTR) lpszServerName, (strlenW(lpszServerName)+1) * sizeof(WCHAR));
2529 sock_namelen = sizeof(socketAddr);
2530 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen))
2532 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2533 goto lerror;
2536 if (socketAddr.sin_family != AF_INET)
2538 WARN("unsupported address family %d\n", socketAddr.sin_family);
2539 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2540 goto lerror;
2543 inet_ntop(socketAddr.sin_family, &socketAddr.sin_addr, szaddr, sizeof(szaddr));
2544 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2545 szaddr, strlen(szaddr)+1);
2547 nsocket = socket(AF_INET,SOCK_STREAM,0);
2548 if (nsocket == -1)
2550 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2551 goto lerror;
2554 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2555 szaddr, strlen(szaddr)+1);
2557 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2559 ERR("Unable to connect (%s)\n", strerror(errno));
2560 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2561 closesocket(nsocket);
2563 else
2565 TRACE("Connected to server\n");
2566 lpwfs->sndSocket = nsocket;
2567 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2568 szaddr, strlen(szaddr)+1);
2570 sock_namelen = sizeof(lpwfs->socketAddress);
2571 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2573 if (FTP_ConnectToHost(lpwfs))
2575 TRACE("Successfully logged into server\n");
2576 bSuccess = TRUE;
2580 lerror:
2581 if (!bSuccess)
2583 if(lpwfs)
2584 WININET_Release( &lpwfs->hdr );
2585 return NULL;
2588 return lpwfs->hdr.hInternet;
2592 /***********************************************************************
2593 * FTP_ConnectToHost (internal)
2595 * Connect to a ftp server
2597 * RETURNS
2598 * TRUE on success
2599 * NULL on failure
2602 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2604 INT nResCode;
2605 BOOL bSuccess = FALSE;
2607 TRACE("\n");
2608 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2610 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2611 goto lend;
2613 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2614 if (nResCode)
2616 /* Login successful... */
2617 if (nResCode == 230)
2618 bSuccess = TRUE;
2619 /* User name okay, need password... */
2620 else if (nResCode == 331)
2621 bSuccess = FTP_SendPassword(lpwfs);
2622 /* Need account for login... */
2623 else if (nResCode == 332)
2624 bSuccess = FTP_SendAccount(lpwfs);
2625 else
2626 FTP_SetResponseError(nResCode);
2629 TRACE("Returning %d\n", bSuccess);
2630 lend:
2631 return bSuccess;
2635 /***********************************************************************
2636 * FTP_SendCommandA (internal)
2638 * Send command to server
2640 * RETURNS
2641 * TRUE on success
2642 * NULL on failure
2645 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2646 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2648 DWORD len;
2649 CHAR *buf;
2650 DWORD nBytesSent = 0;
2651 int nRC = 0;
2652 DWORD dwParamLen;
2654 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2656 if (lpfnStatusCB)
2658 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2661 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2662 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2663 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2665 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2666 return FALSE;
2668 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2669 dwParamLen ? lpszParam : "", szCRLF);
2671 TRACE("Sending (%s) len(%d)\n", buf, len);
2672 while((nBytesSent < len) && (nRC != -1))
2674 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2675 nBytesSent += nRC;
2678 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2680 if (lpfnStatusCB)
2682 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2683 &nBytesSent, sizeof(DWORD));
2686 TRACE("Sent %d bytes\n", nBytesSent);
2687 return (nRC != -1);
2690 /***********************************************************************
2691 * FTP_SendCommand (internal)
2693 * Send command to server
2695 * RETURNS
2696 * TRUE on success
2697 * NULL on failure
2700 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2701 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2703 BOOL ret;
2704 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2705 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2706 HeapFree(GetProcessHeap(), 0, lpszParamA);
2707 return ret;
2710 /***********************************************************************
2711 * FTP_ReceiveResponse (internal)
2713 * Receive response from server
2715 * RETURNS
2716 * Reply code on success
2717 * 0 on failure
2720 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2722 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2723 DWORD nRecv;
2724 INT rc = 0;
2725 char firstprefix[5];
2726 BOOL multiline = FALSE;
2728 TRACE("socket(%d)\n", lpwfs->sndSocket);
2730 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2732 while(1)
2734 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2735 goto lerror;
2737 if (nRecv >= 3)
2739 if(!multiline)
2741 if(lpszResponse[3] != '-')
2742 break;
2743 else
2744 { /* Start of multiline response. Loop until we get "nnn " */
2745 multiline = TRUE;
2746 memcpy(firstprefix, lpszResponse, 3);
2747 firstprefix[3] = ' ';
2748 firstprefix[4] = '\0';
2751 else
2753 if(!memcmp(firstprefix, lpszResponse, 4))
2754 break;
2759 if (nRecv >= 3)
2761 rc = atoi(lpszResponse);
2763 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2764 &nRecv, sizeof(DWORD));
2767 lerror:
2768 TRACE("return %d\n", rc);
2769 return rc;
2773 /***********************************************************************
2774 * FTP_SendPassword (internal)
2776 * Send password to ftp server
2778 * RETURNS
2779 * TRUE on success
2780 * NULL on failure
2783 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2785 INT nResCode;
2786 BOOL bSuccess = FALSE;
2788 TRACE("\n");
2789 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2790 goto lend;
2792 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2793 if (nResCode)
2795 TRACE("Received reply code %d\n", nResCode);
2796 /* Login successful... */
2797 if (nResCode == 230)
2798 bSuccess = TRUE;
2799 /* Command not implemented, superfluous at the server site... */
2800 /* Need account for login... */
2801 else if (nResCode == 332)
2802 bSuccess = FTP_SendAccount(lpwfs);
2803 else
2804 FTP_SetResponseError(nResCode);
2807 lend:
2808 TRACE("Returning %d\n", bSuccess);
2809 return bSuccess;
2813 /***********************************************************************
2814 * FTP_SendAccount (internal)
2818 * RETURNS
2819 * TRUE on success
2820 * FALSE on failure
2823 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2825 INT nResCode;
2826 BOOL bSuccess = FALSE;
2828 TRACE("\n");
2829 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2830 goto lend;
2832 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2833 if (nResCode)
2834 bSuccess = TRUE;
2835 else
2836 FTP_SetResponseError(nResCode);
2838 lend:
2839 return bSuccess;
2843 /***********************************************************************
2844 * FTP_SendStore (internal)
2846 * Send request to upload file to ftp server
2848 * RETURNS
2849 * TRUE on success
2850 * FALSE on failure
2853 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2855 INT nResCode;
2856 BOOL bSuccess = FALSE;
2858 TRACE("\n");
2859 if (!FTP_InitListenSocket(lpwfs))
2860 goto lend;
2862 if (!FTP_SendType(lpwfs, dwType))
2863 goto lend;
2865 if (!FTP_SendPortOrPasv(lpwfs))
2866 goto lend;
2868 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2869 goto lend;
2870 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2871 if (nResCode)
2873 if (nResCode == 150 || nResCode == 125)
2874 bSuccess = TRUE;
2875 else
2876 FTP_SetResponseError(nResCode);
2879 lend:
2880 if (!bSuccess && lpwfs->lstnSocket != -1)
2882 closesocket(lpwfs->lstnSocket);
2883 lpwfs->lstnSocket = -1;
2886 return bSuccess;
2890 /***********************************************************************
2891 * FTP_InitListenSocket (internal)
2893 * Create a socket to listen for server response
2895 * RETURNS
2896 * TRUE on success
2897 * FALSE on failure
2900 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2902 BOOL bSuccess = FALSE;
2903 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2905 TRACE("\n");
2907 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2908 if (lpwfs->lstnSocket == -1)
2910 TRACE("Unable to create listening socket\n");
2911 goto lend;
2914 /* We obtain our ip addr from the name of the command channel socket */
2915 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2917 /* and get the system to assign us a port */
2918 lpwfs->lstnSocketAddress.sin_port = htons(0);
2920 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2922 TRACE("Unable to bind socket\n");
2923 goto lend;
2926 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2928 TRACE("listen failed\n");
2929 goto lend;
2932 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2933 bSuccess = TRUE;
2935 lend:
2936 if (!bSuccess && lpwfs->lstnSocket != -1)
2938 closesocket(lpwfs->lstnSocket);
2939 lpwfs->lstnSocket = -1;
2942 return bSuccess;
2946 /***********************************************************************
2947 * FTP_SendType (internal)
2949 * Tell server type of data being transferred
2951 * RETURNS
2952 * TRUE on success
2953 * FALSE on failure
2955 * W98SE doesn't cache the type that's currently set
2956 * (i.e. it sends it always),
2957 * so we probably don't want to do that either.
2959 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2961 INT nResCode;
2962 WCHAR type[] = { 'I','\0' };
2963 BOOL bSuccess = FALSE;
2965 TRACE("\n");
2966 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2967 type[0] = 'A';
2969 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2970 goto lend;
2972 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2973 if (nResCode)
2975 if (nResCode == 2)
2976 bSuccess = TRUE;
2977 else
2978 FTP_SetResponseError(nResCode);
2981 lend:
2982 return bSuccess;
2986 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2987 /***********************************************************************
2988 * FTP_GetFileSize (internal)
2990 * Retrieves from the server the size of the given file
2992 * RETURNS
2993 * TRUE on success
2994 * FALSE on failure
2997 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2999 INT nResCode;
3000 BOOL bSuccess = FALSE;
3002 TRACE("\n");
3004 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
3005 goto lend;
3007 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3008 if (nResCode)
3010 if (nResCode == 213) {
3011 /* Now parses the output to get the actual file size */
3012 int i;
3013 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3015 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3016 if (lpszResponseBuffer[i] == '\0') return FALSE;
3017 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3019 bSuccess = TRUE;
3020 } else {
3021 FTP_SetResponseError(nResCode);
3025 lend:
3026 return bSuccess;
3028 #endif
3031 /***********************************************************************
3032 * FTP_SendPort (internal)
3034 * Tell server which port to use
3036 * RETURNS
3037 * TRUE on success
3038 * FALSE on failure
3041 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3043 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3044 INT nResCode;
3045 WCHAR szIPAddress[64];
3046 BOOL bSuccess = FALSE;
3047 TRACE("\n");
3049 sprintfW(szIPAddress, szIPFormat,
3050 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
3051 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
3052 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
3053 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
3054 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3055 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3057 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3058 goto lend;
3060 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3061 if (nResCode)
3063 if (nResCode == 200)
3064 bSuccess = TRUE;
3065 else
3066 FTP_SetResponseError(nResCode);
3069 lend:
3070 return bSuccess;
3074 /***********************************************************************
3075 * FTP_DoPassive (internal)
3077 * Tell server that we want to do passive transfers
3078 * and connect data socket
3080 * RETURNS
3081 * TRUE on success
3082 * FALSE on failure
3085 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3087 INT nResCode;
3088 BOOL bSuccess = FALSE;
3090 TRACE("\n");
3091 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3092 goto lend;
3094 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3095 if (nResCode)
3097 if (nResCode == 227)
3099 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3100 LPSTR p;
3101 int f[6];
3102 int i;
3103 char *pAddr, *pPort;
3104 INT nsocket = -1;
3105 struct sockaddr_in dataSocketAddress;
3107 p = lpszResponseBuffer+4; /* skip status code */
3108 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3110 if (*p == '\0')
3112 ERR("no address found in response, aborting\n");
3113 goto lend;
3116 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3117 &f[4], &f[5]) != 6)
3119 ERR("unknown response address format '%s', aborting\n", p);
3120 goto lend;
3122 for (i=0; i < 6; i++)
3123 f[i] = f[i] & 0xff;
3125 dataSocketAddress = lpwfs->socketAddress;
3126 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3127 pPort = (char *)&(dataSocketAddress.sin_port);
3128 pAddr[0] = f[0];
3129 pAddr[1] = f[1];
3130 pAddr[2] = f[2];
3131 pAddr[3] = f[3];
3132 pPort[0] = f[4];
3133 pPort[1] = f[5];
3135 nsocket = socket(AF_INET,SOCK_STREAM,0);
3136 if (nsocket == -1)
3137 goto lend;
3139 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3141 ERR("can't connect passive FTP data port.\n");
3142 closesocket(nsocket);
3143 goto lend;
3145 lpwfs->pasvSocket = nsocket;
3146 bSuccess = TRUE;
3148 else
3149 FTP_SetResponseError(nResCode);
3152 lend:
3153 return bSuccess;
3157 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3159 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3161 if (!FTP_DoPassive(lpwfs))
3162 return FALSE;
3164 else
3166 if (!FTP_SendPort(lpwfs))
3167 return FALSE;
3169 return TRUE;
3173 /***********************************************************************
3174 * FTP_GetDataSocket (internal)
3176 * Either accepts an incoming data socket connection from the server
3177 * or just returns the already opened socket after a PASV command
3178 * in case of passive FTP.
3181 * RETURNS
3182 * TRUE on success
3183 * FALSE on failure
3186 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3188 struct sockaddr_in saddr;
3189 socklen_t addrlen = sizeof(struct sockaddr);
3191 TRACE("\n");
3192 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3194 *nDataSocket = lpwfs->pasvSocket;
3195 lpwfs->pasvSocket = -1;
3197 else
3199 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3200 closesocket(lpwfs->lstnSocket);
3201 lpwfs->lstnSocket = -1;
3203 return *nDataSocket != -1;
3207 /***********************************************************************
3208 * FTP_SendData (internal)
3210 * Send data to the server
3212 * RETURNS
3213 * TRUE on success
3214 * FALSE on failure
3217 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3219 BY_HANDLE_FILE_INFORMATION fi;
3220 DWORD nBytesRead = 0;
3221 DWORD nBytesSent = 0;
3222 DWORD nTotalSent = 0;
3223 DWORD nBytesToSend, nLen;
3224 int nRC = 1;
3225 time_t s_long_time, e_long_time;
3226 LONG nSeconds;
3227 CHAR *lpszBuffer;
3229 TRACE("\n");
3230 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3232 /* Get the size of the file. */
3233 GetFileInformationByHandle(hFile, &fi);
3234 time(&s_long_time);
3238 nBytesToSend = nBytesRead - nBytesSent;
3240 if (nBytesToSend <= 0)
3242 /* Read data from file. */
3243 nBytesSent = 0;
3244 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3245 ERR("Failed reading from file\n");
3247 if (nBytesRead > 0)
3248 nBytesToSend = nBytesRead;
3249 else
3250 break;
3253 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3254 DATA_PACKET_SIZE : nBytesToSend;
3255 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3257 if (nRC != -1)
3259 nBytesSent += nRC;
3260 nTotalSent += nRC;
3263 /* Do some computation to display the status. */
3264 time(&e_long_time);
3265 nSeconds = e_long_time - s_long_time;
3266 if( nSeconds / 60 > 0 )
3268 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3269 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3270 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3272 else
3274 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3275 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3276 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3278 } while (nRC != -1);
3280 TRACE("file transfer complete!\n");
3282 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3284 return nTotalSent;
3288 /***********************************************************************
3289 * FTP_SendRetrieve (internal)
3291 * Send request to retrieve a file
3293 * RETURNS
3294 * Number of bytes to be received on success
3295 * 0 on failure
3298 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3300 INT nResCode;
3301 BOOL ret;
3303 TRACE("\n");
3304 if (!(ret = FTP_InitListenSocket(lpwfs)))
3305 goto lend;
3307 if (!(ret = FTP_SendType(lpwfs, dwType)))
3308 goto lend;
3310 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3311 goto lend;
3313 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3314 goto lend;
3316 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3317 if ((nResCode != 125) && (nResCode != 150)) {
3318 /* That means that we got an error getting the file. */
3319 FTP_SetResponseError(nResCode);
3320 ret = FALSE;
3323 lend:
3324 if (!ret && lpwfs->lstnSocket != -1)
3326 closesocket(lpwfs->lstnSocket);
3327 lpwfs->lstnSocket = -1;
3330 return ret;
3334 /***********************************************************************
3335 * FTP_RetrieveData (internal)
3337 * Retrieve data from server
3339 * RETURNS
3340 * TRUE on success
3341 * FALSE on failure
3344 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3346 DWORD nBytesWritten;
3347 INT nRC = 0;
3348 CHAR *lpszBuffer;
3350 TRACE("\n");
3352 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3353 if (NULL == lpszBuffer)
3355 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3356 return FALSE;
3359 while (nRC != -1)
3361 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3362 if (nRC != -1)
3364 /* other side closed socket. */
3365 if (nRC == 0)
3366 goto recv_end;
3367 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3371 TRACE("Data transfer complete\n");
3373 recv_end:
3374 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3376 return (nRC != -1);
3379 /***********************************************************************
3380 * FTPFINDNEXT_Destroy (internal)
3382 * Deallocate session handle
3384 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3386 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3387 DWORD i;
3389 TRACE("\n");
3391 WININET_Release(&lpwfn->lpFtpSession->hdr);
3393 for (i = 0; i < lpwfn->size; i++)
3395 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3398 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3401 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3403 WIN32_FIND_DATAW *find_data = data;
3404 DWORD res = ERROR_SUCCESS;
3406 TRACE("index(%d) size(%d)\n", find->index, find->size);
3408 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3410 if (find->index < find->size) {
3411 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3412 find->index++;
3414 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3415 }else {
3416 res = ERROR_NO_MORE_FILES;
3419 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3421 INTERNET_ASYNC_RESULT iar;
3423 iar.dwResult = (res == ERROR_SUCCESS);
3424 iar.dwError = res;
3426 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3427 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3428 sizeof(INTERNET_ASYNC_RESULT));
3431 return res;
3434 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3436 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3438 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3441 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3443 switch(option) {
3444 case INTERNET_OPTION_HANDLE_TYPE:
3445 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3447 if (*size < sizeof(ULONG))
3448 return ERROR_INSUFFICIENT_BUFFER;
3450 *size = sizeof(DWORD);
3451 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3452 return ERROR_SUCCESS;
3455 return INET_QueryOption(hdr, option, buffer, size, unicode);
3458 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3460 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3462 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3464 WORKREQUEST workRequest;
3465 struct WORKREQ_FTPFINDNEXTW *req;
3467 workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3468 workRequest.hdr = WININET_AddRef( &find->hdr );
3469 req = &workRequest.u.FtpFindNextW;
3470 req->lpFindFileData = data;
3472 INTERNET_AsyncCall(&workRequest);
3474 return ERROR_SUCCESS;
3477 return FTPFINDNEXT_FindNextFileProc(find, data);
3480 static const object_vtbl_t FTPFINDNEXTVtbl = {
3481 FTPFINDNEXT_Destroy,
3482 NULL,
3483 FTPFINDNEXT_QueryOption,
3484 NULL,
3485 NULL,
3486 NULL,
3487 NULL,
3488 NULL,
3489 NULL,
3490 FTPFINDNEXT_FindNextFileW
3493 /***********************************************************************
3494 * FTP_ReceiveFileList (internal)
3496 * Read file list from server
3498 * RETURNS
3499 * Handle to file list on success
3500 * NULL on failure
3503 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3504 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3506 DWORD dwSize = 0;
3507 LPFILEPROPERTIESW lpafp = NULL;
3508 LPWININETFTPFINDNEXTW lpwfn = NULL;
3510 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3512 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3514 if(lpFindFileData)
3515 FTP_ConvertFileProp(lpafp, lpFindFileData);
3517 lpwfn = alloc_object(&lpwfs->hdr, &FTPFINDNEXTVtbl, sizeof(WININETFTPFINDNEXTW));
3518 if (lpwfn)
3520 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3521 lpwfn->hdr.dwContext = dwContext;
3522 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3523 lpwfn->size = dwSize;
3524 lpwfn->lpafp = lpafp;
3526 WININET_AddRef( &lpwfs->hdr );
3527 lpwfn->lpFtpSession = lpwfs;
3528 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3532 TRACE("Matched %d files\n", dwSize);
3533 return lpwfn ? lpwfn->hdr.hInternet : NULL;
3537 /***********************************************************************
3538 * FTP_ConvertFileProp (internal)
3540 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3542 * RETURNS
3543 * TRUE on success
3544 * FALSE on failure
3547 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3549 BOOL bSuccess = FALSE;
3551 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3553 if (lpafp)
3555 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3556 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3557 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3559 /* Not all fields are filled in */
3560 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3561 lpFindFileData->nFileSizeLow = lpafp->nSize;
3563 if (lpafp->bIsDirectory)
3564 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3566 if (lpafp->lpszName)
3567 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3569 bSuccess = TRUE;
3572 return bSuccess;
3575 /***********************************************************************
3576 * FTP_ParseNextFile (internal)
3578 * Parse the next line in file listing
3580 * RETURNS
3581 * TRUE on success
3582 * FALSE on failure
3584 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3586 static const char szSpace[] = " \t";
3587 DWORD nBufLen;
3588 char *pszLine;
3589 char *pszToken;
3590 char *pszTmp;
3591 BOOL found = FALSE;
3592 int i;
3594 lpfp->lpszName = NULL;
3595 do {
3596 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3597 return FALSE;
3599 pszToken = strtok(pszLine, szSpace);
3600 /* ls format
3601 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3603 * For instance:
3604 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3606 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3607 if(!FTP_ParsePermission(pszToken, lpfp))
3608 lpfp->bIsDirectory = FALSE;
3609 for(i=0; i<=3; i++) {
3610 if(!(pszToken = strtok(NULL, szSpace)))
3611 break;
3613 if(!pszToken) continue;
3614 if(lpfp->bIsDirectory) {
3615 TRACE("Is directory\n");
3616 lpfp->nSize = 0;
3618 else {
3619 TRACE("Size: %s\n", pszToken);
3620 lpfp->nSize = atol(pszToken);
3623 lpfp->tmLastModified.wSecond = 0;
3624 lpfp->tmLastModified.wMinute = 0;
3625 lpfp->tmLastModified.wHour = 0;
3626 lpfp->tmLastModified.wDay = 0;
3627 lpfp->tmLastModified.wMonth = 0;
3628 lpfp->tmLastModified.wYear = 0;
3630 /* Determine month */
3631 pszToken = strtok(NULL, szSpace);
3632 if(!pszToken) continue;
3633 if(strlen(pszToken) >= 3) {
3634 pszToken[3] = 0;
3635 if((pszTmp = StrStrIA(szMonths, pszToken)))
3636 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3638 /* Determine day */
3639 pszToken = strtok(NULL, szSpace);
3640 if(!pszToken) continue;
3641 lpfp->tmLastModified.wDay = atoi(pszToken);
3642 /* Determine time or year */
3643 pszToken = strtok(NULL, szSpace);
3644 if(!pszToken) continue;
3645 if((pszTmp = strchr(pszToken, ':'))) {
3646 SYSTEMTIME curr_time;
3647 *pszTmp = 0;
3648 pszTmp++;
3649 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3650 lpfp->tmLastModified.wHour = atoi(pszToken);
3651 GetLocalTime( &curr_time );
3652 lpfp->tmLastModified.wYear = curr_time.wYear;
3654 else {
3655 lpfp->tmLastModified.wYear = atoi(pszToken);
3656 lpfp->tmLastModified.wHour = 12;
3658 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3659 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3660 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3662 pszToken = strtok(NULL, szSpace);
3663 if(!pszToken) continue;
3664 lpfp->lpszName = heap_strdupAtoW(pszToken);
3665 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3667 /* NT way of parsing ... :
3669 07-13-03 08:55PM <DIR> sakpatch
3670 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3672 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3673 int mon, mday, year, hour, min;
3674 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3676 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3677 lpfp->tmLastModified.wDay = mday;
3678 lpfp->tmLastModified.wMonth = mon;
3679 lpfp->tmLastModified.wYear = year;
3681 /* Hacky and bad Y2K protection :-) */
3682 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3684 pszToken = strtok(NULL, szSpace);
3685 if(!pszToken) continue;
3686 sscanf(pszToken, "%d:%d", &hour, &min);
3687 lpfp->tmLastModified.wHour = hour;
3688 lpfp->tmLastModified.wMinute = min;
3689 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3690 lpfp->tmLastModified.wHour += 12;
3692 lpfp->tmLastModified.wSecond = 0;
3694 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3695 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3696 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3698 pszToken = strtok(NULL, szSpace);
3699 if(!pszToken) continue;
3700 if(!strcasecmp(pszToken, "<DIR>")) {
3701 lpfp->bIsDirectory = TRUE;
3702 lpfp->nSize = 0;
3703 TRACE("Is directory\n");
3705 else {
3706 lpfp->bIsDirectory = FALSE;
3707 lpfp->nSize = atol(pszToken);
3708 TRACE("Size: %d\n", lpfp->nSize);
3711 pszToken = strtok(NULL, szSpace);
3712 if(!pszToken) continue;
3713 lpfp->lpszName = heap_strdupAtoW(pszToken);
3714 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3716 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3717 else if(pszToken[0] == '+') {
3718 FIXME("EPLF Format not implemented\n");
3721 if(lpfp->lpszName) {
3722 if((lpszSearchFile == NULL) ||
3723 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3724 found = TRUE;
3725 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3727 else {
3728 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3729 lpfp->lpszName = NULL;
3732 } while(!found);
3733 return TRUE;
3736 /***********************************************************************
3737 * FTP_ParseDirectory (internal)
3739 * Parse string of directory information
3741 * RETURNS
3742 * TRUE on success
3743 * FALSE on failure
3745 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3746 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3748 BOOL bSuccess = TRUE;
3749 INT sizeFilePropArray = 500;/*20; */
3750 INT indexFilePropArray = -1;
3752 TRACE("\n");
3754 /* Allocate initial file properties array */
3755 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3756 if (!*lpafp)
3757 return FALSE;
3759 do {
3760 if (indexFilePropArray+1 >= sizeFilePropArray)
3762 LPFILEPROPERTIESW tmpafp;
3764 sizeFilePropArray *= 2;
3765 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3766 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3767 if (NULL == tmpafp)
3769 bSuccess = FALSE;
3770 break;
3773 *lpafp = tmpafp;
3775 indexFilePropArray++;
3776 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3778 if (bSuccess && indexFilePropArray)
3780 if (indexFilePropArray < sizeFilePropArray - 1)
3782 LPFILEPROPERTIESW tmpafp;
3784 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3785 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3786 if (NULL != tmpafp)
3787 *lpafp = tmpafp;
3789 *dwfp = indexFilePropArray;
3791 else
3793 HeapFree(GetProcessHeap(), 0, *lpafp);
3794 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3795 bSuccess = FALSE;
3798 return bSuccess;
3802 /***********************************************************************
3803 * FTP_ParsePermission (internal)
3805 * Parse permission string of directory information
3807 * RETURNS
3808 * TRUE on success
3809 * FALSE on failure
3812 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3814 BOOL bSuccess = TRUE;
3815 unsigned short nPermission = 0;
3816 INT nPos = 1;
3817 INT nLast = 9;
3819 TRACE("\n");
3820 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3822 bSuccess = FALSE;
3823 return bSuccess;
3826 lpfp->bIsDirectory = (*lpszPermission == 'd');
3829 switch (nPos)
3831 case 1:
3832 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3833 break;
3834 case 2:
3835 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3836 break;
3837 case 3:
3838 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3839 break;
3840 case 4:
3841 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3842 break;
3843 case 5:
3844 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3845 break;
3846 case 6:
3847 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3848 break;
3849 case 7:
3850 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3851 break;
3852 case 8:
3853 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3854 break;
3855 case 9:
3856 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3857 break;
3859 nPos++;
3860 }while (nPos <= nLast);
3862 lpfp->permissions = nPermission;
3863 return bSuccess;
3867 /***********************************************************************
3868 * FTP_SetResponseError (internal)
3870 * Set the appropriate error code for a given response from the server
3872 * RETURNS
3875 static DWORD FTP_SetResponseError(DWORD dwResponse)
3877 DWORD dwCode = 0;
3879 switch(dwResponse)
3881 case 425: /* Cannot open data connection. */
3882 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3883 break;
3885 case 426: /* Connection closed, transer aborted. */
3886 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3887 break;
3889 case 530: /* Not logged in. Login incorrect. */
3890 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3891 break;
3893 case 421: /* Service not available - Server may be shutting down. */
3894 case 450: /* File action not taken. File may be busy. */
3895 case 451: /* Action aborted. Server error. */
3896 case 452: /* Action not taken. Insufficient storage space on server. */
3897 case 500: /* Syntax error. Command unrecognized. */
3898 case 501: /* Syntax error. Error in parameters or arguments. */
3899 case 502: /* Command not implemented. */
3900 case 503: /* Bad sequence of commands. */
3901 case 504: /* Command not implemented for that parameter. */
3902 case 532: /* Need account for storing files */
3903 case 550: /* File action not taken. File not found or no access. */
3904 case 551: /* Requested action aborted. Page type unknown */
3905 case 552: /* Action aborted. Exceeded storage allocation */
3906 case 553: /* Action not taken. File name not allowed. */
3908 default:
3909 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3910 break;
3913 INTERNET_SetLastError(dwCode);
3914 return dwCode;