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
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
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
42 #include <sys/types.h>
43 #ifdef HAVE_SYS_SOCKET_H
44 # include <sys/socket.h>
46 #ifdef HAVE_ARPA_INET_H
47 # include <arpa/inet.h>
52 #ifdef HAVE_SYS_IOCTL_H
53 # include <sys/ioctl.h>
69 #include "wine/debug.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
74 typedef struct _ftp_session_t ftp_session_t;
79 ftp_session_t *lpFtpSession;
83 HANDLE cache_file_handle;
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;
97 INTERNET_PORT serverport;
107 SYSTEMTIME tmLastModified;
108 unsigned short permissions;
109 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
114 ftp_session_t *lpFtpSession;
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
130 /* FTP commands with arguments. */
146 /* FTP commands without arguments. */
155 static const CHAR *const szFtpCommands[] = {
178 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
179 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
181 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
182 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext);
183 static BOOL FTP_SendStore(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
184 static BOOL FTP_GetDataSocket(ftp_session_t*, LPINT nDataSocket);
185 static BOOL FTP_SendData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
186 static INT FTP_ReceiveResponse(ftp_session_t*, DWORD_PTR dwContext);
187 static BOOL FTP_SendRetrieve(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
188 static BOOL FTP_RetrieveFileData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
189 static BOOL FTP_InitListenSocket(ftp_session_t*);
190 static BOOL FTP_ConnectToHost(ftp_session_t*);
191 static BOOL FTP_SendPassword(ftp_session_t*);
192 static BOOL FTP_SendAccount(ftp_session_t*);
193 static BOOL FTP_SendType(ftp_session_t*, DWORD dwType);
194 static BOOL FTP_SendPort(ftp_session_t*);
195 static BOOL FTP_DoPassive(ftp_session_t*);
196 static BOOL FTP_SendPortOrPasv(ftp_session_t*);
197 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
198 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
199 static BOOL FTP_ParseDirectory(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
200 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
201 static HINTERNET FTP_ReceiveFileList(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
202 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
203 static DWORD FTP_SetResponseError(DWORD dwResponse);
204 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
205 static BOOL FTP_FtpPutFileW(ftp_session_t*, LPCWSTR lpszLocalFile,
206 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
207 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
208 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
209 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t*,
210 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
211 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t*, LPWSTR lpszCurrentDirectory,
212 LPDWORD lpdwCurrentDirectory);
213 static BOOL FTP_FtpRenameFileW(ftp_session_t*, LPCWSTR lpszSrc, LPCWSTR lpszDest);
214 static BOOL FTP_FtpRemoveDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
215 static BOOL FTP_FtpDeleteFileW(ftp_session_t*, LPCWSTR lpszFileName);
216 static BOOL FTP_FtpGetFileW(ftp_session_t*, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
217 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
218 DWORD_PTR dwContext);
220 /* A temporary helper until we get rid of INTERNET_GetLastError calls */
221 static BOOL res_to_le(DWORD res)
223 if(res != ERROR_SUCCESS)
224 INTERNET_SetLastError(res);
225 return res == ERROR_SUCCESS;
228 /***********************************************************************
229 * FtpPutFileA (WININET.@)
231 * Uploads a file to the FTP server
238 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
239 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
241 LPWSTR lpwzLocalFile;
242 LPWSTR lpwzNewRemoteFile;
245 lpwzLocalFile = heap_strdupAtoW(lpszLocalFile);
246 lpwzNewRemoteFile = heap_strdupAtoW(lpszNewRemoteFile);
247 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
249 heap_free(lpwzLocalFile);
250 heap_free(lpwzNewRemoteFile);
254 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
256 struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
257 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
259 TRACE("%p\n", lpwfs);
261 FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
262 req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
264 heap_free(req->lpszLocalFile);
265 heap_free(req->lpszNewRemoteFile);
268 /***********************************************************************
269 * FtpPutFileW (WININET.@)
271 * Uploads a file to the FTP server
278 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
279 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
281 ftp_session_t *lpwfs;
282 appinfo_t *hIC = NULL;
285 if (!lpszLocalFile || !lpszNewRemoteFile)
287 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
291 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
294 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
298 if (WH_HFTPSESSION != lpwfs->hdr.htype)
300 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
304 if (lpwfs->download_in_progress != NULL)
306 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
310 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
312 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
316 hIC = lpwfs->lpAppInfo;
317 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
319 WORKREQUEST *task = alloc_async_task(&lpwfs->hdr, AsyncFtpPutFileProc, sizeof(*task));
320 struct WORKREQ_FTPPUTFILEW *req = &task->u.FtpPutFileW;
322 req->lpszLocalFile = heap_strdupW(lpszLocalFile);
323 req->lpszNewRemoteFile = heap_strdupW(lpszNewRemoteFile);
324 req->dwFlags = dwFlags;
325 req->dwContext = dwContext;
327 r = res_to_le(INTERNET_AsyncCall(task));
331 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
332 lpszNewRemoteFile, dwFlags, dwContext);
336 WININET_Release( &lpwfs->hdr );
341 /***********************************************************************
342 * FTP_FtpPutFileW (Internal)
344 * Uploads a file to the FTP server
351 static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
352 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
355 BOOL bSuccess = FALSE;
356 appinfo_t *hIC = NULL;
359 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
361 /* Clear any error information */
362 INTERNET_SetLastError(0);
364 /* Open file to be uploaded */
365 if (INVALID_HANDLE_VALUE ==
366 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
367 /* Let CreateFile set the appropriate error */
370 hIC = lpwfs->lpAppInfo;
372 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
374 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
378 /* Get data socket to server */
379 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
381 FTP_SendData(lpwfs, nDataSocket, hFile);
382 closesocket(nDataSocket);
383 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
389 FTP_SetResponseError(nResCode);
394 if (lpwfs->lstnSocket != -1)
396 closesocket(lpwfs->lstnSocket);
397 lpwfs->lstnSocket = -1;
400 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
402 INTERNET_ASYNC_RESULT iar;
404 iar.dwResult = (DWORD)bSuccess;
405 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
406 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
407 &iar, sizeof(INTERNET_ASYNC_RESULT));
416 /***********************************************************************
417 * FtpSetCurrentDirectoryA (WININET.@)
419 * Change the working directory on the FTP server
426 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
428 LPWSTR lpwzDirectory;
431 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
432 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
433 heap_free(lpwzDirectory);
438 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
440 struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
441 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
443 TRACE("%p\n", lpwfs);
445 FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
446 heap_free(req->lpszDirectory);
449 /***********************************************************************
450 * FtpSetCurrentDirectoryW (WININET.@)
452 * Change the working directory on the FTP server
459 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
461 ftp_session_t *lpwfs = NULL;
462 appinfo_t *hIC = NULL;
467 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
471 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
472 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
474 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
478 if (lpwfs->download_in_progress != NULL)
480 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
484 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
486 hIC = lpwfs->lpAppInfo;
487 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
490 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
492 task = alloc_async_task(&lpwfs->hdr, AsyncFtpSetCurrentDirectoryProc, sizeof(*task));
493 req = &task->u.FtpSetCurrentDirectoryW;
494 req->lpszDirectory = heap_strdupW(lpszDirectory);
496 r = res_to_le(INTERNET_AsyncCall(task));
500 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
505 WININET_Release( &lpwfs->hdr );
511 /***********************************************************************
512 * FTP_FtpSetCurrentDirectoryW (Internal)
514 * Change the working directory on the FTP server
521 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
524 appinfo_t *hIC = NULL;
525 DWORD bSuccess = FALSE;
527 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
529 /* Clear any error information */
530 INTERNET_SetLastError(0);
532 hIC = lpwfs->lpAppInfo;
533 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
534 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
537 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
544 FTP_SetResponseError(nResCode);
548 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
550 INTERNET_ASYNC_RESULT iar;
552 iar.dwResult = bSuccess;
553 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
554 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
555 &iar, sizeof(INTERNET_ASYNC_RESULT));
561 /***********************************************************************
562 * FtpCreateDirectoryA (WININET.@)
564 * Create new directory on the FTP server
571 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
573 LPWSTR lpwzDirectory;
576 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
577 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
578 heap_free(lpwzDirectory);
583 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
585 struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
586 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
588 TRACE(" %p\n", lpwfs);
590 FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
591 heap_free(req->lpszDirectory);
594 /***********************************************************************
595 * FtpCreateDirectoryW (WININET.@)
597 * Create new directory on the FTP server
604 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
606 ftp_session_t *lpwfs;
607 appinfo_t *hIC = NULL;
610 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
613 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
617 if (WH_HFTPSESSION != lpwfs->hdr.htype)
619 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
623 if (lpwfs->download_in_progress != NULL)
625 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
631 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
635 hIC = lpwfs->lpAppInfo;
636 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
639 struct WORKREQ_FTPCREATEDIRECTORYW *req;
641 task = alloc_async_task(&lpwfs->hdr, AsyncFtpCreateDirectoryProc, sizeof(*task));
642 req = &task->u.FtpCreateDirectoryW;
643 req->lpszDirectory = heap_strdupW(lpszDirectory);
645 r = res_to_le(INTERNET_AsyncCall(task));
649 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
652 WININET_Release( &lpwfs->hdr );
658 /***********************************************************************
659 * FTP_FtpCreateDirectoryW (Internal)
661 * Create new directory on the FTP server
668 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
671 BOOL bSuccess = FALSE;
672 appinfo_t *hIC = NULL;
674 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
676 /* Clear any error information */
677 INTERNET_SetLastError(0);
679 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
682 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
688 FTP_SetResponseError(nResCode);
692 hIC = lpwfs->lpAppInfo;
693 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
695 INTERNET_ASYNC_RESULT iar;
697 iar.dwResult = (DWORD)bSuccess;
698 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
699 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
700 &iar, sizeof(INTERNET_ASYNC_RESULT));
706 /***********************************************************************
707 * FtpFindFirstFileA (WININET.@)
709 * Search the specified directory
712 * HINTERNET on success
716 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
717 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
719 LPWSTR lpwzSearchFile;
720 WIN32_FIND_DATAW wfd;
721 LPWIN32_FIND_DATAW lpFindFileDataW;
724 lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
725 lpFindFileDataW = lpFindFileData?&wfd:NULL;
726 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
727 heap_free(lpwzSearchFile);
729 if (ret && lpFindFileData)
730 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
736 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
738 struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
739 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
741 TRACE("%p\n", lpwfs);
743 FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
744 req->lpFindFileData, req->dwFlags, req->dwContext);
745 heap_free(req->lpszSearchFile);
748 /***********************************************************************
749 * FtpFindFirstFileW (WININET.@)
751 * Search the specified directory
754 * HINTERNET on success
758 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
759 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
761 ftp_session_t *lpwfs;
762 appinfo_t *hIC = NULL;
765 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
766 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
768 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
772 if (lpwfs->download_in_progress != NULL)
774 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
778 hIC = lpwfs->lpAppInfo;
779 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
782 struct WORKREQ_FTPFINDFIRSTFILEW *req;
784 task = alloc_async_task(&lpwfs->hdr, AsyncFtpFindFirstFileProc, sizeof(*task));
785 req = &task->u.FtpFindFirstFileW;
786 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : heap_strdupW(lpszSearchFile);
787 req->lpFindFileData = lpFindFileData;
788 req->dwFlags = dwFlags;
789 req->dwContext= dwContext;
791 INTERNET_AsyncCall(task);
796 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
801 WININET_Release( &lpwfs->hdr );
807 /***********************************************************************
808 * FTP_FtpFindFirstFileW (Internal)
810 * Search the specified directory
813 * HINTERNET on success
817 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
818 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
821 appinfo_t *hIC = NULL;
822 HINTERNET hFindNext = NULL;
826 /* Clear any error information */
827 INTERNET_SetLastError(0);
829 if (!FTP_InitListenSocket(lpwfs))
832 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
835 if (!FTP_SendPortOrPasv(lpwfs))
838 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
839 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
842 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
845 if (nResCode == 125 || nResCode == 150)
849 /* Get data socket to server */
850 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
852 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
853 closesocket(nDataSocket);
854 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
855 if (nResCode != 226 && nResCode != 250)
856 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
860 FTP_SetResponseError(nResCode);
864 if (lpwfs->lstnSocket != -1)
866 closesocket(lpwfs->lstnSocket);
867 lpwfs->lstnSocket = -1;
870 hIC = lpwfs->lpAppInfo;
871 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
873 INTERNET_ASYNC_RESULT iar;
877 iar.dwResult = (DWORD_PTR)hFindNext;
878 iar.dwError = ERROR_SUCCESS;
879 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
880 &iar, sizeof(INTERNET_ASYNC_RESULT));
883 iar.dwResult = (DWORD_PTR)hFindNext;
884 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
885 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
886 &iar, sizeof(INTERNET_ASYNC_RESULT));
893 /***********************************************************************
894 * FtpGetCurrentDirectoryA (WININET.@)
896 * Retrieves the current directory
903 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
904 LPDWORD lpdwCurrentDirectory)
910 if(lpdwCurrentDirectory) {
911 len = *lpdwCurrentDirectory;
912 if(lpszCurrentDirectory)
914 dir = heap_alloc(len * sizeof(WCHAR));
917 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
922 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
924 if (ret && lpszCurrentDirectory)
925 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
927 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
933 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
935 struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
936 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
938 TRACE("%p\n", lpwfs);
940 FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
943 /***********************************************************************
944 * FtpGetCurrentDirectoryW (WININET.@)
946 * Retrieves the current directory
953 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
954 LPDWORD lpdwCurrentDirectory)
956 ftp_session_t *lpwfs;
957 appinfo_t *hIC = NULL;
960 TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
962 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
965 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
969 if (WH_HFTPSESSION != lpwfs->hdr.htype)
971 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
975 if (!lpdwCurrentDirectory)
977 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
981 if (lpszCurrentDirectory == NULL)
983 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
987 if (lpwfs->download_in_progress != NULL)
989 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
993 hIC = lpwfs->lpAppInfo;
994 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
997 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
999 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetCurrentDirectoryProc, sizeof(*task));
1000 req = &task->u.FtpGetCurrentDirectoryW;
1001 req->lpszDirectory = lpszCurrentDirectory;
1002 req->lpdwDirectory = lpdwCurrentDirectory;
1004 r = res_to_le(INTERNET_AsyncCall(task));
1008 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
1009 lpdwCurrentDirectory);
1014 WININET_Release( &lpwfs->hdr );
1020 /***********************************************************************
1021 * FTP_FtpGetCurrentDirectoryW (Internal)
1023 * Retrieves the current directory
1030 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
1031 LPDWORD lpdwCurrentDirectory)
1034 appinfo_t *hIC = NULL;
1035 DWORD bSuccess = FALSE;
1037 /* Clear any error information */
1038 INTERNET_SetLastError(0);
1040 hIC = lpwfs->lpAppInfo;
1041 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1042 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1045 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1048 if (nResCode == 257) /* Extract directory name */
1050 DWORD firstpos, lastpos, len;
1051 LPWSTR lpszResponseBuffer = heap_strdupAtoW(INTERNET_GetResponseBuffer());
1053 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1055 if ('"' == lpszResponseBuffer[lastpos])
1063 len = lastpos - firstpos;
1064 if (*lpdwCurrentDirectory >= len)
1066 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1067 lpszCurrentDirectory[len - 1] = 0;
1068 *lpdwCurrentDirectory = len;
1071 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1073 heap_free(lpszResponseBuffer);
1076 FTP_SetResponseError(nResCode);
1080 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1082 INTERNET_ASYNC_RESULT iar;
1084 iar.dwResult = bSuccess;
1085 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1086 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1087 &iar, sizeof(INTERNET_ASYNC_RESULT));
1094 /***********************************************************************
1095 * FTPFILE_Destroy(internal)
1097 * Closes the file transfer handle. This also 'cleans' the data queue of
1098 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1101 static void FTPFILE_Destroy(object_header_t *hdr)
1103 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1104 ftp_session_t *lpwfs = lpwh->lpFtpSession;
1109 if (lpwh->cache_file_handle != INVALID_HANDLE_VALUE)
1110 CloseHandle(lpwh->cache_file_handle);
1112 heap_free(lpwh->cache_file);
1114 if (!lpwh->session_deleted)
1115 lpwfs->download_in_progress = NULL;
1117 if (lpwh->nDataSocket != -1)
1118 closesocket(lpwh->nDataSocket);
1120 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1121 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1123 WININET_Release(&lpwh->lpFtpSession->hdr);
1126 static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1129 case INTERNET_OPTION_HANDLE_TYPE:
1130 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1132 if (*size < sizeof(ULONG))
1133 return ERROR_INSUFFICIENT_BUFFER;
1135 *size = sizeof(DWORD);
1136 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1137 return ERROR_SUCCESS;
1138 case INTERNET_OPTION_DATAFILE_NAME:
1141 ftp_file_t *file = (ftp_file_t *)hdr;
1143 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1145 if (!file->cache_file)
1148 return ERROR_INTERNET_ITEM_NOT_FOUND;
1152 required = (lstrlenW(file->cache_file) + 1) * sizeof(WCHAR);
1153 if (*size < required)
1154 return ERROR_INSUFFICIENT_BUFFER;
1157 memcpy(buffer, file->cache_file, *size);
1158 return ERROR_SUCCESS;
1162 required = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, NULL, 0, NULL, NULL);
1163 if (required > *size)
1164 return ERROR_INSUFFICIENT_BUFFER;
1166 *size = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, buffer, *size, NULL, NULL);
1167 return ERROR_SUCCESS;
1171 return INET_QueryOption(hdr, option, buffer, size, unicode);
1174 static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
1176 ftp_file_t *file = (ftp_file_t*)hdr;
1180 if (file->nDataSocket == -1)
1181 return ERROR_INTERNET_DISCONNECTED;
1183 /* FIXME: FTP should use NETCON_ stuff */
1184 res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1185 *read = res>0 ? res : 0;
1187 error = res >= 0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME */
1188 if (error == ERROR_SUCCESS && file->cache_file)
1190 DWORD bytes_written;
1192 if (!WriteFile(file->cache_file_handle, buffer, *read, &bytes_written, NULL))
1193 WARN("WriteFile failed: %u\n", GetLastError());
1198 static DWORD FTPFILE_ReadFileEx(object_header_t *hdr, void *buf, DWORD size, DWORD *ret_size,
1199 DWORD flags, DWORD_PTR context)
1201 return FTPFILE_ReadFile(hdr, buf, size, ret_size);
1204 static DWORD FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1206 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1209 res = send(lpwh->nDataSocket, buffer, size, 0);
1211 *written = res>0 ? res : 0;
1212 return res >= 0 ? ERROR_SUCCESS : sock_get_error(errno);
1215 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
1217 INTERNET_ASYNC_RESULT iar;
1221 TRACE("%p\n", file);
1223 available = recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1225 if(available != -1) {
1226 iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1227 iar.dwError = first_notif ? 0 : available;
1230 iar.dwError = INTERNET_GetLastError();
1233 INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1234 sizeof(INTERNET_ASYNC_RESULT));
1237 static void FTPFILE_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
1239 ftp_file_t *file = (ftp_file_t*)workRequest->hdr;
1241 FTP_ReceiveRequestData(file, FALSE);
1244 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1246 ftp_file_t *file = (ftp_file_t*) hdr;
1247 int retval, unread = 0;
1249 TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
1252 retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
1254 TRACE("%d bytes of queued, but unread data\n", unread);
1256 FIXME("FIONREAD not available\n");
1259 *available = unread;
1266 retval = recv(file->nDataSocket, &byte, 1, MSG_PEEK);
1268 task_header_t *task;
1270 task = alloc_async_task(&file->hdr, FTPFILE_AsyncQueryDataAvailableProc, sizeof(*task));
1271 INTERNET_AsyncCall(task);
1274 return ERROR_IO_PENDING;
1278 return ERROR_SUCCESS;
1282 static const object_vtbl_t FTPFILEVtbl = {
1285 FTPFILE_QueryOption,
1290 FTPFILE_QueryDataAvailable,
1294 /***********************************************************************
1295 * FTP_FtpOpenFileW (Internal)
1297 * Open a remote file for writing or reading
1300 * HINTERNET handle on success
1304 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1305 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1306 DWORD_PTR dwContext)
1309 BOOL bSuccess = FALSE;
1310 ftp_file_t *lpwh = NULL;
1311 appinfo_t *hIC = NULL;
1315 /* Clear any error information */
1316 INTERNET_SetLastError(0);
1318 if (GENERIC_READ == fdwAccess)
1320 /* Set up socket to retrieve data */
1321 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1323 else if (GENERIC_WRITE == fdwAccess)
1325 /* Set up socket to send data */
1326 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1329 /* Get data socket to server */
1330 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1332 lpwh = alloc_object(&lpwfs->hdr, &FTPFILEVtbl, sizeof(ftp_file_t));
1333 lpwh->hdr.htype = WH_HFILE;
1334 lpwh->hdr.dwFlags = dwFlags;
1335 lpwh->hdr.dwContext = dwContext;
1336 lpwh->nDataSocket = nDataSocket;
1337 lpwh->cache_file = NULL;
1338 lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1339 lpwh->session_deleted = FALSE;
1341 WININET_AddRef( &lpwfs->hdr );
1342 lpwh->lpFtpSession = lpwfs;
1343 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1345 /* Indicate that a download is currently in progress */
1346 lpwfs->download_in_progress = lpwh;
1349 if (lpwfs->lstnSocket != -1)
1351 closesocket(lpwfs->lstnSocket);
1352 lpwfs->lstnSocket = -1;
1355 if (bSuccess && fdwAccess == GENERIC_READ)
1357 WCHAR filename[MAX_PATH + 1];
1361 memset(&uc, 0, sizeof(uc));
1362 uc.dwStructSize = sizeof(uc);
1363 uc.nScheme = INTERNET_SCHEME_FTP;
1364 uc.lpszHostName = lpwfs->servername;
1365 uc.nPort = lpwfs->serverport;
1366 uc.lpszUserName = lpwfs->lpszUserName;
1367 uc.lpszUrlPath = heap_strdupW(lpszFileName);
1369 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1371 WCHAR *url = heap_alloc(len * sizeof(WCHAR));
1373 if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1375 lpwh->cache_file = heap_strdupW(filename);
1376 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1377 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1378 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1380 WARN("Could not create cache file: %u\n", GetLastError());
1381 heap_free(lpwh->cache_file);
1382 lpwh->cache_file = NULL;
1387 heap_free(uc.lpszUrlPath);
1390 hIC = lpwfs->lpAppInfo;
1391 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1393 INTERNET_ASYNC_RESULT iar;
1397 iar.dwResult = (DWORD_PTR)lpwh->hdr.hInternet;
1398 iar.dwError = ERROR_SUCCESS;
1399 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1400 &iar, sizeof(INTERNET_ASYNC_RESULT));
1404 FTP_ReceiveRequestData(lpwh, TRUE);
1407 iar.dwError = INTERNET_GetLastError();
1408 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1409 &iar, sizeof(INTERNET_ASYNC_RESULT));
1416 return lpwh->hdr.hInternet;
1420 /***********************************************************************
1421 * FtpOpenFileA (WININET.@)
1423 * Open a remote file for writing or reading
1426 * HINTERNET handle on success
1430 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1431 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1432 DWORD_PTR dwContext)
1434 LPWSTR lpwzFileName;
1437 lpwzFileName = heap_strdupAtoW(lpszFileName);
1438 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1439 heap_free(lpwzFileName);
1444 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1446 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1447 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1449 TRACE("%p\n", lpwfs);
1451 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1452 req->dwAccess, req->dwFlags, req->dwContext);
1453 heap_free(req->lpszFilename);
1456 /***********************************************************************
1457 * FtpOpenFileW (WININET.@)
1459 * Open a remote file for writing or reading
1462 * HINTERNET handle on success
1466 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1467 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1468 DWORD_PTR dwContext)
1470 ftp_session_t *lpwfs;
1471 appinfo_t *hIC = NULL;
1474 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1475 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1477 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1480 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1484 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1486 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1490 if ((!lpszFileName) ||
1491 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1492 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1494 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1498 if (lpwfs->download_in_progress != NULL)
1500 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1504 hIC = lpwfs->lpAppInfo;
1505 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1508 struct WORKREQ_FTPOPENFILEW *req;
1510 task = alloc_async_task(&lpwfs->hdr, AsyncFtpOpenFileProc, sizeof(*task));
1511 req = &task->u.FtpOpenFileW;
1512 req->lpszFilename = heap_strdupW(lpszFileName);
1513 req->dwAccess = fdwAccess;
1514 req->dwFlags = dwFlags;
1515 req->dwContext = dwContext;
1517 INTERNET_AsyncCall(task);
1522 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1526 WININET_Release( &lpwfs->hdr );
1532 /***********************************************************************
1533 * FtpGetFileA (WININET.@)
1535 * Retrieve file from the FTP server
1542 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1543 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1544 DWORD_PTR dwContext)
1546 LPWSTR lpwzRemoteFile;
1550 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1551 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1552 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1553 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1554 heap_free(lpwzRemoteFile);
1555 heap_free(lpwzNewFile);
1560 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1562 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1563 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1565 TRACE("%p\n", lpwfs);
1567 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1568 req->lpszNewFile, req->fFailIfExists,
1569 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1570 heap_free(req->lpszRemoteFile);
1571 heap_free(req->lpszNewFile);
1575 /***********************************************************************
1576 * FtpGetFileW (WININET.@)
1578 * Retrieve file from the FTP server
1585 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1586 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1587 DWORD_PTR dwContext)
1589 ftp_session_t *lpwfs;
1590 appinfo_t *hIC = NULL;
1593 if (!lpszRemoteFile || !lpszNewFile)
1595 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1599 lpwfs = (ftp_session_t*) get_handle_object( hInternet );
1602 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1606 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1608 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1612 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1614 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1618 if (lpwfs->download_in_progress != NULL)
1620 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1624 hIC = lpwfs->lpAppInfo;
1625 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1628 struct WORKREQ_FTPGETFILEW *req;
1630 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetFileProc, sizeof(*task));
1631 req = &task->u.FtpGetFileW;
1632 req->lpszRemoteFile = heap_strdupW(lpszRemoteFile);
1633 req->lpszNewFile = heap_strdupW(lpszNewFile);
1634 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1635 req->fFailIfExists = fFailIfExists;
1636 req->dwFlags = dwInternetFlags;
1637 req->dwContext = dwContext;
1639 r = res_to_le(INTERNET_AsyncCall(task));
1643 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1644 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1648 WININET_Release( &lpwfs->hdr );
1654 /***********************************************************************
1655 * FTP_FtpGetFileW (Internal)
1657 * Retrieve file from the FTP server
1664 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1665 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1666 DWORD_PTR dwContext)
1668 BOOL bSuccess = FALSE;
1670 appinfo_t *hIC = NULL;
1672 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1674 /* Clear any error information */
1675 INTERNET_SetLastError(0);
1677 /* Ensure we can write to lpszNewfile by opening it */
1678 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1679 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1680 if (INVALID_HANDLE_VALUE == hFile)
1683 /* Set up socket to retrieve data */
1684 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1688 /* Get data socket to server */
1689 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1694 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1695 closesocket(nDataSocket);
1697 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1700 if (nResCode == 226)
1703 FTP_SetResponseError(nResCode);
1708 if (lpwfs->lstnSocket != -1)
1710 closesocket(lpwfs->lstnSocket);
1711 lpwfs->lstnSocket = -1;
1716 hIC = lpwfs->lpAppInfo;
1717 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1719 INTERNET_ASYNC_RESULT iar;
1721 iar.dwResult = (DWORD)bSuccess;
1722 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1723 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1724 &iar, sizeof(INTERNET_ASYNC_RESULT));
1727 if (!bSuccess) DeleteFileW(lpszNewFile);
1731 /***********************************************************************
1732 * FtpGetFileSize (WININET.@)
1734 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1736 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1738 if (lpdwFileSizeHigh)
1739 *lpdwFileSizeHigh = 0;
1744 /***********************************************************************
1745 * FtpDeleteFileA (WININET.@)
1747 * Delete a file on the ftp server
1754 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1756 LPWSTR lpwzFileName;
1759 lpwzFileName = heap_strdupAtoW(lpszFileName);
1760 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1761 heap_free(lpwzFileName);
1765 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1767 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1768 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1770 TRACE("%p\n", lpwfs);
1772 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1773 heap_free(req->lpszFilename);
1776 /***********************************************************************
1777 * FtpDeleteFileW (WININET.@)
1779 * Delete a file on the ftp server
1786 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1788 ftp_session_t *lpwfs;
1789 appinfo_t *hIC = NULL;
1792 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1795 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1799 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1801 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1805 if (lpwfs->download_in_progress != NULL)
1807 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1813 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1817 hIC = lpwfs->lpAppInfo;
1818 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1821 struct WORKREQ_FTPDELETEFILEW *req;
1823 task = alloc_async_task(&lpwfs->hdr, AsyncFtpDeleteFileProc, sizeof(*task));
1824 req = &task->u.FtpDeleteFileW;
1825 req->lpszFilename = heap_strdupW(lpszFileName);
1827 r = res_to_le(INTERNET_AsyncCall(task));
1831 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1835 WININET_Release( &lpwfs->hdr );
1840 /***********************************************************************
1841 * FTP_FtpDeleteFileW (Internal)
1843 * Delete a file on the ftp server
1850 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1853 BOOL bSuccess = FALSE;
1854 appinfo_t *hIC = NULL;
1856 TRACE("%p\n", lpwfs);
1858 /* Clear any error information */
1859 INTERNET_SetLastError(0);
1861 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1864 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1867 if (nResCode == 250)
1870 FTP_SetResponseError(nResCode);
1873 hIC = lpwfs->lpAppInfo;
1874 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1876 INTERNET_ASYNC_RESULT iar;
1878 iar.dwResult = (DWORD)bSuccess;
1879 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1880 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1881 &iar, sizeof(INTERNET_ASYNC_RESULT));
1888 /***********************************************************************
1889 * FtpRemoveDirectoryA (WININET.@)
1891 * Remove a directory on the ftp server
1898 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1900 LPWSTR lpwzDirectory;
1903 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1904 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1905 heap_free(lpwzDirectory);
1909 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1911 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1912 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1914 TRACE("%p\n", lpwfs);
1916 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1917 heap_free(req->lpszDirectory);
1920 /***********************************************************************
1921 * FtpRemoveDirectoryW (WININET.@)
1923 * Remove a directory on the ftp server
1930 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1932 ftp_session_t *lpwfs;
1933 appinfo_t *hIC = NULL;
1936 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1939 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1943 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1945 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1949 if (lpwfs->download_in_progress != NULL)
1951 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1957 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1961 hIC = lpwfs->lpAppInfo;
1962 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1965 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1967 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRemoveDirectoryProc, sizeof(*task));
1968 req = &task->u.FtpRemoveDirectoryW;
1969 req->lpszDirectory = heap_strdupW(lpszDirectory);
1971 r = res_to_le(INTERNET_AsyncCall(task));
1975 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1979 WININET_Release( &lpwfs->hdr );
1984 /***********************************************************************
1985 * FTP_FtpRemoveDirectoryW (Internal)
1987 * Remove a directory on the ftp server
1994 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
1997 BOOL bSuccess = FALSE;
1998 appinfo_t *hIC = NULL;
2002 /* Clear any error information */
2003 INTERNET_SetLastError(0);
2005 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2008 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2011 if (nResCode == 250)
2014 FTP_SetResponseError(nResCode);
2018 hIC = lpwfs->lpAppInfo;
2019 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2021 INTERNET_ASYNC_RESULT iar;
2023 iar.dwResult = (DWORD)bSuccess;
2024 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2025 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2026 &iar, sizeof(INTERNET_ASYNC_RESULT));
2033 /***********************************************************************
2034 * FtpRenameFileA (WININET.@)
2036 * Rename a file on the ftp server
2043 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2049 lpwzSrc = heap_strdupAtoW(lpszSrc);
2050 lpwzDest = heap_strdupAtoW(lpszDest);
2051 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2053 heap_free(lpwzDest);
2057 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
2059 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
2060 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
2062 TRACE("%p\n", lpwfs);
2064 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
2065 heap_free(req->lpszSrcFile);
2066 heap_free(req->lpszDestFile);
2069 /***********************************************************************
2070 * FtpRenameFileW (WININET.@)
2072 * Rename a file on the ftp server
2079 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2081 ftp_session_t *lpwfs;
2082 appinfo_t *hIC = NULL;
2085 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
2088 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2092 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2094 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2098 if (lpwfs->download_in_progress != NULL)
2100 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2104 if (!lpszSrc || !lpszDest)
2106 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2110 hIC = lpwfs->lpAppInfo;
2111 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2114 struct WORKREQ_FTPRENAMEFILEW *req;
2116 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRenameFileProc, sizeof(*task));
2117 req = &task->u.FtpRenameFileW;
2118 req->lpszSrcFile = heap_strdupW(lpszSrc);
2119 req->lpszDestFile = heap_strdupW(lpszDest);
2121 r = res_to_le(INTERNET_AsyncCall(task));
2125 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2129 WININET_Release( &lpwfs->hdr );
2134 /***********************************************************************
2135 * FTP_FtpRenameFileW (Internal)
2137 * Rename a file on the ftp server
2144 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2147 BOOL bSuccess = FALSE;
2148 appinfo_t *hIC = NULL;
2152 /* Clear any error information */
2153 INTERNET_SetLastError(0);
2155 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2158 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2159 if (nResCode == 350)
2161 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2164 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2167 if (nResCode == 250)
2170 FTP_SetResponseError(nResCode);
2173 hIC = lpwfs->lpAppInfo;
2174 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2176 INTERNET_ASYNC_RESULT iar;
2178 iar.dwResult = (DWORD)bSuccess;
2179 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2180 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2181 &iar, sizeof(INTERNET_ASYNC_RESULT));
2187 /***********************************************************************
2188 * FtpCommandA (WININET.@)
2190 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2191 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2196 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2197 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2199 if (fExpectResponse)
2201 FIXME("data connection not supported\n");
2205 if (!lpszCommand || !lpszCommand[0])
2207 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2211 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2213 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2217 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2223 /***********************************************************************
2224 * FtpCommandW (WININET.@)
2226 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2227 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2230 ftp_session_t *lpwfs;
2232 DWORD len, nBytesSent= 0;
2233 INT nResCode, nRC = 0;
2235 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2236 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2238 if (!lpszCommand || !lpszCommand[0])
2240 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2244 if (fExpectResponse)
2246 FIXME("data connection not supported\n");
2250 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
2253 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2257 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2259 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2263 if (lpwfs->download_in_progress != NULL)
2265 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2269 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2270 if ((cmd = heap_alloc(len)))
2271 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2274 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2278 strcat(cmd, szCRLF);
2281 TRACE("Sending (%s) len(%d)\n", cmd, len);
2282 while ((nBytesSent < len) && (nRC != -1))
2284 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2288 TRACE("Sent %d bytes\n", nRC);
2294 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2295 if (nResCode > 0 && nResCode < 400)
2298 FTP_SetResponseError(nResCode);
2302 WININET_Release( &lpwfs->hdr );
2308 /***********************************************************************
2309 * FTPSESSION_Destroy (internal)
2311 * Deallocate session handle
2313 static void FTPSESSION_Destroy(object_header_t *hdr)
2315 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2319 WININET_Release(&lpwfs->lpAppInfo->hdr);
2321 heap_free(lpwfs->lpszPassword);
2322 heap_free(lpwfs->lpszUserName);
2323 heap_free(lpwfs->servername);
2326 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2328 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2332 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2333 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2335 if (lpwfs->download_in_progress != NULL)
2336 lpwfs->download_in_progress->session_deleted = TRUE;
2338 if (lpwfs->sndSocket != -1)
2339 closesocket(lpwfs->sndSocket);
2341 if (lpwfs->lstnSocket != -1)
2342 closesocket(lpwfs->lstnSocket);
2344 if (lpwfs->pasvSocket != -1)
2345 closesocket(lpwfs->pasvSocket);
2347 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2348 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2351 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2354 case INTERNET_OPTION_HANDLE_TYPE:
2355 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2357 if (*size < sizeof(ULONG))
2358 return ERROR_INSUFFICIENT_BUFFER;
2360 *size = sizeof(DWORD);
2361 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2362 return ERROR_SUCCESS;
2365 return INET_QueryOption(hdr, option, buffer, size, unicode);
2368 static const object_vtbl_t FTPSESSIONVtbl = {
2370 FTPSESSION_CloseConnection,
2371 FTPSESSION_QueryOption,
2381 /***********************************************************************
2382 * FTP_Connect (internal)
2384 * Connect to a ftp server
2387 * HINTERNET a session handle on success
2392 * Windows uses 'anonymous' as the username, when given a NULL username
2393 * and a NULL password. The password is first looked up in:
2395 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2397 * If this entry is not present it uses the current username as the password.
2401 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2402 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2403 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2404 DWORD dwInternalFlags)
2406 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2407 'M','i','c','r','o','s','o','f','t','\\',
2408 'W','i','n','d','o','w','s','\\',
2409 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2410 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2411 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2412 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2413 static const WCHAR szEmpty[] = {'\0'};
2414 struct sockaddr_in socketAddr;
2416 socklen_t sock_namelen;
2417 BOOL bSuccess = FALSE;
2418 ftp_session_t *lpwfs = NULL;
2419 char szaddr[INET_ADDRSTRLEN];
2421 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2422 hIC, debugstr_w(lpszServerName),
2423 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2425 assert( hIC->hdr.htype == WH_HINIT );
2427 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2429 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2433 lpwfs = alloc_object(&hIC->hdr, &FTPSESSIONVtbl, sizeof(ftp_session_t));
2436 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2440 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2441 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2443 lpwfs->serverport = nServerPort;
2445 lpwfs->hdr.htype = WH_HFTPSESSION;
2446 lpwfs->hdr.dwFlags = dwFlags;
2447 lpwfs->hdr.dwContext = dwContext;
2448 lpwfs->hdr.dwInternalFlags |= dwInternalFlags;
2449 lpwfs->download_in_progress = NULL;
2450 lpwfs->sndSocket = -1;
2451 lpwfs->lstnSocket = -1;
2452 lpwfs->pasvSocket = -1;
2454 WININET_AddRef( &hIC->hdr );
2455 lpwfs->lpAppInfo = hIC;
2456 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2458 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
2459 if(strchrW(hIC->proxy, ' '))
2460 FIXME("Several proxies not implemented.\n");
2461 if(hIC->proxyBypass)
2462 FIXME("Proxy bypass is ignored.\n");
2464 if (!lpszUserName || !strlenW(lpszUserName)) {
2466 WCHAR szPassword[MAX_PATH];
2467 DWORD len = sizeof(szPassword);
2469 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2471 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2472 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2473 /* Nothing in the registry, get the username and use that as the password */
2474 if (!GetUserNameW(szPassword, &len)) {
2475 /* Should never get here, but use an empty password as failsafe */
2476 strcpyW(szPassword, szEmpty);
2481 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2482 lpwfs->lpszPassword = heap_strdupW(szPassword);
2485 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2486 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2488 lpwfs->servername = heap_strdupW(lpszServerName);
2490 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2491 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2493 INTERNET_ASYNC_RESULT iar;
2495 iar.dwResult = (DWORD_PTR)lpwfs->hdr.hInternet;
2496 iar.dwError = ERROR_SUCCESS;
2498 SendAsyncCallback(&hIC->hdr, dwContext,
2499 INTERNET_STATUS_HANDLE_CREATED, &iar,
2500 sizeof(INTERNET_ASYNC_RESULT));
2503 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2504 (LPWSTR) lpszServerName, (strlenW(lpszServerName)+1) * sizeof(WCHAR));
2506 sock_namelen = sizeof(socketAddr);
2507 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen))
2509 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2513 if (socketAddr.sin_family != AF_INET)
2515 WARN("unsupported address family %d\n", socketAddr.sin_family);
2516 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2520 inet_ntop(socketAddr.sin_family, &socketAddr.sin_addr, szaddr, sizeof(szaddr));
2521 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2522 szaddr, strlen(szaddr)+1);
2524 nsocket = socket(AF_INET,SOCK_STREAM,0);
2527 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2531 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2532 szaddr, strlen(szaddr)+1);
2534 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2536 ERR("Unable to connect (%s)\n", strerror(errno));
2537 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2538 closesocket(nsocket);
2542 TRACE("Connected to server\n");
2543 lpwfs->sndSocket = nsocket;
2544 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2545 szaddr, strlen(szaddr)+1);
2547 sock_namelen = sizeof(lpwfs->socketAddress);
2548 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2550 if (FTP_ConnectToHost(lpwfs))
2552 TRACE("Successfully logged into server\n");
2561 WININET_Release( &lpwfs->hdr );
2565 return lpwfs->hdr.hInternet;
2569 /***********************************************************************
2570 * FTP_ConnectToHost (internal)
2572 * Connect to a ftp server
2579 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2582 BOOL bSuccess = FALSE;
2585 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2587 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2590 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2593 /* Login successful... */
2594 if (nResCode == 230)
2596 /* User name okay, need password... */
2597 else if (nResCode == 331)
2598 bSuccess = FTP_SendPassword(lpwfs);
2599 /* Need account for login... */
2600 else if (nResCode == 332)
2601 bSuccess = FTP_SendAccount(lpwfs);
2603 FTP_SetResponseError(nResCode);
2606 TRACE("Returning %d\n", bSuccess);
2612 /***********************************************************************
2613 * FTP_SendCommandA (internal)
2615 * Send command to server
2622 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2623 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2627 DWORD nBytesSent = 0;
2631 TRACE("%d: (%s) %d\n", ftpCmd, debugstr_a(lpszParam), nSocket);
2635 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2638 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2639 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2640 if (NULL == (buf = heap_alloc(len+1)))
2642 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2645 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2646 dwParamLen ? lpszParam : "", szCRLF);
2648 TRACE("Sending (%s) len(%d)\n", buf, len);
2649 while((nBytesSent < len) && (nRC != -1))
2651 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2658 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2659 &nBytesSent, sizeof(DWORD));
2662 TRACE("Sent %d bytes\n", nBytesSent);
2666 /***********************************************************************
2667 * FTP_SendCommand (internal)
2669 * Send command to server
2676 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2677 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2680 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2681 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2682 heap_free(lpszParamA);
2686 /***********************************************************************
2687 * FTP_ReceiveResponse (internal)
2689 * Receive response from server
2692 * Reply code on success
2696 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2698 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2701 char firstprefix[5];
2702 BOOL multiline = FALSE;
2704 TRACE("socket(%d)\n", lpwfs->sndSocket);
2706 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2710 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2717 if(lpszResponse[3] != '-')
2720 { /* Start of multiline response. Loop until we get "nnn " */
2722 memcpy(firstprefix, lpszResponse, 3);
2723 firstprefix[3] = ' ';
2724 firstprefix[4] = '\0';
2729 if(!memcmp(firstprefix, lpszResponse, 4))
2737 rc = atoi(lpszResponse);
2739 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2740 &nRecv, sizeof(DWORD));
2744 TRACE("return %d\n", rc);
2749 /***********************************************************************
2750 * FTP_SendPassword (internal)
2752 * Send password to ftp server
2759 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2762 BOOL bSuccess = FALSE;
2765 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2768 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2771 TRACE("Received reply code %d\n", nResCode);
2772 /* Login successful... */
2773 if (nResCode == 230)
2775 /* Command not implemented, superfluous at the server site... */
2776 /* Need account for login... */
2777 else if (nResCode == 332)
2778 bSuccess = FTP_SendAccount(lpwfs);
2780 FTP_SetResponseError(nResCode);
2784 TRACE("Returning %d\n", bSuccess);
2789 /***********************************************************************
2790 * FTP_SendAccount (internal)
2799 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2802 BOOL bSuccess = FALSE;
2805 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2808 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2812 FTP_SetResponseError(nResCode);
2819 /***********************************************************************
2820 * FTP_SendStore (internal)
2822 * Send request to upload file to ftp server
2829 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2832 BOOL bSuccess = FALSE;
2835 if (!FTP_InitListenSocket(lpwfs))
2838 if (!FTP_SendType(lpwfs, dwType))
2841 if (!FTP_SendPortOrPasv(lpwfs))
2844 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2846 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2849 if (nResCode == 150 || nResCode == 125)
2852 FTP_SetResponseError(nResCode);
2856 if (!bSuccess && lpwfs->lstnSocket != -1)
2858 closesocket(lpwfs->lstnSocket);
2859 lpwfs->lstnSocket = -1;
2866 /***********************************************************************
2867 * FTP_InitListenSocket (internal)
2869 * Create a socket to listen for server response
2876 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2878 BOOL bSuccess = FALSE;
2879 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2883 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2884 if (lpwfs->lstnSocket == -1)
2886 TRACE("Unable to create listening socket\n");
2890 /* We obtain our ip addr from the name of the command channel socket */
2891 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2893 /* and get the system to assign us a port */
2894 lpwfs->lstnSocketAddress.sin_port = htons(0);
2896 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2898 TRACE("Unable to bind socket\n");
2902 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2904 TRACE("listen failed\n");
2908 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2912 if (!bSuccess && lpwfs->lstnSocket != -1)
2914 closesocket(lpwfs->lstnSocket);
2915 lpwfs->lstnSocket = -1;
2922 /***********************************************************************
2923 * FTP_SendType (internal)
2925 * Tell server type of data being transferred
2931 * W98SE doesn't cache the type that's currently set
2932 * (i.e. it sends it always),
2933 * so we probably don't want to do that either.
2935 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2938 WCHAR type[] = { 'I','\0' };
2939 BOOL bSuccess = FALSE;
2942 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2945 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2948 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2954 FTP_SetResponseError(nResCode);
2962 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2963 /***********************************************************************
2964 * FTP_GetFileSize (internal)
2966 * Retrieves from the server the size of the given file
2973 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2976 BOOL bSuccess = FALSE;
2980 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2983 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2986 if (nResCode == 213) {
2987 /* Now parses the output to get the actual file size */
2989 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2991 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2992 if (lpszResponseBuffer[i] == '\0') return FALSE;
2993 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2997 FTP_SetResponseError(nResCode);
3007 /***********************************************************************
3008 * FTP_SendPort (internal)
3010 * Tell server which port to use
3017 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3019 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3021 WCHAR szIPAddress[64];
3022 BOOL bSuccess = FALSE;
3025 sprintfW(szIPAddress, szIPFormat,
3026 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
3027 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
3028 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
3029 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
3030 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3031 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3033 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3036 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3039 if (nResCode == 200)
3042 FTP_SetResponseError(nResCode);
3050 /***********************************************************************
3051 * FTP_DoPassive (internal)
3053 * Tell server that we want to do passive transfers
3054 * and connect data socket
3061 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3064 BOOL bSuccess = FALSE;
3067 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3070 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3073 if (nResCode == 227)
3075 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3079 char *pAddr, *pPort;
3081 struct sockaddr_in dataSocketAddress;
3083 p = lpszResponseBuffer+4; /* skip status code */
3084 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3088 ERR("no address found in response, aborting\n");
3092 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3095 ERR("unknown response address format '%s', aborting\n", p);
3098 for (i=0; i < 6; i++)
3101 dataSocketAddress = lpwfs->socketAddress;
3102 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3103 pPort = (char *)&(dataSocketAddress.sin_port);
3111 nsocket = socket(AF_INET,SOCK_STREAM,0);
3115 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3117 ERR("can't connect passive FTP data port.\n");
3118 closesocket(nsocket);
3121 lpwfs->pasvSocket = nsocket;
3125 FTP_SetResponseError(nResCode);
3133 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3135 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3137 if (!FTP_DoPassive(lpwfs))
3142 if (!FTP_SendPort(lpwfs))
3149 /***********************************************************************
3150 * FTP_GetDataSocket (internal)
3152 * Either accepts an incoming data socket connection from the server
3153 * or just returns the already opened socket after a PASV command
3154 * in case of passive FTP.
3162 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3164 struct sockaddr_in saddr;
3165 socklen_t addrlen = sizeof(struct sockaddr);
3168 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3170 *nDataSocket = lpwfs->pasvSocket;
3171 lpwfs->pasvSocket = -1;
3175 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3176 closesocket(lpwfs->lstnSocket);
3177 lpwfs->lstnSocket = -1;
3179 return *nDataSocket != -1;
3183 /***********************************************************************
3184 * FTP_SendData (internal)
3186 * Send data to the server
3193 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3195 BY_HANDLE_FILE_INFORMATION fi;
3196 DWORD nBytesRead = 0;
3197 DWORD nBytesSent = 0;
3198 DWORD nTotalSent = 0;
3199 DWORD nBytesToSend, nLen;
3201 time_t s_long_time, e_long_time;
3206 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3208 /* Get the size of the file. */
3209 GetFileInformationByHandle(hFile, &fi);
3214 nBytesToSend = nBytesRead - nBytesSent;
3216 if (nBytesToSend <= 0)
3218 /* Read data from file. */
3220 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3221 ERR("Failed reading from file\n");
3224 nBytesToSend = nBytesRead;
3229 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3230 DATA_PACKET_SIZE : nBytesToSend;
3231 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3239 /* Do some computation to display the status. */
3241 nSeconds = e_long_time - s_long_time;
3242 if( nSeconds / 60 > 0 )
3244 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3245 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3246 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3250 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3251 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3252 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3254 } while (nRC != -1);
3256 TRACE("file transfer complete!\n");
3258 heap_free(lpszBuffer);
3263 /***********************************************************************
3264 * FTP_SendRetrieve (internal)
3266 * Send request to retrieve a file
3269 * Number of bytes to be received on success
3273 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3279 if (!(ret = FTP_InitListenSocket(lpwfs)))
3282 if (!(ret = FTP_SendType(lpwfs, dwType)))
3285 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3288 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3291 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3292 if ((nResCode != 125) && (nResCode != 150)) {
3293 /* That means that we got an error getting the file. */
3294 FTP_SetResponseError(nResCode);
3299 if (!ret && lpwfs->lstnSocket != -1)
3301 closesocket(lpwfs->lstnSocket);
3302 lpwfs->lstnSocket = -1;
3309 /***********************************************************************
3310 * FTP_RetrieveData (internal)
3312 * Retrieve data from server
3319 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3321 DWORD nBytesWritten;
3327 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3328 if (NULL == lpszBuffer)
3330 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3336 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3339 /* other side closed socket. */
3342 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3346 TRACE("Data transfer complete\n");
3349 heap_free(lpszBuffer);
3353 /***********************************************************************
3354 * FTPFINDNEXT_Destroy (internal)
3356 * Deallocate session handle
3358 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3360 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3365 WININET_Release(&lpwfn->lpFtpSession->hdr);
3367 for (i = 0; i < lpwfn->size; i++)
3369 heap_free(lpwfn->lpafp[i].lpszName);
3371 heap_free(lpwfn->lpafp);
3374 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3376 WIN32_FIND_DATAW *find_data = data;
3377 DWORD res = ERROR_SUCCESS;
3379 TRACE("index(%d) size(%d)\n", find->index, find->size);
3381 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3383 if (find->index < find->size) {
3384 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3387 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3389 res = ERROR_NO_MORE_FILES;
3392 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3394 INTERNET_ASYNC_RESULT iar;
3396 iar.dwResult = (res == ERROR_SUCCESS);
3399 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3400 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3401 sizeof(INTERNET_ASYNC_RESULT));
3407 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3409 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3411 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3414 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3417 case INTERNET_OPTION_HANDLE_TYPE:
3418 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3420 if (*size < sizeof(ULONG))
3421 return ERROR_INSUFFICIENT_BUFFER;
3423 *size = sizeof(DWORD);
3424 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3425 return ERROR_SUCCESS;
3428 return INET_QueryOption(hdr, option, buffer, size, unicode);
3431 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3433 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3435 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3438 struct WORKREQ_FTPFINDNEXTW *req;
3440 task = alloc_async_task(&find->hdr, FTPFINDNEXT_AsyncFindNextFileProc, sizeof(*task));
3441 req = &task->u.FtpFindNextW;
3442 req->lpFindFileData = data;
3444 INTERNET_AsyncCall(task);
3446 return ERROR_SUCCESS;
3449 return FTPFINDNEXT_FindNextFileProc(find, data);
3452 static const object_vtbl_t FTPFINDNEXTVtbl = {
3453 FTPFINDNEXT_Destroy,
3455 FTPFINDNEXT_QueryOption,
3461 FTPFINDNEXT_FindNextFileW
3464 /***********************************************************************
3465 * FTP_ReceiveFileList (internal)
3467 * Read file list from server
3470 * Handle to file list on success
3474 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3475 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3478 LPFILEPROPERTIESW lpafp = NULL;
3479 LPWININETFTPFINDNEXTW lpwfn = NULL;
3481 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3483 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3486 FTP_ConvertFileProp(lpafp, lpFindFileData);
3488 lpwfn = alloc_object(&lpwfs->hdr, &FTPFINDNEXTVtbl, sizeof(WININETFTPFINDNEXTW));
3491 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3492 lpwfn->hdr.dwContext = dwContext;
3493 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3494 lpwfn->size = dwSize;
3495 lpwfn->lpafp = lpafp;
3497 WININET_AddRef( &lpwfs->hdr );
3498 lpwfn->lpFtpSession = lpwfs;
3499 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3503 TRACE("Matched %d files\n", dwSize);
3504 return lpwfn ? lpwfn->hdr.hInternet : NULL;
3508 /***********************************************************************
3509 * FTP_ConvertFileProp (internal)
3511 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3518 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3520 BOOL bSuccess = FALSE;
3522 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3526 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3527 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3528 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3530 /* Not all fields are filled in */
3531 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3532 lpFindFileData->nFileSizeLow = lpafp->nSize;
3534 if (lpafp->bIsDirectory)
3535 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3537 if (lpafp->lpszName)
3538 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3546 /***********************************************************************
3547 * FTP_ParseNextFile (internal)
3549 * Parse the next line in file listing
3555 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3557 static const char szSpace[] = " \t";
3565 lpfp->lpszName = NULL;
3567 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3570 pszToken = strtok(pszLine, szSpace);
3572 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3575 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3577 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3578 if(!FTP_ParsePermission(pszToken, lpfp))
3579 lpfp->bIsDirectory = FALSE;
3580 for(i=0; i<=3; i++) {
3581 if(!(pszToken = strtok(NULL, szSpace)))
3584 if(!pszToken) continue;
3585 if(lpfp->bIsDirectory) {
3586 TRACE("Is directory\n");
3590 TRACE("Size: %s\n", pszToken);
3591 lpfp->nSize = atol(pszToken);
3594 lpfp->tmLastModified.wSecond = 0;
3595 lpfp->tmLastModified.wMinute = 0;
3596 lpfp->tmLastModified.wHour = 0;
3597 lpfp->tmLastModified.wDay = 0;
3598 lpfp->tmLastModified.wMonth = 0;
3599 lpfp->tmLastModified.wYear = 0;
3601 /* Determine month */
3602 pszToken = strtok(NULL, szSpace);
3603 if(!pszToken) continue;
3604 if(strlen(pszToken) >= 3) {
3606 if((pszTmp = StrStrIA(szMonths, pszToken)))
3607 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3610 pszToken = strtok(NULL, szSpace);
3611 if(!pszToken) continue;
3612 lpfp->tmLastModified.wDay = atoi(pszToken);
3613 /* Determine time or year */
3614 pszToken = strtok(NULL, szSpace);
3615 if(!pszToken) continue;
3616 if((pszTmp = strchr(pszToken, ':'))) {
3617 SYSTEMTIME curr_time;
3620 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3621 lpfp->tmLastModified.wHour = atoi(pszToken);
3622 GetLocalTime( &curr_time );
3623 lpfp->tmLastModified.wYear = curr_time.wYear;
3626 lpfp->tmLastModified.wYear = atoi(pszToken);
3627 lpfp->tmLastModified.wHour = 12;
3629 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3630 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3631 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3633 pszToken = strtok(NULL, szSpace);
3634 if(!pszToken) continue;
3635 lpfp->lpszName = heap_strdupAtoW(pszToken);
3636 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3638 /* NT way of parsing ... :
3640 07-13-03 08:55PM <DIR> sakpatch
3641 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3643 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3644 int mon, mday, year, hour, min;
3645 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3647 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3648 lpfp->tmLastModified.wDay = mday;
3649 lpfp->tmLastModified.wMonth = mon;
3650 lpfp->tmLastModified.wYear = year;
3652 /* Hacky and bad Y2K protection :-) */
3653 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3655 pszToken = strtok(NULL, szSpace);
3656 if(!pszToken) continue;
3657 sscanf(pszToken, "%d:%d", &hour, &min);
3658 lpfp->tmLastModified.wHour = hour;
3659 lpfp->tmLastModified.wMinute = min;
3660 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3661 lpfp->tmLastModified.wHour += 12;
3663 lpfp->tmLastModified.wSecond = 0;
3665 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3666 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3667 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3669 pszToken = strtok(NULL, szSpace);
3670 if(!pszToken) continue;
3671 if(!strcasecmp(pszToken, "<DIR>")) {
3672 lpfp->bIsDirectory = TRUE;
3674 TRACE("Is directory\n");
3677 lpfp->bIsDirectory = FALSE;
3678 lpfp->nSize = atol(pszToken);
3679 TRACE("Size: %d\n", lpfp->nSize);
3682 pszToken = strtok(NULL, szSpace);
3683 if(!pszToken) continue;
3684 lpfp->lpszName = heap_strdupAtoW(pszToken);
3685 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3687 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3688 else if(pszToken[0] == '+') {
3689 FIXME("EPLF Format not implemented\n");
3692 if(lpfp->lpszName) {
3693 if((lpszSearchFile == NULL) ||
3694 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3696 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3699 heap_free(lpfp->lpszName);
3700 lpfp->lpszName = NULL;
3707 /***********************************************************************
3708 * FTP_ParseDirectory (internal)
3710 * Parse string of directory information
3716 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3717 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3719 BOOL bSuccess = TRUE;
3720 INT sizeFilePropArray = 500;/*20; */
3721 INT indexFilePropArray = -1;
3725 /* Allocate initial file properties array */
3726 *lpafp = heap_alloc_zero(sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3731 if (indexFilePropArray+1 >= sizeFilePropArray)
3733 LPFILEPROPERTIESW tmpafp;
3735 sizeFilePropArray *= 2;
3736 tmpafp = heap_realloc_zero(*lpafp, sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3745 indexFilePropArray++;
3746 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3748 if (bSuccess && indexFilePropArray)
3750 if (indexFilePropArray < sizeFilePropArray - 1)
3752 LPFILEPROPERTIESW tmpafp;
3754 tmpafp = heap_realloc(*lpafp, sizeof(FILEPROPERTIESW)*indexFilePropArray);
3758 *dwfp = indexFilePropArray;
3763 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3771 /***********************************************************************
3772 * FTP_ParsePermission (internal)
3774 * Parse permission string of directory information
3781 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3783 BOOL bSuccess = TRUE;
3784 unsigned short nPermission = 0;
3789 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3795 lpfp->bIsDirectory = (*lpszPermission == 'd');
3801 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3804 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3807 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3810 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3813 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3816 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3819 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3822 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3825 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3829 }while (nPos <= nLast);
3831 lpfp->permissions = nPermission;
3836 /***********************************************************************
3837 * FTP_SetResponseError (internal)
3839 * Set the appropriate error code for a given response from the server
3844 static DWORD FTP_SetResponseError(DWORD dwResponse)
3850 case 425: /* Cannot open data connection. */
3851 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3854 case 426: /* Connection closed, transer aborted. */
3855 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3858 case 530: /* Not logged in. Login incorrect. */
3859 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3862 case 421: /* Service not available - Server may be shutting down. */
3863 case 450: /* File action not taken. File may be busy. */
3864 case 451: /* Action aborted. Server error. */
3865 case 452: /* Action not taken. Insufficient storage space on server. */
3866 case 500: /* Syntax error. Command unrecognized. */
3867 case 501: /* Syntax error. Error in parameters or arguments. */
3868 case 502: /* Command not implemented. */
3869 case 503: /* Bad sequence of commands. */
3870 case 504: /* Command not implemented for that parameter. */
3871 case 532: /* Need account for storing files */
3872 case 550: /* File action not taken. File not found or no access. */
3873 case 551: /* Requested action aborted. Page type unknown */
3874 case 552: /* Action aborted. Exceeded storage allocation */
3875 case 553: /* Action not taken. File name not allowed. */
3878 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3882 INTERNET_SetLastError(dwCode);