urlmon: Added HTTP redirecting test.
[wine.git] / dlls / wininet / ftp.c
blob6c9765f5165b2d6d6fd5553482d067971eb0773c
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);
221 /***********************************************************************
222 * FtpPutFileA (WININET.@)
224 * Uploads a file to the FTP server
226 * RETURNS
227 * TRUE on success
228 * FALSE on failure
231 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
232 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
234 LPWSTR lpwzLocalFile;
235 LPWSTR lpwzNewRemoteFile;
236 BOOL ret;
238 lpwzLocalFile = heap_strdupAtoW(lpszLocalFile);
239 lpwzNewRemoteFile = heap_strdupAtoW(lpszNewRemoteFile);
240 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
241 dwFlags, dwContext);
242 HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
243 HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
244 return ret;
247 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
249 struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
250 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
252 TRACE("%p\n", lpwfs);
254 FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
255 req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
257 HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
258 HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
261 /***********************************************************************
262 * FtpPutFileW (WININET.@)
264 * Uploads a file to the FTP server
266 * RETURNS
267 * TRUE on success
268 * FALSE on failure
271 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
272 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
274 ftp_session_t *lpwfs;
275 appinfo_t *hIC = NULL;
276 BOOL r = FALSE;
278 if (!lpszLocalFile || !lpszNewRemoteFile)
280 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
281 return FALSE;
284 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
285 if (!lpwfs)
287 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
288 return FALSE;
291 if (WH_HFTPSESSION != lpwfs->hdr.htype)
293 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
294 goto lend;
297 if (lpwfs->download_in_progress != NULL)
299 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
300 goto lend;
303 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
305 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
306 goto lend;
309 hIC = lpwfs->lpAppInfo;
310 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
312 WORKREQUEST workRequest;
313 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
315 workRequest.asyncproc = AsyncFtpPutFileProc;
316 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
317 req->lpszLocalFile = heap_strdupW(lpszLocalFile);
318 req->lpszNewRemoteFile = heap_strdupW(lpszNewRemoteFile);
319 req->dwFlags = dwFlags;
320 req->dwContext = dwContext;
322 r = INTERNET_AsyncCall(&workRequest);
324 else
326 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
327 lpszNewRemoteFile, dwFlags, dwContext);
330 lend:
331 WININET_Release( &lpwfs->hdr );
333 return r;
336 /***********************************************************************
337 * FTP_FtpPutFileW (Internal)
339 * Uploads a file to the FTP server
341 * RETURNS
342 * TRUE on success
343 * FALSE on failure
346 static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
347 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
349 HANDLE hFile;
350 BOOL bSuccess = FALSE;
351 appinfo_t *hIC = NULL;
352 INT nResCode;
354 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
356 /* Clear any error information */
357 INTERNET_SetLastError(0);
359 /* Open file to be uploaded */
360 if (INVALID_HANDLE_VALUE ==
361 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
362 /* Let CreateFile set the appropriate error */
363 return FALSE;
365 hIC = lpwfs->lpAppInfo;
367 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
369 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
371 INT nDataSocket;
373 /* Get data socket to server */
374 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
376 FTP_SendData(lpwfs, nDataSocket, hFile);
377 closesocket(nDataSocket);
378 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
379 if (nResCode)
381 if (nResCode == 226)
382 bSuccess = TRUE;
383 else
384 FTP_SetResponseError(nResCode);
389 if (lpwfs->lstnSocket != -1)
391 closesocket(lpwfs->lstnSocket);
392 lpwfs->lstnSocket = -1;
395 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
397 INTERNET_ASYNC_RESULT iar;
399 iar.dwResult = (DWORD)bSuccess;
400 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
401 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
402 &iar, sizeof(INTERNET_ASYNC_RESULT));
405 CloseHandle(hFile);
407 return bSuccess;
411 /***********************************************************************
412 * FtpSetCurrentDirectoryA (WININET.@)
414 * Change the working directory on the FTP server
416 * RETURNS
417 * TRUE on success
418 * FALSE on failure
421 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
423 LPWSTR lpwzDirectory;
424 BOOL ret;
426 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
427 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
428 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
429 return ret;
433 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
435 struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
436 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
438 TRACE("%p\n", lpwfs);
440 FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
441 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
444 /***********************************************************************
445 * FtpSetCurrentDirectoryW (WININET.@)
447 * Change the working directory on the FTP server
449 * RETURNS
450 * TRUE on success
451 * FALSE on failure
454 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
456 ftp_session_t *lpwfs = NULL;
457 appinfo_t *hIC = NULL;
458 BOOL r = FALSE;
460 if (!lpszDirectory)
462 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
463 goto lend;
466 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
467 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
469 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
470 goto lend;
473 if (lpwfs->download_in_progress != NULL)
475 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
476 goto lend;
479 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
481 hIC = lpwfs->lpAppInfo;
482 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
484 WORKREQUEST workRequest;
485 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
487 workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
488 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
489 req = &workRequest.u.FtpSetCurrentDirectoryW;
490 req->lpszDirectory = heap_strdupW(lpszDirectory);
492 r = INTERNET_AsyncCall(&workRequest);
494 else
496 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
499 lend:
500 if( lpwfs )
501 WININET_Release( &lpwfs->hdr );
503 return r;
507 /***********************************************************************
508 * FTP_FtpSetCurrentDirectoryW (Internal)
510 * Change the working directory on the FTP server
512 * RETURNS
513 * TRUE on success
514 * FALSE on failure
517 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
519 INT nResCode;
520 appinfo_t *hIC = NULL;
521 DWORD bSuccess = FALSE;
523 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
525 /* Clear any error information */
526 INTERNET_SetLastError(0);
528 hIC = lpwfs->lpAppInfo;
529 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
530 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
531 goto lend;
533 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
535 if (nResCode)
537 if (nResCode == 250)
538 bSuccess = TRUE;
539 else
540 FTP_SetResponseError(nResCode);
543 lend:
544 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
546 INTERNET_ASYNC_RESULT iar;
548 iar.dwResult = bSuccess;
549 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
550 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
551 &iar, sizeof(INTERNET_ASYNC_RESULT));
553 return bSuccess;
557 /***********************************************************************
558 * FtpCreateDirectoryA (WININET.@)
560 * Create new directory on the FTP server
562 * RETURNS
563 * TRUE on success
564 * FALSE on failure
567 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
569 LPWSTR lpwzDirectory;
570 BOOL ret;
572 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
573 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
574 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
575 return ret;
579 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
581 struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
582 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
584 TRACE(" %p\n", lpwfs);
586 FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
587 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
590 /***********************************************************************
591 * FtpCreateDirectoryW (WININET.@)
593 * Create new directory on the FTP server
595 * RETURNS
596 * TRUE on success
597 * FALSE on failure
600 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
602 ftp_session_t *lpwfs;
603 appinfo_t *hIC = NULL;
604 BOOL r = FALSE;
606 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
607 if (!lpwfs)
609 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
610 return FALSE;
613 if (WH_HFTPSESSION != lpwfs->hdr.htype)
615 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
616 goto lend;
619 if (lpwfs->download_in_progress != NULL)
621 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
622 goto lend;
625 if (!lpszDirectory)
627 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
628 goto lend;
631 hIC = lpwfs->lpAppInfo;
632 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
634 WORKREQUEST workRequest;
635 struct WORKREQ_FTPCREATEDIRECTORYW *req;
637 workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
638 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
639 req = &workRequest.u.FtpCreateDirectoryW;
640 req->lpszDirectory = heap_strdupW(lpszDirectory);
642 r = INTERNET_AsyncCall(&workRequest);
644 else
646 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
648 lend:
649 WININET_Release( &lpwfs->hdr );
651 return r;
655 /***********************************************************************
656 * FTP_FtpCreateDirectoryW (Internal)
658 * Create new directory on the FTP server
660 * RETURNS
661 * TRUE on success
662 * FALSE on failure
665 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
667 INT nResCode;
668 BOOL bSuccess = FALSE;
669 appinfo_t *hIC = NULL;
671 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
673 /* Clear any error information */
674 INTERNET_SetLastError(0);
676 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
677 goto lend;
679 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
680 if (nResCode)
682 if (nResCode == 257)
683 bSuccess = TRUE;
684 else
685 FTP_SetResponseError(nResCode);
688 lend:
689 hIC = lpwfs->lpAppInfo;
690 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
692 INTERNET_ASYNC_RESULT iar;
694 iar.dwResult = (DWORD)bSuccess;
695 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
696 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
697 &iar, sizeof(INTERNET_ASYNC_RESULT));
700 return bSuccess;
703 /***********************************************************************
704 * FtpFindFirstFileA (WININET.@)
706 * Search the specified directory
708 * RETURNS
709 * HINTERNET on success
710 * NULL on failure
713 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
714 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
716 LPWSTR lpwzSearchFile;
717 WIN32_FIND_DATAW wfd;
718 LPWIN32_FIND_DATAW lpFindFileDataW;
719 HINTERNET ret;
721 lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
722 lpFindFileDataW = lpFindFileData?&wfd:NULL;
723 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
724 HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
726 if (ret && lpFindFileData)
727 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
729 return ret;
733 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
735 struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
736 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
738 TRACE("%p\n", lpwfs);
740 FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
741 req->lpFindFileData, req->dwFlags, req->dwContext);
742 HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
745 /***********************************************************************
746 * FtpFindFirstFileW (WININET.@)
748 * Search the specified directory
750 * RETURNS
751 * HINTERNET on success
752 * NULL on failure
755 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
756 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
758 ftp_session_t *lpwfs;
759 appinfo_t *hIC = NULL;
760 HINTERNET r = NULL;
762 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
763 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
765 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
766 goto lend;
769 if (lpwfs->download_in_progress != NULL)
771 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
772 goto lend;
775 hIC = lpwfs->lpAppInfo;
776 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
778 WORKREQUEST workRequest;
779 struct WORKREQ_FTPFINDFIRSTFILEW *req;
781 workRequest.asyncproc = AsyncFtpFindFirstFileProc;
782 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
783 req = &workRequest.u.FtpFindFirstFileW;
784 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : heap_strdupW(lpszSearchFile);
785 req->lpFindFileData = lpFindFileData;
786 req->dwFlags = dwFlags;
787 req->dwContext= dwContext;
789 INTERNET_AsyncCall(&workRequest);
790 r = NULL;
792 else
794 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
795 dwFlags, dwContext);
797 lend:
798 if( lpwfs )
799 WININET_Release( &lpwfs->hdr );
801 return r;
805 /***********************************************************************
806 * FTP_FtpFindFirstFileW (Internal)
808 * Search the specified directory
810 * RETURNS
811 * HINTERNET on success
812 * NULL on failure
815 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
816 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
818 INT nResCode;
819 appinfo_t *hIC = NULL;
820 HINTERNET hFindNext = NULL;
822 TRACE("\n");
824 /* Clear any error information */
825 INTERNET_SetLastError(0);
827 if (!FTP_InitListenSocket(lpwfs))
828 goto lend;
830 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
831 goto lend;
833 if (!FTP_SendPortOrPasv(lpwfs))
834 goto lend;
836 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
837 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
838 goto lend;
840 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
841 if (nResCode)
843 if (nResCode == 125 || nResCode == 150)
845 INT nDataSocket;
847 /* Get data socket to server */
848 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
850 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
851 closesocket(nDataSocket);
852 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
853 if (nResCode != 226 && nResCode != 250)
854 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
857 else
858 FTP_SetResponseError(nResCode);
861 lend:
862 if (lpwfs->lstnSocket != -1)
864 closesocket(lpwfs->lstnSocket);
865 lpwfs->lstnSocket = -1;
868 hIC = lpwfs->lpAppInfo;
869 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
871 INTERNET_ASYNC_RESULT iar;
873 if (hFindNext)
875 iar.dwResult = (DWORD_PTR)hFindNext;
876 iar.dwError = ERROR_SUCCESS;
877 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
878 &iar, sizeof(INTERNET_ASYNC_RESULT));
881 iar.dwResult = (DWORD_PTR)hFindNext;
882 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
883 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
884 &iar, sizeof(INTERNET_ASYNC_RESULT));
887 return hFindNext;
891 /***********************************************************************
892 * FtpGetCurrentDirectoryA (WININET.@)
894 * Retrieves the current directory
896 * RETURNS
897 * TRUE on success
898 * FALSE on failure
901 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
902 LPDWORD lpdwCurrentDirectory)
904 WCHAR *dir = NULL;
905 DWORD len;
906 BOOL ret;
908 if(lpdwCurrentDirectory) {
909 len = *lpdwCurrentDirectory;
910 if(lpszCurrentDirectory)
912 dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
913 if (NULL == dir)
915 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
916 return FALSE;
920 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
922 if (ret && lpszCurrentDirectory)
923 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
925 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
926 HeapFree(GetProcessHeap(), 0, dir);
927 return ret;
931 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
933 struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
934 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
936 TRACE("%p\n", lpwfs);
938 FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
941 /***********************************************************************
942 * FtpGetCurrentDirectoryW (WININET.@)
944 * Retrieves the current directory
946 * RETURNS
947 * TRUE on success
948 * FALSE on failure
951 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
952 LPDWORD lpdwCurrentDirectory)
954 ftp_session_t *lpwfs;
955 appinfo_t *hIC = NULL;
956 BOOL r = FALSE;
958 TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
960 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
961 if (NULL == lpwfs)
963 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
964 goto lend;
967 if (WH_HFTPSESSION != lpwfs->hdr.htype)
969 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
970 goto lend;
973 if (!lpdwCurrentDirectory)
975 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
976 goto lend;
979 if (lpszCurrentDirectory == NULL)
981 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
982 goto lend;
985 if (lpwfs->download_in_progress != NULL)
987 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
988 goto lend;
991 hIC = lpwfs->lpAppInfo;
992 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
994 WORKREQUEST workRequest;
995 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
997 workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
998 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
999 req = &workRequest.u.FtpGetCurrentDirectoryW;
1000 req->lpszDirectory = lpszCurrentDirectory;
1001 req->lpdwDirectory = lpdwCurrentDirectory;
1003 r = INTERNET_AsyncCall(&workRequest);
1005 else
1007 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
1008 lpdwCurrentDirectory);
1011 lend:
1012 if( lpwfs )
1013 WININET_Release( &lpwfs->hdr );
1015 return r;
1019 /***********************************************************************
1020 * FTP_FtpGetCurrentDirectoryW (Internal)
1022 * Retrieves the current directory
1024 * RETURNS
1025 * TRUE on success
1026 * FALSE on failure
1029 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
1030 LPDWORD lpdwCurrentDirectory)
1032 INT nResCode;
1033 appinfo_t *hIC = NULL;
1034 DWORD bSuccess = FALSE;
1036 /* Clear any error information */
1037 INTERNET_SetLastError(0);
1039 hIC = lpwfs->lpAppInfo;
1040 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1041 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1042 goto lend;
1044 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1045 if (nResCode)
1047 if (nResCode == 257) /* Extract directory name */
1049 DWORD firstpos, lastpos, len;
1050 LPWSTR lpszResponseBuffer = heap_strdupAtoW(INTERNET_GetResponseBuffer());
1052 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1054 if ('"' == lpszResponseBuffer[lastpos])
1056 if (!firstpos)
1057 firstpos = lastpos;
1058 else
1059 break;
1062 len = lastpos - firstpos;
1063 if (*lpdwCurrentDirectory >= len)
1065 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1066 lpszCurrentDirectory[len - 1] = 0;
1067 *lpdwCurrentDirectory = len;
1068 bSuccess = TRUE;
1070 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1072 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
1074 else
1075 FTP_SetResponseError(nResCode);
1078 lend:
1079 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1081 INTERNET_ASYNC_RESULT iar;
1083 iar.dwResult = bSuccess;
1084 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1085 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1086 &iar, sizeof(INTERNET_ASYNC_RESULT));
1089 return bSuccess;
1093 /***********************************************************************
1094 * FTPFILE_Destroy(internal)
1096 * Closes the file transfer handle. This also 'cleans' the data queue of
1097 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1100 static void FTPFILE_Destroy(object_header_t *hdr)
1102 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1103 ftp_session_t *lpwfs = lpwh->lpFtpSession;
1104 INT nResCode;
1106 TRACE("\n");
1108 if (lpwh->cache_file_handle != INVALID_HANDLE_VALUE)
1109 CloseHandle(lpwh->cache_file_handle);
1111 HeapFree(GetProcessHeap(), 0, lpwh->cache_file);
1113 if (!lpwh->session_deleted)
1114 lpwfs->download_in_progress = NULL;
1116 if (lpwh->nDataSocket != -1)
1117 closesocket(lpwh->nDataSocket);
1119 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1120 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1122 WININET_Release(&lpwh->lpFtpSession->hdr);
1124 HeapFree(GetProcessHeap(), 0, lpwh);
1127 static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1129 switch(option) {
1130 case INTERNET_OPTION_HANDLE_TYPE:
1131 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1133 if (*size < sizeof(ULONG))
1134 return ERROR_INSUFFICIENT_BUFFER;
1136 *size = sizeof(DWORD);
1137 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1138 return ERROR_SUCCESS;
1139 case INTERNET_OPTION_DATAFILE_NAME:
1141 DWORD required;
1142 ftp_file_t *file = (ftp_file_t *)hdr;
1144 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1146 if (!file->cache_file)
1148 *size = 0;
1149 return ERROR_INTERNET_ITEM_NOT_FOUND;
1151 if (unicode)
1153 required = (lstrlenW(file->cache_file) + 1) * sizeof(WCHAR);
1154 if (*size < required)
1155 return ERROR_INSUFFICIENT_BUFFER;
1157 *size = required;
1158 memcpy(buffer, file->cache_file, *size);
1159 return ERROR_SUCCESS;
1161 else
1163 required = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, NULL, 0, NULL, NULL);
1164 if (required > *size)
1165 return ERROR_INSUFFICIENT_BUFFER;
1167 *size = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, buffer, *size, NULL, NULL);
1168 return ERROR_SUCCESS;
1172 return INET_QueryOption(option, buffer, size, unicode);
1175 static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
1177 ftp_file_t *file = (ftp_file_t*)hdr;
1178 int res;
1179 DWORD error;
1181 if (file->nDataSocket == -1)
1182 return ERROR_INTERNET_DISCONNECTED;
1184 /* FIXME: FTP should use NETCON_ stuff */
1185 res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1186 *read = res>0 ? res : 0;
1188 error = res >= 0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME */
1189 if (error == ERROR_SUCCESS && file->cache_file)
1191 DWORD bytes_written;
1193 if (!WriteFile(file->cache_file_handle, buffer, *read, &bytes_written, NULL))
1194 WARN("WriteFile failed: %u\n", GetLastError());
1196 return error;
1199 static DWORD FTPFILE_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
1200 DWORD flags, DWORD_PTR context)
1202 return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1205 static DWORD FTPFILE_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
1206 DWORD flags, DWORD_PTR context)
1208 return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1211 static DWORD FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1213 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1214 int res;
1216 res = send(lpwh->nDataSocket, buffer, size, 0);
1218 *written = res>0 ? res : 0;
1219 return res >= 0 ? ERROR_SUCCESS : sock_get_error(errno);
1222 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
1224 INTERNET_ASYNC_RESULT iar;
1225 BYTE buffer[4096];
1226 int available;
1228 TRACE("%p\n", file);
1230 available = recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1232 if(available != -1) {
1233 iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1234 iar.dwError = first_notif ? 0 : available;
1235 }else {
1236 iar.dwResult = 0;
1237 iar.dwError = INTERNET_GetLastError();
1240 INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1241 sizeof(INTERNET_ASYNC_RESULT));
1244 static void FTPFILE_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
1246 ftp_file_t *file = (ftp_file_t*)workRequest->hdr;
1248 FTP_ReceiveRequestData(file, FALSE);
1251 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1253 ftp_file_t *file = (ftp_file_t*) hdr;
1254 int retval, unread = 0;
1256 TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
1258 #ifdef FIONREAD
1259 retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
1260 if (!retval)
1261 TRACE("%d bytes of queued, but unread data\n", unread);
1262 #else
1263 FIXME("FIONREAD not available\n");
1264 #endif
1266 *available = unread;
1268 if(!unread) {
1269 BYTE byte;
1271 *available = 0;
1273 retval = recv(file->nDataSocket, &byte, 1, MSG_PEEK);
1274 if(retval > 0) {
1275 WORKREQUEST workRequest;
1277 *available = 0;
1278 workRequest.asyncproc = FTPFILE_AsyncQueryDataAvailableProc;
1279 workRequest.hdr = WININET_AddRef( &file->hdr );
1281 INTERNET_AsyncCall(&workRequest);
1283 return ERROR_IO_PENDING;
1287 return ERROR_SUCCESS;
1291 static const object_vtbl_t FTPFILEVtbl = {
1292 FTPFILE_Destroy,
1293 NULL,
1294 FTPFILE_QueryOption,
1295 NULL,
1296 FTPFILE_ReadFile,
1297 FTPFILE_ReadFileExA,
1298 FTPFILE_ReadFileExW,
1299 FTPFILE_WriteFile,
1300 FTPFILE_QueryDataAvailable,
1301 NULL
1304 /***********************************************************************
1305 * FTP_FtpOpenFileW (Internal)
1307 * Open a remote file for writing or reading
1309 * RETURNS
1310 * HINTERNET handle on success
1311 * NULL on failure
1314 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1315 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1316 DWORD_PTR dwContext)
1318 INT nDataSocket;
1319 BOOL bSuccess = FALSE;
1320 ftp_file_t *lpwh = NULL;
1321 appinfo_t *hIC = NULL;
1322 HINTERNET handle = NULL;
1324 TRACE("\n");
1326 /* Clear any error information */
1327 INTERNET_SetLastError(0);
1329 if (GENERIC_READ == fdwAccess)
1331 /* Set up socket to retrieve data */
1332 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1334 else if (GENERIC_WRITE == fdwAccess)
1336 /* Set up socket to send data */
1337 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1340 /* Get data socket to server */
1341 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1343 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(ftp_file_t));
1344 lpwh->hdr.htype = WH_HFILE;
1345 lpwh->hdr.vtbl = &FTPFILEVtbl;
1346 lpwh->hdr.dwFlags = dwFlags;
1347 lpwh->hdr.dwContext = dwContext;
1348 lpwh->hdr.dwInternalFlags = 0;
1349 lpwh->hdr.refs = 1;
1350 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
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 handle = WININET_AllocHandle( &lpwh->hdr );
1361 if( !handle )
1362 goto lend;
1364 /* Indicate that a download is currently in progress */
1365 lpwfs->download_in_progress = lpwh;
1368 if (lpwfs->lstnSocket != -1)
1370 closesocket(lpwfs->lstnSocket);
1371 lpwfs->lstnSocket = -1;
1374 if (bSuccess && fdwAccess == GENERIC_READ)
1376 WCHAR filename[MAX_PATH + 1];
1377 URL_COMPONENTSW uc;
1378 DWORD len;
1380 memset(&uc, 0, sizeof(uc));
1381 uc.dwStructSize = sizeof(uc);
1382 uc.nScheme = INTERNET_SCHEME_FTP;
1383 uc.lpszHostName = lpwfs->servername;
1384 uc.nPort = lpwfs->serverport;
1385 uc.lpszUserName = lpwfs->lpszUserName;
1386 uc.lpszUrlPath = heap_strdupW(lpszFileName);
1388 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1390 WCHAR *url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1392 if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1394 lpwh->cache_file = heap_strdupW(filename);
1395 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1396 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1397 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1399 WARN("Could not create cache file: %u\n", GetLastError());
1400 HeapFree(GetProcessHeap(), 0, lpwh->cache_file);
1401 lpwh->cache_file = NULL;
1404 HeapFree(GetProcessHeap(), 0, url);
1406 HeapFree(GetProcessHeap(), 0, uc.lpszUrlPath);
1409 hIC = lpwfs->lpAppInfo;
1410 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1412 INTERNET_ASYNC_RESULT iar;
1414 if (lpwh)
1416 iar.dwResult = (DWORD_PTR)handle;
1417 iar.dwError = ERROR_SUCCESS;
1418 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1419 &iar, sizeof(INTERNET_ASYNC_RESULT));
1422 if(bSuccess) {
1423 FTP_ReceiveRequestData(lpwh, TRUE);
1424 }else {
1425 iar.dwResult = 0;
1426 iar.dwError = INTERNET_GetLastError();
1427 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1428 &iar, sizeof(INTERNET_ASYNC_RESULT));
1432 lend:
1433 if( lpwh )
1434 WININET_Release( &lpwh->hdr );
1436 return handle;
1440 /***********************************************************************
1441 * FtpOpenFileA (WININET.@)
1443 * Open a remote file for writing or reading
1445 * RETURNS
1446 * HINTERNET handle on success
1447 * NULL on failure
1450 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1451 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1452 DWORD_PTR dwContext)
1454 LPWSTR lpwzFileName;
1455 HINTERNET ret;
1457 lpwzFileName = heap_strdupAtoW(lpszFileName);
1458 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1459 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1460 return ret;
1464 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1466 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1467 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1469 TRACE("%p\n", lpwfs);
1471 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1472 req->dwAccess, req->dwFlags, req->dwContext);
1473 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1476 /***********************************************************************
1477 * FtpOpenFileW (WININET.@)
1479 * Open a remote file for writing or reading
1481 * RETURNS
1482 * HINTERNET handle on success
1483 * NULL on failure
1486 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1487 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1488 DWORD_PTR dwContext)
1490 ftp_session_t *lpwfs;
1491 appinfo_t *hIC = NULL;
1492 HINTERNET r = NULL;
1494 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1495 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1497 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1498 if (!lpwfs)
1500 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1501 return FALSE;
1504 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1506 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1507 goto lend;
1510 if ((!lpszFileName) ||
1511 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1512 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1514 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1515 goto lend;
1518 if (lpwfs->download_in_progress != NULL)
1520 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1521 goto lend;
1524 hIC = lpwfs->lpAppInfo;
1525 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1527 WORKREQUEST workRequest;
1528 struct WORKREQ_FTPOPENFILEW *req;
1530 workRequest.asyncproc = AsyncFtpOpenFileProc;
1531 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1532 req = &workRequest.u.FtpOpenFileW;
1533 req->lpszFilename = heap_strdupW(lpszFileName);
1534 req->dwAccess = fdwAccess;
1535 req->dwFlags = dwFlags;
1536 req->dwContext = dwContext;
1538 INTERNET_AsyncCall(&workRequest);
1539 r = NULL;
1541 else
1543 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1546 lend:
1547 WININET_Release( &lpwfs->hdr );
1549 return r;
1553 /***********************************************************************
1554 * FtpGetFileA (WININET.@)
1556 * Retrieve file from the FTP server
1558 * RETURNS
1559 * TRUE on success
1560 * FALSE on failure
1563 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1564 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1565 DWORD_PTR dwContext)
1567 LPWSTR lpwzRemoteFile;
1568 LPWSTR lpwzNewFile;
1569 BOOL ret;
1571 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1572 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1573 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1574 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1575 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1576 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1577 return ret;
1581 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1583 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1584 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1586 TRACE("%p\n", lpwfs);
1588 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1589 req->lpszNewFile, req->fFailIfExists,
1590 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1591 HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1592 HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1596 /***********************************************************************
1597 * FtpGetFileW (WININET.@)
1599 * Retrieve file from the FTP server
1601 * RETURNS
1602 * TRUE on success
1603 * FALSE on failure
1606 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1607 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1608 DWORD_PTR dwContext)
1610 ftp_session_t *lpwfs;
1611 appinfo_t *hIC = NULL;
1612 BOOL r = FALSE;
1614 if (!lpszRemoteFile || !lpszNewFile)
1616 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1617 return FALSE;
1620 lpwfs = (ftp_session_t*) WININET_GetObject( hInternet );
1621 if (!lpwfs)
1623 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1624 return FALSE;
1627 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1629 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1630 goto lend;
1633 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1635 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1636 goto lend;
1639 if (lpwfs->download_in_progress != NULL)
1641 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1642 goto lend;
1645 hIC = lpwfs->lpAppInfo;
1646 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1648 WORKREQUEST workRequest;
1649 struct WORKREQ_FTPGETFILEW *req;
1651 workRequest.asyncproc = AsyncFtpGetFileProc;
1652 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1653 req = &workRequest.u.FtpGetFileW;
1654 req->lpszRemoteFile = heap_strdupW(lpszRemoteFile);
1655 req->lpszNewFile = heap_strdupW(lpszNewFile);
1656 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1657 req->fFailIfExists = fFailIfExists;
1658 req->dwFlags = dwInternetFlags;
1659 req->dwContext = dwContext;
1661 r = INTERNET_AsyncCall(&workRequest);
1663 else
1665 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1666 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1669 lend:
1670 WININET_Release( &lpwfs->hdr );
1672 return r;
1676 /***********************************************************************
1677 * FTP_FtpGetFileW (Internal)
1679 * Retrieve file from the FTP server
1681 * RETURNS
1682 * TRUE on success
1683 * FALSE on failure
1686 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1687 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1688 DWORD_PTR dwContext)
1690 BOOL bSuccess = FALSE;
1691 HANDLE hFile;
1692 appinfo_t *hIC = NULL;
1694 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1696 /* Clear any error information */
1697 INTERNET_SetLastError(0);
1699 /* Ensure we can write to lpszNewfile by opening it */
1700 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1701 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1702 if (INVALID_HANDLE_VALUE == hFile)
1703 return FALSE;
1705 /* Set up socket to retrieve data */
1706 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1708 INT nDataSocket;
1710 /* Get data socket to server */
1711 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1713 INT nResCode;
1715 /* Receive data */
1716 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1717 closesocket(nDataSocket);
1719 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1720 if (nResCode)
1722 if (nResCode == 226)
1723 bSuccess = TRUE;
1724 else
1725 FTP_SetResponseError(nResCode);
1730 if (lpwfs->lstnSocket != -1)
1732 closesocket(lpwfs->lstnSocket);
1733 lpwfs->lstnSocket = -1;
1736 CloseHandle(hFile);
1738 hIC = lpwfs->lpAppInfo;
1739 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1741 INTERNET_ASYNC_RESULT iar;
1743 iar.dwResult = (DWORD)bSuccess;
1744 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1745 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1746 &iar, sizeof(INTERNET_ASYNC_RESULT));
1749 if (!bSuccess) DeleteFileW(lpszNewFile);
1750 return bSuccess;
1753 /***********************************************************************
1754 * FtpGetFileSize (WININET.@)
1756 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1758 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1760 if (lpdwFileSizeHigh)
1761 *lpdwFileSizeHigh = 0;
1763 return 0;
1766 /***********************************************************************
1767 * FtpDeleteFileA (WININET.@)
1769 * Delete a file on the ftp server
1771 * RETURNS
1772 * TRUE on success
1773 * FALSE on failure
1776 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1778 LPWSTR lpwzFileName;
1779 BOOL ret;
1781 lpwzFileName = heap_strdupAtoW(lpszFileName);
1782 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1783 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1784 return ret;
1787 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1789 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1790 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1792 TRACE("%p\n", lpwfs);
1794 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1795 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1798 /***********************************************************************
1799 * FtpDeleteFileW (WININET.@)
1801 * Delete a file on the ftp server
1803 * RETURNS
1804 * TRUE on success
1805 * FALSE on failure
1808 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1810 ftp_session_t *lpwfs;
1811 appinfo_t *hIC = NULL;
1812 BOOL r = FALSE;
1814 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1815 if (!lpwfs)
1817 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1818 return FALSE;
1821 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1823 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1824 goto lend;
1827 if (lpwfs->download_in_progress != NULL)
1829 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1830 goto lend;
1833 if (!lpszFileName)
1835 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1836 goto lend;
1839 hIC = lpwfs->lpAppInfo;
1840 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1842 WORKREQUEST workRequest;
1843 struct WORKREQ_FTPDELETEFILEW *req;
1845 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1846 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1847 req = &workRequest.u.FtpDeleteFileW;
1848 req->lpszFilename = heap_strdupW(lpszFileName);
1850 r = INTERNET_AsyncCall(&workRequest);
1852 else
1854 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1857 lend:
1858 WININET_Release( &lpwfs->hdr );
1860 return r;
1863 /***********************************************************************
1864 * FTP_FtpDeleteFileW (Internal)
1866 * Delete a file on the ftp server
1868 * RETURNS
1869 * TRUE on success
1870 * FALSE on failure
1873 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1875 INT nResCode;
1876 BOOL bSuccess = FALSE;
1877 appinfo_t *hIC = NULL;
1879 TRACE("%p\n", lpwfs);
1881 /* Clear any error information */
1882 INTERNET_SetLastError(0);
1884 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1885 goto lend;
1887 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1888 if (nResCode)
1890 if (nResCode == 250)
1891 bSuccess = TRUE;
1892 else
1893 FTP_SetResponseError(nResCode);
1895 lend:
1896 hIC = lpwfs->lpAppInfo;
1897 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1899 INTERNET_ASYNC_RESULT iar;
1901 iar.dwResult = (DWORD)bSuccess;
1902 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1903 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1904 &iar, sizeof(INTERNET_ASYNC_RESULT));
1907 return bSuccess;
1911 /***********************************************************************
1912 * FtpRemoveDirectoryA (WININET.@)
1914 * Remove a directory on the ftp server
1916 * RETURNS
1917 * TRUE on success
1918 * FALSE on failure
1921 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1923 LPWSTR lpwzDirectory;
1924 BOOL ret;
1926 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1927 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1928 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1929 return ret;
1932 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1934 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1935 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1937 TRACE("%p\n", lpwfs);
1939 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1940 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1943 /***********************************************************************
1944 * FtpRemoveDirectoryW (WININET.@)
1946 * Remove a directory on the ftp server
1948 * RETURNS
1949 * TRUE on success
1950 * FALSE on failure
1953 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1955 ftp_session_t *lpwfs;
1956 appinfo_t *hIC = NULL;
1957 BOOL r = FALSE;
1959 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1960 if (!lpwfs)
1962 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1963 return FALSE;
1966 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1968 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1969 goto lend;
1972 if (lpwfs->download_in_progress != NULL)
1974 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1975 goto lend;
1978 if (!lpszDirectory)
1980 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1981 goto lend;
1984 hIC = lpwfs->lpAppInfo;
1985 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1987 WORKREQUEST workRequest;
1988 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1990 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1991 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1992 req = &workRequest.u.FtpRemoveDirectoryW;
1993 req->lpszDirectory = heap_strdupW(lpszDirectory);
1995 r = INTERNET_AsyncCall(&workRequest);
1997 else
1999 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
2002 lend:
2003 WININET_Release( &lpwfs->hdr );
2005 return r;
2008 /***********************************************************************
2009 * FTP_FtpRemoveDirectoryW (Internal)
2011 * Remove a directory on the ftp server
2013 * RETURNS
2014 * TRUE on success
2015 * FALSE on failure
2018 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
2020 INT nResCode;
2021 BOOL bSuccess = FALSE;
2022 appinfo_t *hIC = NULL;
2024 TRACE("\n");
2026 /* Clear any error information */
2027 INTERNET_SetLastError(0);
2029 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2030 goto lend;
2032 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2033 if (nResCode)
2035 if (nResCode == 250)
2036 bSuccess = TRUE;
2037 else
2038 FTP_SetResponseError(nResCode);
2041 lend:
2042 hIC = lpwfs->lpAppInfo;
2043 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2045 INTERNET_ASYNC_RESULT iar;
2047 iar.dwResult = (DWORD)bSuccess;
2048 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2049 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2050 &iar, sizeof(INTERNET_ASYNC_RESULT));
2053 return bSuccess;
2057 /***********************************************************************
2058 * FtpRenameFileA (WININET.@)
2060 * Rename a file on the ftp server
2062 * RETURNS
2063 * TRUE on success
2064 * FALSE on failure
2067 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2069 LPWSTR lpwzSrc;
2070 LPWSTR lpwzDest;
2071 BOOL ret;
2073 lpwzSrc = heap_strdupAtoW(lpszSrc);
2074 lpwzDest = heap_strdupAtoW(lpszDest);
2075 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2076 HeapFree(GetProcessHeap(), 0, lpwzSrc);
2077 HeapFree(GetProcessHeap(), 0, lpwzDest);
2078 return ret;
2081 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
2083 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
2084 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
2086 TRACE("%p\n", lpwfs);
2088 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
2089 HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
2090 HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
2093 /***********************************************************************
2094 * FtpRenameFileW (WININET.@)
2096 * Rename a file on the ftp server
2098 * RETURNS
2099 * TRUE on success
2100 * FALSE on failure
2103 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2105 ftp_session_t *lpwfs;
2106 appinfo_t *hIC = NULL;
2107 BOOL r = FALSE;
2109 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
2110 if (!lpwfs)
2112 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2113 return FALSE;
2116 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2118 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2119 goto lend;
2122 if (lpwfs->download_in_progress != NULL)
2124 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2125 goto lend;
2128 if (!lpszSrc || !lpszDest)
2130 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2131 goto lend;
2134 hIC = lpwfs->lpAppInfo;
2135 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2137 WORKREQUEST workRequest;
2138 struct WORKREQ_FTPRENAMEFILEW *req;
2140 workRequest.asyncproc = AsyncFtpRenameFileProc;
2141 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
2142 req = &workRequest.u.FtpRenameFileW;
2143 req->lpszSrcFile = heap_strdupW(lpszSrc);
2144 req->lpszDestFile = heap_strdupW(lpszDest);
2146 r = INTERNET_AsyncCall(&workRequest);
2148 else
2150 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2153 lend:
2154 WININET_Release( &lpwfs->hdr );
2156 return r;
2159 /***********************************************************************
2160 * FTP_FtpRenameFileW (Internal)
2162 * Rename a file on the ftp server
2164 * RETURNS
2165 * TRUE on success
2166 * FALSE on failure
2169 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2171 INT nResCode;
2172 BOOL bSuccess = FALSE;
2173 appinfo_t *hIC = NULL;
2175 TRACE("\n");
2177 /* Clear any error information */
2178 INTERNET_SetLastError(0);
2180 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2181 goto lend;
2183 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2184 if (nResCode == 350)
2186 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2187 goto lend;
2189 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2192 if (nResCode == 250)
2193 bSuccess = TRUE;
2194 else
2195 FTP_SetResponseError(nResCode);
2197 lend:
2198 hIC = lpwfs->lpAppInfo;
2199 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2201 INTERNET_ASYNC_RESULT iar;
2203 iar.dwResult = (DWORD)bSuccess;
2204 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2205 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2206 &iar, sizeof(INTERNET_ASYNC_RESULT));
2209 return bSuccess;
2212 /***********************************************************************
2213 * FtpCommandA (WININET.@)
2215 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2216 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2218 BOOL r;
2219 WCHAR *cmdW;
2221 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2222 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2224 if (fExpectResponse)
2226 FIXME("data connection not supported\n");
2227 return FALSE;
2230 if (!lpszCommand || !lpszCommand[0])
2232 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2233 return FALSE;
2236 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2238 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2239 return FALSE;
2242 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2244 HeapFree(GetProcessHeap(), 0, cmdW);
2245 return r;
2248 /***********************************************************************
2249 * FtpCommandW (WININET.@)
2251 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2252 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2254 BOOL r = FALSE;
2255 ftp_session_t *lpwfs;
2256 LPSTR cmd = NULL;
2257 DWORD len, nBytesSent= 0;
2258 INT nResCode, nRC = 0;
2260 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2261 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2263 if (!lpszCommand || !lpszCommand[0])
2265 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2266 return FALSE;
2269 if (fExpectResponse)
2271 FIXME("data connection not supported\n");
2272 return FALSE;
2275 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
2276 if (!lpwfs)
2278 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2279 return FALSE;
2282 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2284 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2285 goto lend;
2288 if (lpwfs->download_in_progress != NULL)
2290 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2291 goto lend;
2294 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2295 if ((cmd = HeapAlloc(GetProcessHeap(), 0, len )))
2296 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2297 else
2299 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2300 goto lend;
2303 strcat(cmd, szCRLF);
2304 len--;
2306 TRACE("Sending (%s) len(%d)\n", cmd, len);
2307 while ((nBytesSent < len) && (nRC != -1))
2309 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2310 if (nRC != -1)
2312 nBytesSent += nRC;
2313 TRACE("Sent %d bytes\n", nRC);
2317 if (nBytesSent)
2319 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2320 if (nResCode > 0 && nResCode < 400)
2321 r = TRUE;
2322 else
2323 FTP_SetResponseError(nResCode);
2326 lend:
2327 WININET_Release( &lpwfs->hdr );
2328 HeapFree(GetProcessHeap(), 0, cmd);
2329 return r;
2333 /***********************************************************************
2334 * FTPSESSION_Destroy (internal)
2336 * Deallocate session handle
2338 static void FTPSESSION_Destroy(object_header_t *hdr)
2340 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2342 TRACE("\n");
2344 WININET_Release(&lpwfs->lpAppInfo->hdr);
2346 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2347 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2348 HeapFree(GetProcessHeap(), 0, lpwfs->servername);
2349 HeapFree(GetProcessHeap(), 0, lpwfs);
2352 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2354 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2356 TRACE("\n");
2358 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2359 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2361 if (lpwfs->download_in_progress != NULL)
2362 lpwfs->download_in_progress->session_deleted = TRUE;
2364 if (lpwfs->sndSocket != -1)
2365 closesocket(lpwfs->sndSocket);
2367 if (lpwfs->lstnSocket != -1)
2368 closesocket(lpwfs->lstnSocket);
2370 if (lpwfs->pasvSocket != -1)
2371 closesocket(lpwfs->pasvSocket);
2373 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2374 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2377 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2379 switch(option) {
2380 case INTERNET_OPTION_HANDLE_TYPE:
2381 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2383 if (*size < sizeof(ULONG))
2384 return ERROR_INSUFFICIENT_BUFFER;
2386 *size = sizeof(DWORD);
2387 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2388 return ERROR_SUCCESS;
2391 return INET_QueryOption(option, buffer, size, unicode);
2394 static const object_vtbl_t FTPSESSIONVtbl = {
2395 FTPSESSION_Destroy,
2396 FTPSESSION_CloseConnection,
2397 FTPSESSION_QueryOption,
2398 NULL,
2399 NULL,
2400 NULL,
2401 NULL,
2402 NULL,
2403 NULL
2407 /***********************************************************************
2408 * FTP_Connect (internal)
2410 * Connect to a ftp server
2412 * RETURNS
2413 * HINTERNET a session handle on success
2414 * NULL on failure
2416 * NOTES:
2418 * Windows uses 'anonymous' as the username, when given a NULL username
2419 * and a NULL password. The password is first looked up in:
2421 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2423 * If this entry is not present it uses the current username as the password.
2427 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2428 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2429 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2430 DWORD dwInternalFlags)
2432 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2433 'M','i','c','r','o','s','o','f','t','\\',
2434 'W','i','n','d','o','w','s','\\',
2435 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2436 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2437 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2438 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2439 static const WCHAR szEmpty[] = {'\0'};
2440 struct sockaddr_in socketAddr;
2441 INT nsocket = -1;
2442 UINT sock_namelen;
2443 BOOL bSuccess = FALSE;
2444 ftp_session_t *lpwfs = NULL;
2445 HINTERNET handle = NULL;
2446 char szaddr[INET_ADDRSTRLEN];
2448 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2449 hIC, debugstr_w(lpszServerName),
2450 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2452 assert( hIC->hdr.htype == WH_HINIT );
2454 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2456 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2457 goto lerror;
2460 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(ftp_session_t));
2461 if (NULL == lpwfs)
2463 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2464 goto lerror;
2467 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2468 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2469 else
2470 lpwfs->serverport = nServerPort;
2472 lpwfs->hdr.htype = WH_HFTPSESSION;
2473 lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
2474 lpwfs->hdr.dwFlags = dwFlags;
2475 lpwfs->hdr.dwContext = dwContext;
2476 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2477 lpwfs->hdr.refs = 1;
2478 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2479 lpwfs->download_in_progress = NULL;
2480 lpwfs->sndSocket = -1;
2481 lpwfs->lstnSocket = -1;
2482 lpwfs->pasvSocket = -1;
2484 WININET_AddRef( &hIC->hdr );
2485 lpwfs->lpAppInfo = hIC;
2486 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2488 handle = WININET_AllocHandle( &lpwfs->hdr );
2489 if( !handle )
2491 ERR("Failed to alloc handle\n");
2492 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2493 goto lerror;
2496 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2497 if(strchrW(hIC->lpszProxy, ' '))
2498 FIXME("Several proxies not implemented.\n");
2499 if(hIC->lpszProxyBypass)
2500 FIXME("Proxy bypass is ignored.\n");
2502 if (!lpszUserName || !strlenW(lpszUserName)) {
2503 HKEY key;
2504 WCHAR szPassword[MAX_PATH];
2505 DWORD len = sizeof(szPassword);
2507 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2509 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2510 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2511 /* Nothing in the registry, get the username and use that as the password */
2512 if (!GetUserNameW(szPassword, &len)) {
2513 /* Should never get here, but use an empty password as failsafe */
2514 strcpyW(szPassword, szEmpty);
2517 RegCloseKey(key);
2519 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2520 lpwfs->lpszPassword = heap_strdupW(szPassword);
2522 else {
2523 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2524 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2526 lpwfs->servername = heap_strdupW(lpszServerName);
2528 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2529 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2531 INTERNET_ASYNC_RESULT iar;
2533 iar.dwResult = (DWORD_PTR)handle;
2534 iar.dwError = ERROR_SUCCESS;
2536 SendAsyncCallback(&hIC->hdr, dwContext,
2537 INTERNET_STATUS_HANDLE_CREATED, &iar,
2538 sizeof(INTERNET_ASYNC_RESULT));
2541 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2542 (LPWSTR) lpszServerName, (strlenW(lpszServerName)+1) * sizeof(WCHAR));
2544 sock_namelen = sizeof(socketAddr);
2545 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen))
2547 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2548 goto lerror;
2551 if (socketAddr.sin_family != AF_INET)
2553 WARN("unsupported address family %d\n", socketAddr.sin_family);
2554 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2555 goto lerror;
2558 inet_ntop(socketAddr.sin_family, &socketAddr.sin_addr, szaddr, sizeof(szaddr));
2559 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2560 szaddr, strlen(szaddr)+1);
2562 nsocket = socket(AF_INET,SOCK_STREAM,0);
2563 if (nsocket == -1)
2565 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2566 goto lerror;
2569 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2570 szaddr, strlen(szaddr)+1);
2572 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2574 ERR("Unable to connect (%s)\n", strerror(errno));
2575 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2576 closesocket(nsocket);
2578 else
2580 TRACE("Connected to server\n");
2581 lpwfs->sndSocket = nsocket;
2582 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2583 szaddr, strlen(szaddr)+1);
2585 sock_namelen = sizeof(lpwfs->socketAddress);
2586 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2588 if (FTP_ConnectToHost(lpwfs))
2590 TRACE("Successfully logged into server\n");
2591 bSuccess = TRUE;
2595 lerror:
2596 if (lpwfs) WININET_Release( &lpwfs->hdr );
2598 if (!bSuccess && handle)
2600 WININET_FreeHandle( handle );
2601 handle = NULL;
2604 return handle;
2608 /***********************************************************************
2609 * FTP_ConnectToHost (internal)
2611 * Connect to a ftp server
2613 * RETURNS
2614 * TRUE on success
2615 * NULL on failure
2618 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2620 INT nResCode;
2621 BOOL bSuccess = FALSE;
2623 TRACE("\n");
2624 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2626 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2627 goto lend;
2629 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2630 if (nResCode)
2632 /* Login successful... */
2633 if (nResCode == 230)
2634 bSuccess = TRUE;
2635 /* User name okay, need password... */
2636 else if (nResCode == 331)
2637 bSuccess = FTP_SendPassword(lpwfs);
2638 /* Need account for login... */
2639 else if (nResCode == 332)
2640 bSuccess = FTP_SendAccount(lpwfs);
2641 else
2642 FTP_SetResponseError(nResCode);
2645 TRACE("Returning %d\n", bSuccess);
2646 lend:
2647 return bSuccess;
2651 /***********************************************************************
2652 * FTP_SendCommandA (internal)
2654 * Send command to server
2656 * RETURNS
2657 * TRUE on success
2658 * NULL on failure
2661 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2662 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2664 DWORD len;
2665 CHAR *buf;
2666 DWORD nBytesSent = 0;
2667 int nRC = 0;
2668 DWORD dwParamLen;
2670 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2672 if (lpfnStatusCB)
2674 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2677 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2678 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2679 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2681 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2682 return FALSE;
2684 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2685 dwParamLen ? lpszParam : "", szCRLF);
2687 TRACE("Sending (%s) len(%d)\n", buf, len);
2688 while((nBytesSent < len) && (nRC != -1))
2690 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2691 nBytesSent += nRC;
2694 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2696 if (lpfnStatusCB)
2698 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2699 &nBytesSent, sizeof(DWORD));
2702 TRACE("Sent %d bytes\n", nBytesSent);
2703 return (nRC != -1);
2706 /***********************************************************************
2707 * FTP_SendCommand (internal)
2709 * Send command to server
2711 * RETURNS
2712 * TRUE on success
2713 * NULL on failure
2716 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2717 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2719 BOOL ret;
2720 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2721 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2722 HeapFree(GetProcessHeap(), 0, lpszParamA);
2723 return ret;
2726 /***********************************************************************
2727 * FTP_ReceiveResponse (internal)
2729 * Receive response from server
2731 * RETURNS
2732 * Reply code on success
2733 * 0 on failure
2736 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2738 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2739 DWORD nRecv;
2740 INT rc = 0;
2741 char firstprefix[5];
2742 BOOL multiline = FALSE;
2744 TRACE("socket(%d)\n", lpwfs->sndSocket);
2746 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2748 while(1)
2750 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2751 goto lerror;
2753 if (nRecv >= 3)
2755 if(!multiline)
2757 if(lpszResponse[3] != '-')
2758 break;
2759 else
2760 { /* Start of multiline response. Loop until we get "nnn " */
2761 multiline = TRUE;
2762 memcpy(firstprefix, lpszResponse, 3);
2763 firstprefix[3] = ' ';
2764 firstprefix[4] = '\0';
2767 else
2769 if(!memcmp(firstprefix, lpszResponse, 4))
2770 break;
2775 if (nRecv >= 3)
2777 rc = atoi(lpszResponse);
2779 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2780 &nRecv, sizeof(DWORD));
2783 lerror:
2784 TRACE("return %d\n", rc);
2785 return rc;
2789 /***********************************************************************
2790 * FTP_SendPassword (internal)
2792 * Send password to ftp server
2794 * RETURNS
2795 * TRUE on success
2796 * NULL on failure
2799 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2801 INT nResCode;
2802 BOOL bSuccess = FALSE;
2804 TRACE("\n");
2805 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2806 goto lend;
2808 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2809 if (nResCode)
2811 TRACE("Received reply code %d\n", nResCode);
2812 /* Login successful... */
2813 if (nResCode == 230)
2814 bSuccess = TRUE;
2815 /* Command not implemented, superfluous at the server site... */
2816 /* Need account for login... */
2817 else if (nResCode == 332)
2818 bSuccess = FTP_SendAccount(lpwfs);
2819 else
2820 FTP_SetResponseError(nResCode);
2823 lend:
2824 TRACE("Returning %d\n", bSuccess);
2825 return bSuccess;
2829 /***********************************************************************
2830 * FTP_SendAccount (internal)
2834 * RETURNS
2835 * TRUE on success
2836 * FALSE on failure
2839 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2841 INT nResCode;
2842 BOOL bSuccess = FALSE;
2844 TRACE("\n");
2845 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2846 goto lend;
2848 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2849 if (nResCode)
2850 bSuccess = TRUE;
2851 else
2852 FTP_SetResponseError(nResCode);
2854 lend:
2855 return bSuccess;
2859 /***********************************************************************
2860 * FTP_SendStore (internal)
2862 * Send request to upload file to ftp server
2864 * RETURNS
2865 * TRUE on success
2866 * FALSE on failure
2869 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2871 INT nResCode;
2872 BOOL bSuccess = FALSE;
2874 TRACE("\n");
2875 if (!FTP_InitListenSocket(lpwfs))
2876 goto lend;
2878 if (!FTP_SendType(lpwfs, dwType))
2879 goto lend;
2881 if (!FTP_SendPortOrPasv(lpwfs))
2882 goto lend;
2884 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2885 goto lend;
2886 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2887 if (nResCode)
2889 if (nResCode == 150 || nResCode == 125)
2890 bSuccess = TRUE;
2891 else
2892 FTP_SetResponseError(nResCode);
2895 lend:
2896 if (!bSuccess && lpwfs->lstnSocket != -1)
2898 closesocket(lpwfs->lstnSocket);
2899 lpwfs->lstnSocket = -1;
2902 return bSuccess;
2906 /***********************************************************************
2907 * FTP_InitListenSocket (internal)
2909 * Create a socket to listen for server response
2911 * RETURNS
2912 * TRUE on success
2913 * FALSE on failure
2916 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2918 BOOL bSuccess = FALSE;
2919 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2921 TRACE("\n");
2923 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2924 if (lpwfs->lstnSocket == -1)
2926 TRACE("Unable to create listening socket\n");
2927 goto lend;
2930 /* We obtain our ip addr from the name of the command channel socket */
2931 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2933 /* and get the system to assign us a port */
2934 lpwfs->lstnSocketAddress.sin_port = htons(0);
2936 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2938 TRACE("Unable to bind socket\n");
2939 goto lend;
2942 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2944 TRACE("listen failed\n");
2945 goto lend;
2948 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2949 bSuccess = TRUE;
2951 lend:
2952 if (!bSuccess && lpwfs->lstnSocket != -1)
2954 closesocket(lpwfs->lstnSocket);
2955 lpwfs->lstnSocket = -1;
2958 return bSuccess;
2962 /***********************************************************************
2963 * FTP_SendType (internal)
2965 * Tell server type of data being transferred
2967 * RETURNS
2968 * TRUE on success
2969 * FALSE on failure
2971 * W98SE doesn't cache the type that's currently set
2972 * (i.e. it sends it always),
2973 * so we probably don't want to do that either.
2975 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2977 INT nResCode;
2978 WCHAR type[] = { 'I','\0' };
2979 BOOL bSuccess = FALSE;
2981 TRACE("\n");
2982 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2983 type[0] = 'A';
2985 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2986 goto lend;
2988 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2989 if (nResCode)
2991 if (nResCode == 2)
2992 bSuccess = TRUE;
2993 else
2994 FTP_SetResponseError(nResCode);
2997 lend:
2998 return bSuccess;
3002 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
3003 /***********************************************************************
3004 * FTP_GetFileSize (internal)
3006 * Retrieves from the server the size of the given file
3008 * RETURNS
3009 * TRUE on success
3010 * FALSE on failure
3013 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
3015 INT nResCode;
3016 BOOL bSuccess = FALSE;
3018 TRACE("\n");
3020 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
3021 goto lend;
3023 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3024 if (nResCode)
3026 if (nResCode == 213) {
3027 /* Now parses the output to get the actual file size */
3028 int i;
3029 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3031 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3032 if (lpszResponseBuffer[i] == '\0') return FALSE;
3033 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3035 bSuccess = TRUE;
3036 } else {
3037 FTP_SetResponseError(nResCode);
3041 lend:
3042 return bSuccess;
3044 #endif
3047 /***********************************************************************
3048 * FTP_SendPort (internal)
3050 * Tell server which port to use
3052 * RETURNS
3053 * TRUE on success
3054 * FALSE on failure
3057 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3059 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3060 INT nResCode;
3061 WCHAR szIPAddress[64];
3062 BOOL bSuccess = FALSE;
3063 TRACE("\n");
3065 sprintfW(szIPAddress, szIPFormat,
3066 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
3067 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
3068 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
3069 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
3070 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3071 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3073 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3074 goto lend;
3076 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3077 if (nResCode)
3079 if (nResCode == 200)
3080 bSuccess = TRUE;
3081 else
3082 FTP_SetResponseError(nResCode);
3085 lend:
3086 return bSuccess;
3090 /***********************************************************************
3091 * FTP_DoPassive (internal)
3093 * Tell server that we want to do passive transfers
3094 * and connect data socket
3096 * RETURNS
3097 * TRUE on success
3098 * FALSE on failure
3101 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3103 INT nResCode;
3104 BOOL bSuccess = FALSE;
3106 TRACE("\n");
3107 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3108 goto lend;
3110 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3111 if (nResCode)
3113 if (nResCode == 227)
3115 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3116 LPSTR p;
3117 int f[6];
3118 int i;
3119 char *pAddr, *pPort;
3120 INT nsocket = -1;
3121 struct sockaddr_in dataSocketAddress;
3123 p = lpszResponseBuffer+4; /* skip status code */
3124 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3126 if (*p == '\0')
3128 ERR("no address found in response, aborting\n");
3129 goto lend;
3132 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3133 &f[4], &f[5]) != 6)
3135 ERR("unknown response address format '%s', aborting\n", p);
3136 goto lend;
3138 for (i=0; i < 6; i++)
3139 f[i] = f[i] & 0xff;
3141 dataSocketAddress = lpwfs->socketAddress;
3142 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3143 pPort = (char *)&(dataSocketAddress.sin_port);
3144 pAddr[0] = f[0];
3145 pAddr[1] = f[1];
3146 pAddr[2] = f[2];
3147 pAddr[3] = f[3];
3148 pPort[0] = f[4];
3149 pPort[1] = f[5];
3151 nsocket = socket(AF_INET,SOCK_STREAM,0);
3152 if (nsocket == -1)
3153 goto lend;
3155 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3157 ERR("can't connect passive FTP data port.\n");
3158 closesocket(nsocket);
3159 goto lend;
3161 lpwfs->pasvSocket = nsocket;
3162 bSuccess = TRUE;
3164 else
3165 FTP_SetResponseError(nResCode);
3168 lend:
3169 return bSuccess;
3173 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3175 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3177 if (!FTP_DoPassive(lpwfs))
3178 return FALSE;
3180 else
3182 if (!FTP_SendPort(lpwfs))
3183 return FALSE;
3185 return TRUE;
3189 /***********************************************************************
3190 * FTP_GetDataSocket (internal)
3192 * Either accepts an incoming data socket connection from the server
3193 * or just returns the already opened socket after a PASV command
3194 * in case of passive FTP.
3197 * RETURNS
3198 * TRUE on success
3199 * FALSE on failure
3202 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3204 struct sockaddr_in saddr;
3205 socklen_t addrlen = sizeof(struct sockaddr);
3207 TRACE("\n");
3208 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3210 *nDataSocket = lpwfs->pasvSocket;
3211 lpwfs->pasvSocket = -1;
3213 else
3215 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3216 closesocket(lpwfs->lstnSocket);
3217 lpwfs->lstnSocket = -1;
3219 return *nDataSocket != -1;
3223 /***********************************************************************
3224 * FTP_SendData (internal)
3226 * Send data to the server
3228 * RETURNS
3229 * TRUE on success
3230 * FALSE on failure
3233 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3235 BY_HANDLE_FILE_INFORMATION fi;
3236 DWORD nBytesRead = 0;
3237 DWORD nBytesSent = 0;
3238 DWORD nTotalSent = 0;
3239 DWORD nBytesToSend, nLen;
3240 int nRC = 1;
3241 time_t s_long_time, e_long_time;
3242 LONG nSeconds;
3243 CHAR *lpszBuffer;
3245 TRACE("\n");
3246 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3248 /* Get the size of the file. */
3249 GetFileInformationByHandle(hFile, &fi);
3250 time(&s_long_time);
3254 nBytesToSend = nBytesRead - nBytesSent;
3256 if (nBytesToSend <= 0)
3258 /* Read data from file. */
3259 nBytesSent = 0;
3260 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3261 ERR("Failed reading from file\n");
3263 if (nBytesRead > 0)
3264 nBytesToSend = nBytesRead;
3265 else
3266 break;
3269 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3270 DATA_PACKET_SIZE : nBytesToSend;
3271 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3273 if (nRC != -1)
3275 nBytesSent += nRC;
3276 nTotalSent += nRC;
3279 /* Do some computation to display the status. */
3280 time(&e_long_time);
3281 nSeconds = e_long_time - s_long_time;
3282 if( nSeconds / 60 > 0 )
3284 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3285 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3286 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3288 else
3290 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3291 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3292 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3294 } while (nRC != -1);
3296 TRACE("file transfer complete!\n");
3298 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3300 return nTotalSent;
3304 /***********************************************************************
3305 * FTP_SendRetrieve (internal)
3307 * Send request to retrieve a file
3309 * RETURNS
3310 * Number of bytes to be received on success
3311 * 0 on failure
3314 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3316 INT nResCode;
3317 BOOL ret;
3319 TRACE("\n");
3320 if (!(ret = FTP_InitListenSocket(lpwfs)))
3321 goto lend;
3323 if (!(ret = FTP_SendType(lpwfs, dwType)))
3324 goto lend;
3326 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3327 goto lend;
3329 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3330 goto lend;
3332 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3333 if ((nResCode != 125) && (nResCode != 150)) {
3334 /* That means that we got an error getting the file. */
3335 FTP_SetResponseError(nResCode);
3336 ret = FALSE;
3339 lend:
3340 if (!ret && lpwfs->lstnSocket != -1)
3342 closesocket(lpwfs->lstnSocket);
3343 lpwfs->lstnSocket = -1;
3346 return ret;
3350 /***********************************************************************
3351 * FTP_RetrieveData (internal)
3353 * Retrieve data from server
3355 * RETURNS
3356 * TRUE on success
3357 * FALSE on failure
3360 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3362 DWORD nBytesWritten;
3363 INT nRC = 0;
3364 CHAR *lpszBuffer;
3366 TRACE("\n");
3368 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3369 if (NULL == lpszBuffer)
3371 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3372 return FALSE;
3375 while (nRC != -1)
3377 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3378 if (nRC != -1)
3380 /* other side closed socket. */
3381 if (nRC == 0)
3382 goto recv_end;
3383 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3387 TRACE("Data transfer complete\n");
3389 recv_end:
3390 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3392 return (nRC != -1);
3395 /***********************************************************************
3396 * FTPFINDNEXT_Destroy (internal)
3398 * Deallocate session handle
3400 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3402 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3403 DWORD i;
3405 TRACE("\n");
3407 WININET_Release(&lpwfn->lpFtpSession->hdr);
3409 for (i = 0; i < lpwfn->size; i++)
3411 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3414 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3415 HeapFree(GetProcessHeap(), 0, lpwfn);
3418 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3420 WIN32_FIND_DATAW *find_data = data;
3421 DWORD res = ERROR_SUCCESS;
3423 TRACE("index(%d) size(%d)\n", find->index, find->size);
3425 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3427 if (find->index < find->size) {
3428 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3429 find->index++;
3431 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3432 }else {
3433 res = ERROR_NO_MORE_FILES;
3436 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3438 INTERNET_ASYNC_RESULT iar;
3440 iar.dwResult = (res == ERROR_SUCCESS);
3441 iar.dwError = res;
3443 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3444 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3445 sizeof(INTERNET_ASYNC_RESULT));
3448 return res;
3451 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3453 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3455 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3458 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3460 switch(option) {
3461 case INTERNET_OPTION_HANDLE_TYPE:
3462 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3464 if (*size < sizeof(ULONG))
3465 return ERROR_INSUFFICIENT_BUFFER;
3467 *size = sizeof(DWORD);
3468 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3469 return ERROR_SUCCESS;
3472 return INET_QueryOption(option, buffer, size, unicode);
3475 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3477 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3479 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3481 WORKREQUEST workRequest;
3482 struct WORKREQ_FTPFINDNEXTW *req;
3484 workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3485 workRequest.hdr = WININET_AddRef( &find->hdr );
3486 req = &workRequest.u.FtpFindNextW;
3487 req->lpFindFileData = data;
3489 INTERNET_AsyncCall(&workRequest);
3491 return ERROR_SUCCESS;
3494 return FTPFINDNEXT_FindNextFileProc(find, data);
3497 static const object_vtbl_t FTPFINDNEXTVtbl = {
3498 FTPFINDNEXT_Destroy,
3499 NULL,
3500 FTPFINDNEXT_QueryOption,
3501 NULL,
3502 NULL,
3503 NULL,
3504 NULL,
3505 NULL,
3506 NULL,
3507 FTPFINDNEXT_FindNextFileW
3510 /***********************************************************************
3511 * FTP_ReceiveFileList (internal)
3513 * Read file list from server
3515 * RETURNS
3516 * Handle to file list on success
3517 * NULL on failure
3520 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3521 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3523 DWORD dwSize = 0;
3524 LPFILEPROPERTIESW lpafp = NULL;
3525 LPWININETFTPFINDNEXTW lpwfn = NULL;
3526 HINTERNET handle = 0;
3528 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3530 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3532 if(lpFindFileData)
3533 FTP_ConvertFileProp(lpafp, lpFindFileData);
3535 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3536 if (lpwfn)
3538 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3539 lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3540 lpwfn->hdr.dwContext = dwContext;
3541 lpwfn->hdr.refs = 1;
3542 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3543 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3544 lpwfn->size = dwSize;
3545 lpwfn->lpafp = lpafp;
3547 WININET_AddRef( &lpwfs->hdr );
3548 lpwfn->lpFtpSession = lpwfs;
3549 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3551 handle = WININET_AllocHandle( &lpwfn->hdr );
3555 if( lpwfn )
3556 WININET_Release( &lpwfn->hdr );
3558 TRACE("Matched %d files\n", dwSize);
3559 return handle;
3563 /***********************************************************************
3564 * FTP_ConvertFileProp (internal)
3566 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3568 * RETURNS
3569 * TRUE on success
3570 * FALSE on failure
3573 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3575 BOOL bSuccess = FALSE;
3577 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3579 if (lpafp)
3581 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3582 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3583 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3585 /* Not all fields are filled in */
3586 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3587 lpFindFileData->nFileSizeLow = lpafp->nSize;
3589 if (lpafp->bIsDirectory)
3590 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3592 if (lpafp->lpszName)
3593 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3595 bSuccess = TRUE;
3598 return bSuccess;
3601 /***********************************************************************
3602 * FTP_ParseNextFile (internal)
3604 * Parse the next line in file listing
3606 * RETURNS
3607 * TRUE on success
3608 * FALSE on failure
3610 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3612 static const char szSpace[] = " \t";
3613 DWORD nBufLen;
3614 char *pszLine;
3615 char *pszToken;
3616 char *pszTmp;
3617 BOOL found = FALSE;
3618 int i;
3620 lpfp->lpszName = NULL;
3621 do {
3622 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3623 return FALSE;
3625 pszToken = strtok(pszLine, szSpace);
3626 /* ls format
3627 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3629 * For instance:
3630 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3632 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3633 if(!FTP_ParsePermission(pszToken, lpfp))
3634 lpfp->bIsDirectory = FALSE;
3635 for(i=0; i<=3; i++) {
3636 if(!(pszToken = strtok(NULL, szSpace)))
3637 break;
3639 if(!pszToken) continue;
3640 if(lpfp->bIsDirectory) {
3641 TRACE("Is directory\n");
3642 lpfp->nSize = 0;
3644 else {
3645 TRACE("Size: %s\n", pszToken);
3646 lpfp->nSize = atol(pszToken);
3649 lpfp->tmLastModified.wSecond = 0;
3650 lpfp->tmLastModified.wMinute = 0;
3651 lpfp->tmLastModified.wHour = 0;
3652 lpfp->tmLastModified.wDay = 0;
3653 lpfp->tmLastModified.wMonth = 0;
3654 lpfp->tmLastModified.wYear = 0;
3656 /* Determine month */
3657 pszToken = strtok(NULL, szSpace);
3658 if(!pszToken) continue;
3659 if(strlen(pszToken) >= 3) {
3660 pszToken[3] = 0;
3661 if((pszTmp = StrStrIA(szMonths, pszToken)))
3662 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3664 /* Determine day */
3665 pszToken = strtok(NULL, szSpace);
3666 if(!pszToken) continue;
3667 lpfp->tmLastModified.wDay = atoi(pszToken);
3668 /* Determine time or year */
3669 pszToken = strtok(NULL, szSpace);
3670 if(!pszToken) continue;
3671 if((pszTmp = strchr(pszToken, ':'))) {
3672 SYSTEMTIME curr_time;
3673 *pszTmp = 0;
3674 pszTmp++;
3675 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3676 lpfp->tmLastModified.wHour = atoi(pszToken);
3677 GetLocalTime( &curr_time );
3678 lpfp->tmLastModified.wYear = curr_time.wYear;
3680 else {
3681 lpfp->tmLastModified.wYear = atoi(pszToken);
3682 lpfp->tmLastModified.wHour = 12;
3684 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3685 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3686 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3688 pszToken = strtok(NULL, szSpace);
3689 if(!pszToken) continue;
3690 lpfp->lpszName = heap_strdupAtoW(pszToken);
3691 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3693 /* NT way of parsing ... :
3695 07-13-03 08:55PM <DIR> sakpatch
3696 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3698 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3699 int mon, mday, year, hour, min;
3700 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3702 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3703 lpfp->tmLastModified.wDay = mday;
3704 lpfp->tmLastModified.wMonth = mon;
3705 lpfp->tmLastModified.wYear = year;
3707 /* Hacky and bad Y2K protection :-) */
3708 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3710 pszToken = strtok(NULL, szSpace);
3711 if(!pszToken) continue;
3712 sscanf(pszToken, "%d:%d", &hour, &min);
3713 lpfp->tmLastModified.wHour = hour;
3714 lpfp->tmLastModified.wMinute = min;
3715 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3716 lpfp->tmLastModified.wHour += 12;
3718 lpfp->tmLastModified.wSecond = 0;
3720 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3721 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3722 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3724 pszToken = strtok(NULL, szSpace);
3725 if(!pszToken) continue;
3726 if(!strcasecmp(pszToken, "<DIR>")) {
3727 lpfp->bIsDirectory = TRUE;
3728 lpfp->nSize = 0;
3729 TRACE("Is directory\n");
3731 else {
3732 lpfp->bIsDirectory = FALSE;
3733 lpfp->nSize = atol(pszToken);
3734 TRACE("Size: %d\n", lpfp->nSize);
3737 pszToken = strtok(NULL, szSpace);
3738 if(!pszToken) continue;
3739 lpfp->lpszName = heap_strdupAtoW(pszToken);
3740 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3742 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3743 else if(pszToken[0] == '+') {
3744 FIXME("EPLF Format not implemented\n");
3747 if(lpfp->lpszName) {
3748 if((lpszSearchFile == NULL) ||
3749 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3750 found = TRUE;
3751 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3753 else {
3754 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3755 lpfp->lpszName = NULL;
3758 } while(!found);
3759 return TRUE;
3762 /***********************************************************************
3763 * FTP_ParseDirectory (internal)
3765 * Parse string of directory information
3767 * RETURNS
3768 * TRUE on success
3769 * FALSE on failure
3771 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3772 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3774 BOOL bSuccess = TRUE;
3775 INT sizeFilePropArray = 500;/*20; */
3776 INT indexFilePropArray = -1;
3778 TRACE("\n");
3780 /* Allocate initial file properties array */
3781 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3782 if (!*lpafp)
3783 return FALSE;
3785 do {
3786 if (indexFilePropArray+1 >= sizeFilePropArray)
3788 LPFILEPROPERTIESW tmpafp;
3790 sizeFilePropArray *= 2;
3791 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3792 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3793 if (NULL == tmpafp)
3795 bSuccess = FALSE;
3796 break;
3799 *lpafp = tmpafp;
3801 indexFilePropArray++;
3802 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3804 if (bSuccess && indexFilePropArray)
3806 if (indexFilePropArray < sizeFilePropArray - 1)
3808 LPFILEPROPERTIESW tmpafp;
3810 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3811 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3812 if (NULL != tmpafp)
3813 *lpafp = tmpafp;
3815 *dwfp = indexFilePropArray;
3817 else
3819 HeapFree(GetProcessHeap(), 0, *lpafp);
3820 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3821 bSuccess = FALSE;
3824 return bSuccess;
3828 /***********************************************************************
3829 * FTP_ParsePermission (internal)
3831 * Parse permission string of directory information
3833 * RETURNS
3834 * TRUE on success
3835 * FALSE on failure
3838 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3840 BOOL bSuccess = TRUE;
3841 unsigned short nPermission = 0;
3842 INT nPos = 1;
3843 INT nLast = 9;
3845 TRACE("\n");
3846 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3848 bSuccess = FALSE;
3849 return bSuccess;
3852 lpfp->bIsDirectory = (*lpszPermission == 'd');
3855 switch (nPos)
3857 case 1:
3858 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3859 break;
3860 case 2:
3861 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3862 break;
3863 case 3:
3864 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3865 break;
3866 case 4:
3867 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3868 break;
3869 case 5:
3870 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3871 break;
3872 case 6:
3873 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3874 break;
3875 case 7:
3876 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3877 break;
3878 case 8:
3879 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3880 break;
3881 case 9:
3882 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3883 break;
3885 nPos++;
3886 }while (nPos <= nLast);
3888 lpfp->permissions = nPermission;
3889 return bSuccess;
3893 /***********************************************************************
3894 * FTP_SetResponseError (internal)
3896 * Set the appropriate error code for a given response from the server
3898 * RETURNS
3901 static DWORD FTP_SetResponseError(DWORD dwResponse)
3903 DWORD dwCode = 0;
3905 switch(dwResponse)
3907 case 425: /* Cannot open data connection. */
3908 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3909 break;
3911 case 426: /* Connection closed, transer aborted. */
3912 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3913 break;
3915 case 530: /* Not logged in. Login incorrect. */
3916 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3917 break;
3919 case 421: /* Service not available - Server may be shutting down. */
3920 case 450: /* File action not taken. File may be busy. */
3921 case 451: /* Action aborted. Server error. */
3922 case 452: /* Action not taken. Insufficient storage space on server. */
3923 case 500: /* Syntax error. Command unrecognized. */
3924 case 501: /* Syntax error. Error in parameters or arguments. */
3925 case 502: /* Command not implemented. */
3926 case 503: /* Bad sequence of commands. */
3927 case 504: /* Command not implemented for that parameter. */
3928 case 532: /* Need account for storing files */
3929 case 550: /* File action not taken. File not found or no access. */
3930 case 551: /* Requested action aborted. Page type unknown */
3931 case 552: /* Action aborted. Exceeded storage allocation */
3932 case 553: /* Action not taken. File name not allowed. */
3934 default:
3935 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3936 break;
3939 INTERNET_SetLastError(dwCode);
3940 return dwCode;