hlink: Site data should only be set if the hlink has an HlinkSite.
[wine/multimedia.git] / dlls / wininet / ftp.c
blob80cc6f0e60a1356e20ea4777494b04fcb77d7eeb
1 /*
2 * WININET - Ftp implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2004 Mike McCormack for CodeWeavers
6 * Copyright 2004 Kevin Koltzau
7 * Copyright 2007 Hans Leidekker
9 * Ulrich Czekalla
10 * Noureddine Jemmali
12 * Copyright 2000 Andreas Mohr
13 * Copyright 2002 Jaco Greeff
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 #include "config.h"
31 #include "wine/port.h"
33 #if defined(__MINGW32__) || defined (_MSC_VER)
34 #include <ws2tcpip.h>
35 #endif
37 #include <errno.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/types.h>
43 #ifdef HAVE_SYS_SOCKET_H
44 # include <sys/socket.h>
45 #endif
46 #ifdef HAVE_ARPA_INET_H
47 # include <arpa/inet.h>
48 #endif
49 #ifdef HAVE_UNISTD_H
50 # include <unistd.h>
51 #endif
52 #ifdef HAVE_SYS_IOCTL_H
53 # include <sys/ioctl.h>
54 #endif
55 #include <time.h>
56 #include <assert.h>
58 #include "windef.h"
59 #include "winbase.h"
60 #include "wingdi.h"
61 #include "winuser.h"
62 #include "wininet.h"
63 #include "winnls.h"
64 #include "winerror.h"
65 #include "winreg.h"
66 #include "winternl.h"
67 #include "shlwapi.h"
69 #include "wine/debug.h"
70 #include "internet.h"
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
74 typedef struct _ftp_session_t ftp_session_t;
76 typedef struct
78 object_header_t hdr;
79 ftp_session_t *lpFtpSession;
80 BOOL session_deleted;
81 int nDataSocket;
82 WCHAR *cache_file;
83 HANDLE cache_file_handle;
84 } ftp_file_t;
86 struct _ftp_session_t
88 object_header_t hdr;
89 appinfo_t *lpAppInfo;
90 int sndSocket;
91 int lstnSocket;
92 int pasvSocket; /* data socket connected by us in case of passive FTP */
93 ftp_file_t *download_in_progress;
94 struct sockaddr_in socketAddress;
95 struct sockaddr_in lstnSocketAddress;
96 LPWSTR servername;
97 INTERNET_PORT serverport;
98 LPWSTR lpszPassword;
99 LPWSTR lpszUserName;
102 typedef struct
104 BOOL bIsDirectory;
105 LPWSTR lpszName;
106 DWORD nSize;
107 SYSTEMTIME tmLastModified;
108 unsigned short permissions;
109 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
111 typedef struct
113 object_header_t hdr;
114 ftp_session_t *lpFtpSession;
115 DWORD index;
116 DWORD size;
117 LPFILEPROPERTIESW lpafp;
118 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
120 #define DATA_PACKET_SIZE 0x2000
121 #define szCRLF "\r\n"
122 #define MAX_BACKLOG 5
124 /* Testing shows that Windows only accepts dwFlags where the last
125 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
127 #define FTP_CONDITION_MASK 0x0007
129 typedef enum {
130 /* FTP commands with arguments. */
131 FTP_CMD_ACCT,
132 FTP_CMD_CWD,
133 FTP_CMD_DELE,
134 FTP_CMD_MKD,
135 FTP_CMD_PASS,
136 FTP_CMD_PORT,
137 FTP_CMD_RETR,
138 FTP_CMD_RMD,
139 FTP_CMD_RNFR,
140 FTP_CMD_RNTO,
141 FTP_CMD_STOR,
142 FTP_CMD_TYPE,
143 FTP_CMD_USER,
144 FTP_CMD_SIZE,
146 /* FTP commands without arguments. */
147 FTP_CMD_ABOR,
148 FTP_CMD_LIST,
149 FTP_CMD_NLST,
150 FTP_CMD_PASV,
151 FTP_CMD_PWD,
152 FTP_CMD_QUIT,
153 } FTP_COMMAND;
155 static const CHAR *const szFtpCommands[] = {
156 "ACCT",
157 "CWD",
158 "DELE",
159 "MKD",
160 "PASS",
161 "PORT",
162 "RETR",
163 "RMD",
164 "RNFR",
165 "RNTO",
166 "STOR",
167 "TYPE",
168 "USER",
169 "SIZE",
170 "ABOR",
171 "LIST",
172 "NLST",
173 "PASV",
174 "PWD",
175 "QUIT",
178 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
179 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
181 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
182 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext);
183 static BOOL FTP_SendStore(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
184 static BOOL FTP_GetDataSocket(ftp_session_t*, LPINT nDataSocket);
185 static BOOL FTP_SendData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
186 static INT FTP_ReceiveResponse(ftp_session_t*, DWORD_PTR dwContext);
187 static BOOL FTP_SendRetrieve(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
188 static BOOL FTP_RetrieveFileData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
189 static BOOL FTP_InitListenSocket(ftp_session_t*);
190 static BOOL FTP_ConnectToHost(ftp_session_t*);
191 static BOOL FTP_SendPassword(ftp_session_t*);
192 static BOOL FTP_SendAccount(ftp_session_t*);
193 static BOOL FTP_SendType(ftp_session_t*, DWORD dwType);
194 static BOOL FTP_SendPort(ftp_session_t*);
195 static BOOL FTP_DoPassive(ftp_session_t*);
196 static BOOL FTP_SendPortOrPasv(ftp_session_t*);
197 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
198 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
199 static BOOL FTP_ParseDirectory(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
200 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
201 static HINTERNET FTP_ReceiveFileList(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
202 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
203 static DWORD FTP_SetResponseError(DWORD dwResponse);
204 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
205 static BOOL FTP_FtpPutFileW(ftp_session_t*, LPCWSTR lpszLocalFile,
206 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
207 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
208 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
209 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t*,
210 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
211 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t*, LPWSTR lpszCurrentDirectory,
212 LPDWORD lpdwCurrentDirectory);
213 static BOOL FTP_FtpRenameFileW(ftp_session_t*, LPCWSTR lpszSrc, LPCWSTR lpszDest);
214 static BOOL FTP_FtpRemoveDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
215 static BOOL FTP_FtpDeleteFileW(ftp_session_t*, LPCWSTR lpszFileName);
216 static BOOL FTP_FtpGetFileW(ftp_session_t*, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
217 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
218 DWORD_PTR dwContext);
220 /* A temporary helper until we get rid of INTERNET_GetLastError calls */
221 static BOOL res_to_le(DWORD res)
223 if(res != ERROR_SUCCESS)
224 INTERNET_SetLastError(res);
225 return res == ERROR_SUCCESS;
228 /***********************************************************************
229 * FtpPutFileA (WININET.@)
231 * Uploads a file to the FTP server
233 * RETURNS
234 * TRUE on success
235 * FALSE on failure
238 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
239 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
241 LPWSTR lpwzLocalFile;
242 LPWSTR lpwzNewRemoteFile;
243 BOOL ret;
245 lpwzLocalFile = heap_strdupAtoW(lpszLocalFile);
246 lpwzNewRemoteFile = heap_strdupAtoW(lpszNewRemoteFile);
247 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
248 dwFlags, dwContext);
249 HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
250 HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
251 return ret;
254 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
256 struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
257 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
259 TRACE("%p\n", lpwfs);
261 FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
262 req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
264 HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
265 HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
268 /***********************************************************************
269 * FtpPutFileW (WININET.@)
271 * Uploads a file to the FTP server
273 * RETURNS
274 * TRUE on success
275 * FALSE on failure
278 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
279 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
281 ftp_session_t *lpwfs;
282 appinfo_t *hIC = NULL;
283 BOOL r = FALSE;
285 if (!lpszLocalFile || !lpszNewRemoteFile)
287 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
288 return FALSE;
291 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
292 if (!lpwfs)
294 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
295 return FALSE;
298 if (WH_HFTPSESSION != lpwfs->hdr.htype)
300 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
301 goto lend;
304 if (lpwfs->download_in_progress != NULL)
306 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
307 goto lend;
310 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
312 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
313 goto lend;
316 hIC = lpwfs->lpAppInfo;
317 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
319 WORKREQUEST workRequest;
320 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
322 workRequest.asyncproc = AsyncFtpPutFileProc;
323 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
324 req->lpszLocalFile = heap_strdupW(lpszLocalFile);
325 req->lpszNewRemoteFile = heap_strdupW(lpszNewRemoteFile);
326 req->dwFlags = dwFlags;
327 req->dwContext = dwContext;
329 r = res_to_le(INTERNET_AsyncCall(&workRequest));
331 else
333 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
334 lpszNewRemoteFile, dwFlags, dwContext);
337 lend:
338 WININET_Release( &lpwfs->hdr );
340 return r;
343 /***********************************************************************
344 * FTP_FtpPutFileW (Internal)
346 * Uploads a file to the FTP server
348 * RETURNS
349 * TRUE on success
350 * FALSE on failure
353 static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
354 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
356 HANDLE hFile;
357 BOOL bSuccess = FALSE;
358 appinfo_t *hIC = NULL;
359 INT nResCode;
361 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
363 /* Clear any error information */
364 INTERNET_SetLastError(0);
366 /* Open file to be uploaded */
367 if (INVALID_HANDLE_VALUE ==
368 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
369 /* Let CreateFile set the appropriate error */
370 return FALSE;
372 hIC = lpwfs->lpAppInfo;
374 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
376 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
378 INT nDataSocket;
380 /* Get data socket to server */
381 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
383 FTP_SendData(lpwfs, nDataSocket, hFile);
384 closesocket(nDataSocket);
385 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
386 if (nResCode)
388 if (nResCode == 226)
389 bSuccess = TRUE;
390 else
391 FTP_SetResponseError(nResCode);
396 if (lpwfs->lstnSocket != -1)
398 closesocket(lpwfs->lstnSocket);
399 lpwfs->lstnSocket = -1;
402 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
404 INTERNET_ASYNC_RESULT iar;
406 iar.dwResult = (DWORD)bSuccess;
407 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
408 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
409 &iar, sizeof(INTERNET_ASYNC_RESULT));
412 CloseHandle(hFile);
414 return bSuccess;
418 /***********************************************************************
419 * FtpSetCurrentDirectoryA (WININET.@)
421 * Change the working directory on the FTP server
423 * RETURNS
424 * TRUE on success
425 * FALSE on failure
428 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
430 LPWSTR lpwzDirectory;
431 BOOL ret;
433 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
434 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
435 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
436 return ret;
440 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
442 struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
443 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
445 TRACE("%p\n", lpwfs);
447 FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
448 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
451 /***********************************************************************
452 * FtpSetCurrentDirectoryW (WININET.@)
454 * Change the working directory on the FTP server
456 * RETURNS
457 * TRUE on success
458 * FALSE on failure
461 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
463 ftp_session_t *lpwfs = NULL;
464 appinfo_t *hIC = NULL;
465 BOOL r = FALSE;
467 if (!lpszDirectory)
469 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
470 goto lend;
473 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
474 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
476 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
477 goto lend;
480 if (lpwfs->download_in_progress != NULL)
482 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
483 goto lend;
486 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
488 hIC = lpwfs->lpAppInfo;
489 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
491 WORKREQUEST workRequest;
492 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
494 workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
495 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
496 req = &workRequest.u.FtpSetCurrentDirectoryW;
497 req->lpszDirectory = heap_strdupW(lpszDirectory);
499 r = res_to_le(INTERNET_AsyncCall(&workRequest));
501 else
503 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
506 lend:
507 if( lpwfs )
508 WININET_Release( &lpwfs->hdr );
510 return r;
514 /***********************************************************************
515 * FTP_FtpSetCurrentDirectoryW (Internal)
517 * Change the working directory on the FTP server
519 * RETURNS
520 * TRUE on success
521 * FALSE on failure
524 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
526 INT nResCode;
527 appinfo_t *hIC = NULL;
528 DWORD bSuccess = FALSE;
530 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
532 /* Clear any error information */
533 INTERNET_SetLastError(0);
535 hIC = lpwfs->lpAppInfo;
536 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
537 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
538 goto lend;
540 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
542 if (nResCode)
544 if (nResCode == 250)
545 bSuccess = TRUE;
546 else
547 FTP_SetResponseError(nResCode);
550 lend:
551 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
553 INTERNET_ASYNC_RESULT iar;
555 iar.dwResult = bSuccess;
556 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
557 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
558 &iar, sizeof(INTERNET_ASYNC_RESULT));
560 return bSuccess;
564 /***********************************************************************
565 * FtpCreateDirectoryA (WININET.@)
567 * Create new directory on the FTP server
569 * RETURNS
570 * TRUE on success
571 * FALSE on failure
574 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
576 LPWSTR lpwzDirectory;
577 BOOL ret;
579 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
580 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
581 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
582 return ret;
586 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
588 struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
589 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
591 TRACE(" %p\n", lpwfs);
593 FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
594 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
597 /***********************************************************************
598 * FtpCreateDirectoryW (WININET.@)
600 * Create new directory on the FTP server
602 * RETURNS
603 * TRUE on success
604 * FALSE on failure
607 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
609 ftp_session_t *lpwfs;
610 appinfo_t *hIC = NULL;
611 BOOL r = FALSE;
613 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
614 if (!lpwfs)
616 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
617 return FALSE;
620 if (WH_HFTPSESSION != lpwfs->hdr.htype)
622 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
623 goto lend;
626 if (lpwfs->download_in_progress != NULL)
628 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
629 goto lend;
632 if (!lpszDirectory)
634 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
635 goto lend;
638 hIC = lpwfs->lpAppInfo;
639 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
641 WORKREQUEST workRequest;
642 struct WORKREQ_FTPCREATEDIRECTORYW *req;
644 workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
645 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
646 req = &workRequest.u.FtpCreateDirectoryW;
647 req->lpszDirectory = heap_strdupW(lpszDirectory);
649 r = res_to_le(INTERNET_AsyncCall(&workRequest));
651 else
653 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
655 lend:
656 WININET_Release( &lpwfs->hdr );
658 return r;
662 /***********************************************************************
663 * FTP_FtpCreateDirectoryW (Internal)
665 * Create new directory on the FTP server
667 * RETURNS
668 * TRUE on success
669 * FALSE on failure
672 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
674 INT nResCode;
675 BOOL bSuccess = FALSE;
676 appinfo_t *hIC = NULL;
678 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
680 /* Clear any error information */
681 INTERNET_SetLastError(0);
683 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
684 goto lend;
686 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
687 if (nResCode)
689 if (nResCode == 257)
690 bSuccess = TRUE;
691 else
692 FTP_SetResponseError(nResCode);
695 lend:
696 hIC = lpwfs->lpAppInfo;
697 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
699 INTERNET_ASYNC_RESULT iar;
701 iar.dwResult = (DWORD)bSuccess;
702 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
703 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
704 &iar, sizeof(INTERNET_ASYNC_RESULT));
707 return bSuccess;
710 /***********************************************************************
711 * FtpFindFirstFileA (WININET.@)
713 * Search the specified directory
715 * RETURNS
716 * HINTERNET on success
717 * NULL on failure
720 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
721 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
723 LPWSTR lpwzSearchFile;
724 WIN32_FIND_DATAW wfd;
725 LPWIN32_FIND_DATAW lpFindFileDataW;
726 HINTERNET ret;
728 lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
729 lpFindFileDataW = lpFindFileData?&wfd:NULL;
730 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
731 HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
733 if (ret && lpFindFileData)
734 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
736 return ret;
740 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
742 struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
743 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
745 TRACE("%p\n", lpwfs);
747 FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
748 req->lpFindFileData, req->dwFlags, req->dwContext);
749 HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
752 /***********************************************************************
753 * FtpFindFirstFileW (WININET.@)
755 * Search the specified directory
757 * RETURNS
758 * HINTERNET on success
759 * NULL on failure
762 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
763 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
765 ftp_session_t *lpwfs;
766 appinfo_t *hIC = NULL;
767 HINTERNET r = NULL;
769 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
770 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
772 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
773 goto lend;
776 if (lpwfs->download_in_progress != NULL)
778 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
779 goto lend;
782 hIC = lpwfs->lpAppInfo;
783 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
785 WORKREQUEST workRequest;
786 struct WORKREQ_FTPFINDFIRSTFILEW *req;
788 workRequest.asyncproc = AsyncFtpFindFirstFileProc;
789 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
790 req = &workRequest.u.FtpFindFirstFileW;
791 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : heap_strdupW(lpszSearchFile);
792 req->lpFindFileData = lpFindFileData;
793 req->dwFlags = dwFlags;
794 req->dwContext= dwContext;
796 INTERNET_AsyncCall(&workRequest);
797 r = NULL;
799 else
801 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
802 dwFlags, dwContext);
804 lend:
805 if( lpwfs )
806 WININET_Release( &lpwfs->hdr );
808 return r;
812 /***********************************************************************
813 * FTP_FtpFindFirstFileW (Internal)
815 * Search the specified directory
817 * RETURNS
818 * HINTERNET on success
819 * NULL on failure
822 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
823 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
825 INT nResCode;
826 appinfo_t *hIC = NULL;
827 HINTERNET hFindNext = NULL;
829 TRACE("\n");
831 /* Clear any error information */
832 INTERNET_SetLastError(0);
834 if (!FTP_InitListenSocket(lpwfs))
835 goto lend;
837 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
838 goto lend;
840 if (!FTP_SendPortOrPasv(lpwfs))
841 goto lend;
843 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
844 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
845 goto lend;
847 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
848 if (nResCode)
850 if (nResCode == 125 || nResCode == 150)
852 INT nDataSocket;
854 /* Get data socket to server */
855 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
857 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
858 closesocket(nDataSocket);
859 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
860 if (nResCode != 226 && nResCode != 250)
861 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
864 else
865 FTP_SetResponseError(nResCode);
868 lend:
869 if (lpwfs->lstnSocket != -1)
871 closesocket(lpwfs->lstnSocket);
872 lpwfs->lstnSocket = -1;
875 hIC = lpwfs->lpAppInfo;
876 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
878 INTERNET_ASYNC_RESULT iar;
880 if (hFindNext)
882 iar.dwResult = (DWORD_PTR)hFindNext;
883 iar.dwError = ERROR_SUCCESS;
884 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
885 &iar, sizeof(INTERNET_ASYNC_RESULT));
888 iar.dwResult = (DWORD_PTR)hFindNext;
889 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
890 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
891 &iar, sizeof(INTERNET_ASYNC_RESULT));
894 return hFindNext;
898 /***********************************************************************
899 * FtpGetCurrentDirectoryA (WININET.@)
901 * Retrieves the current directory
903 * RETURNS
904 * TRUE on success
905 * FALSE on failure
908 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
909 LPDWORD lpdwCurrentDirectory)
911 WCHAR *dir = NULL;
912 DWORD len;
913 BOOL ret;
915 if(lpdwCurrentDirectory) {
916 len = *lpdwCurrentDirectory;
917 if(lpszCurrentDirectory)
919 dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
920 if (NULL == dir)
922 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
923 return FALSE;
927 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
929 if (ret && lpszCurrentDirectory)
930 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
932 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
933 HeapFree(GetProcessHeap(), 0, dir);
934 return ret;
938 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
940 struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
941 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
943 TRACE("%p\n", lpwfs);
945 FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
948 /***********************************************************************
949 * FtpGetCurrentDirectoryW (WININET.@)
951 * Retrieves the current directory
953 * RETURNS
954 * TRUE on success
955 * FALSE on failure
958 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
959 LPDWORD lpdwCurrentDirectory)
961 ftp_session_t *lpwfs;
962 appinfo_t *hIC = NULL;
963 BOOL r = FALSE;
965 TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
967 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
968 if (NULL == lpwfs)
970 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
971 goto lend;
974 if (WH_HFTPSESSION != lpwfs->hdr.htype)
976 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
977 goto lend;
980 if (!lpdwCurrentDirectory)
982 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
983 goto lend;
986 if (lpszCurrentDirectory == NULL)
988 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
989 goto lend;
992 if (lpwfs->download_in_progress != NULL)
994 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
995 goto lend;
998 hIC = lpwfs->lpAppInfo;
999 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1001 WORKREQUEST workRequest;
1002 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
1004 workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
1005 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1006 req = &workRequest.u.FtpGetCurrentDirectoryW;
1007 req->lpszDirectory = lpszCurrentDirectory;
1008 req->lpdwDirectory = lpdwCurrentDirectory;
1010 r = res_to_le(INTERNET_AsyncCall(&workRequest));
1012 else
1014 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
1015 lpdwCurrentDirectory);
1018 lend:
1019 if( lpwfs )
1020 WININET_Release( &lpwfs->hdr );
1022 return r;
1026 /***********************************************************************
1027 * FTP_FtpGetCurrentDirectoryW (Internal)
1029 * Retrieves the current directory
1031 * RETURNS
1032 * TRUE on success
1033 * FALSE on failure
1036 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
1037 LPDWORD lpdwCurrentDirectory)
1039 INT nResCode;
1040 appinfo_t *hIC = NULL;
1041 DWORD bSuccess = FALSE;
1043 /* Clear any error information */
1044 INTERNET_SetLastError(0);
1046 hIC = lpwfs->lpAppInfo;
1047 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1048 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1049 goto lend;
1051 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1052 if (nResCode)
1054 if (nResCode == 257) /* Extract directory name */
1056 DWORD firstpos, lastpos, len;
1057 LPWSTR lpszResponseBuffer = heap_strdupAtoW(INTERNET_GetResponseBuffer());
1059 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1061 if ('"' == lpszResponseBuffer[lastpos])
1063 if (!firstpos)
1064 firstpos = lastpos;
1065 else
1066 break;
1069 len = lastpos - firstpos;
1070 if (*lpdwCurrentDirectory >= len)
1072 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1073 lpszCurrentDirectory[len - 1] = 0;
1074 *lpdwCurrentDirectory = len;
1075 bSuccess = TRUE;
1077 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1079 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
1081 else
1082 FTP_SetResponseError(nResCode);
1085 lend:
1086 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1088 INTERNET_ASYNC_RESULT iar;
1090 iar.dwResult = bSuccess;
1091 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1092 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1093 &iar, sizeof(INTERNET_ASYNC_RESULT));
1096 return bSuccess;
1100 /***********************************************************************
1101 * FTPFILE_Destroy(internal)
1103 * Closes the file transfer handle. This also 'cleans' the data queue of
1104 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1107 static void FTPFILE_Destroy(object_header_t *hdr)
1109 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1110 ftp_session_t *lpwfs = lpwh->lpFtpSession;
1111 INT nResCode;
1113 TRACE("\n");
1115 if (lpwh->cache_file_handle != INVALID_HANDLE_VALUE)
1116 CloseHandle(lpwh->cache_file_handle);
1118 HeapFree(GetProcessHeap(), 0, lpwh->cache_file);
1120 if (!lpwh->session_deleted)
1121 lpwfs->download_in_progress = NULL;
1123 if (lpwh->nDataSocket != -1)
1124 closesocket(lpwh->nDataSocket);
1126 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1127 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1129 WININET_Release(&lpwh->lpFtpSession->hdr);
1131 HeapFree(GetProcessHeap(), 0, lpwh);
1134 static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1136 switch(option) {
1137 case INTERNET_OPTION_HANDLE_TYPE:
1138 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1140 if (*size < sizeof(ULONG))
1141 return ERROR_INSUFFICIENT_BUFFER;
1143 *size = sizeof(DWORD);
1144 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1145 return ERROR_SUCCESS;
1146 case INTERNET_OPTION_DATAFILE_NAME:
1148 DWORD required;
1149 ftp_file_t *file = (ftp_file_t *)hdr;
1151 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1153 if (!file->cache_file)
1155 *size = 0;
1156 return ERROR_INTERNET_ITEM_NOT_FOUND;
1158 if (unicode)
1160 required = (lstrlenW(file->cache_file) + 1) * sizeof(WCHAR);
1161 if (*size < required)
1162 return ERROR_INSUFFICIENT_BUFFER;
1164 *size = required;
1165 memcpy(buffer, file->cache_file, *size);
1166 return ERROR_SUCCESS;
1168 else
1170 required = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, NULL, 0, NULL, NULL);
1171 if (required > *size)
1172 return ERROR_INSUFFICIENT_BUFFER;
1174 *size = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, buffer, *size, NULL, NULL);
1175 return ERROR_SUCCESS;
1179 return INET_QueryOption(hdr, option, buffer, size, unicode);
1182 static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
1184 ftp_file_t *file = (ftp_file_t*)hdr;
1185 int res;
1186 DWORD error;
1188 if (file->nDataSocket == -1)
1189 return ERROR_INTERNET_DISCONNECTED;
1191 /* FIXME: FTP should use NETCON_ stuff */
1192 res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1193 *read = res>0 ? res : 0;
1195 error = res >= 0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME */
1196 if (error == ERROR_SUCCESS && file->cache_file)
1198 DWORD bytes_written;
1200 if (!WriteFile(file->cache_file_handle, buffer, *read, &bytes_written, NULL))
1201 WARN("WriteFile failed: %u\n", GetLastError());
1203 return error;
1206 static DWORD FTPFILE_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
1207 DWORD flags, DWORD_PTR context)
1209 return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1212 static DWORD FTPFILE_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
1213 DWORD flags, DWORD_PTR context)
1215 return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1218 static DWORD FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1220 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1221 int res;
1223 res = send(lpwh->nDataSocket, buffer, size, 0);
1225 *written = res>0 ? res : 0;
1226 return res >= 0 ? ERROR_SUCCESS : sock_get_error(errno);
1229 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
1231 INTERNET_ASYNC_RESULT iar;
1232 BYTE buffer[4096];
1233 int available;
1235 TRACE("%p\n", file);
1237 available = recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1239 if(available != -1) {
1240 iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1241 iar.dwError = first_notif ? 0 : available;
1242 }else {
1243 iar.dwResult = 0;
1244 iar.dwError = INTERNET_GetLastError();
1247 INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1248 sizeof(INTERNET_ASYNC_RESULT));
1251 static void FTPFILE_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
1253 ftp_file_t *file = (ftp_file_t*)workRequest->hdr;
1255 FTP_ReceiveRequestData(file, FALSE);
1258 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1260 ftp_file_t *file = (ftp_file_t*) hdr;
1261 int retval, unread = 0;
1263 TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
1265 #ifdef FIONREAD
1266 retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
1267 if (!retval)
1268 TRACE("%d bytes of queued, but unread data\n", unread);
1269 #else
1270 FIXME("FIONREAD not available\n");
1271 #endif
1273 *available = unread;
1275 if(!unread) {
1276 BYTE byte;
1278 *available = 0;
1280 retval = recv(file->nDataSocket, &byte, 1, MSG_PEEK);
1281 if(retval > 0) {
1282 WORKREQUEST workRequest;
1284 *available = 0;
1285 workRequest.asyncproc = FTPFILE_AsyncQueryDataAvailableProc;
1286 workRequest.hdr = WININET_AddRef( &file->hdr );
1288 INTERNET_AsyncCall(&workRequest);
1290 return ERROR_IO_PENDING;
1294 return ERROR_SUCCESS;
1298 static const object_vtbl_t FTPFILEVtbl = {
1299 FTPFILE_Destroy,
1300 NULL,
1301 FTPFILE_QueryOption,
1302 NULL,
1303 FTPFILE_ReadFile,
1304 FTPFILE_ReadFileExA,
1305 FTPFILE_ReadFileExW,
1306 FTPFILE_WriteFile,
1307 FTPFILE_QueryDataAvailable,
1308 NULL
1311 /***********************************************************************
1312 * FTP_FtpOpenFileW (Internal)
1314 * Open a remote file for writing or reading
1316 * RETURNS
1317 * HINTERNET handle on success
1318 * NULL on failure
1321 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1322 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1323 DWORD_PTR dwContext)
1325 INT nDataSocket;
1326 BOOL bSuccess = FALSE;
1327 ftp_file_t *lpwh = NULL;
1328 appinfo_t *hIC = NULL;
1329 HINTERNET handle = NULL;
1331 TRACE("\n");
1333 /* Clear any error information */
1334 INTERNET_SetLastError(0);
1336 if (GENERIC_READ == fdwAccess)
1338 /* Set up socket to retrieve data */
1339 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1341 else if (GENERIC_WRITE == fdwAccess)
1343 /* Set up socket to send data */
1344 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1347 /* Get data socket to server */
1348 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1350 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(ftp_file_t));
1351 lpwh->hdr.htype = WH_HFILE;
1352 lpwh->hdr.vtbl = &FTPFILEVtbl;
1353 lpwh->hdr.dwFlags = dwFlags;
1354 lpwh->hdr.dwContext = dwContext;
1355 lpwh->hdr.dwInternalFlags = 0;
1356 lpwh->hdr.refs = 1;
1357 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1358 lpwh->nDataSocket = nDataSocket;
1359 lpwh->cache_file = NULL;
1360 lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1361 lpwh->session_deleted = FALSE;
1363 WININET_AddRef( &lpwfs->hdr );
1364 lpwh->lpFtpSession = lpwfs;
1365 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1367 handle = WININET_AllocHandle( &lpwh->hdr );
1368 if( !handle )
1369 goto lend;
1371 /* Indicate that a download is currently in progress */
1372 lpwfs->download_in_progress = lpwh;
1375 if (lpwfs->lstnSocket != -1)
1377 closesocket(lpwfs->lstnSocket);
1378 lpwfs->lstnSocket = -1;
1381 if (bSuccess && fdwAccess == GENERIC_READ)
1383 WCHAR filename[MAX_PATH + 1];
1384 URL_COMPONENTSW uc;
1385 DWORD len;
1387 memset(&uc, 0, sizeof(uc));
1388 uc.dwStructSize = sizeof(uc);
1389 uc.nScheme = INTERNET_SCHEME_FTP;
1390 uc.lpszHostName = lpwfs->servername;
1391 uc.nPort = lpwfs->serverport;
1392 uc.lpszUserName = lpwfs->lpszUserName;
1393 uc.lpszUrlPath = heap_strdupW(lpszFileName);
1395 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1397 WCHAR *url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1399 if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1401 lpwh->cache_file = heap_strdupW(filename);
1402 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1403 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1404 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1406 WARN("Could not create cache file: %u\n", GetLastError());
1407 HeapFree(GetProcessHeap(), 0, lpwh->cache_file);
1408 lpwh->cache_file = NULL;
1411 HeapFree(GetProcessHeap(), 0, url);
1413 HeapFree(GetProcessHeap(), 0, uc.lpszUrlPath);
1416 hIC = lpwfs->lpAppInfo;
1417 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1419 INTERNET_ASYNC_RESULT iar;
1421 if (lpwh)
1423 iar.dwResult = (DWORD_PTR)handle;
1424 iar.dwError = ERROR_SUCCESS;
1425 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1426 &iar, sizeof(INTERNET_ASYNC_RESULT));
1429 if(bSuccess) {
1430 FTP_ReceiveRequestData(lpwh, TRUE);
1431 }else {
1432 iar.dwResult = 0;
1433 iar.dwError = INTERNET_GetLastError();
1434 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1435 &iar, sizeof(INTERNET_ASYNC_RESULT));
1439 lend:
1440 if( lpwh )
1441 WININET_Release( &lpwh->hdr );
1443 return handle;
1447 /***********************************************************************
1448 * FtpOpenFileA (WININET.@)
1450 * Open a remote file for writing or reading
1452 * RETURNS
1453 * HINTERNET handle on success
1454 * NULL on failure
1457 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1458 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1459 DWORD_PTR dwContext)
1461 LPWSTR lpwzFileName;
1462 HINTERNET ret;
1464 lpwzFileName = heap_strdupAtoW(lpszFileName);
1465 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1466 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1467 return ret;
1471 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1473 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1474 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1476 TRACE("%p\n", lpwfs);
1478 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1479 req->dwAccess, req->dwFlags, req->dwContext);
1480 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1483 /***********************************************************************
1484 * FtpOpenFileW (WININET.@)
1486 * Open a remote file for writing or reading
1488 * RETURNS
1489 * HINTERNET handle on success
1490 * NULL on failure
1493 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1494 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1495 DWORD_PTR dwContext)
1497 ftp_session_t *lpwfs;
1498 appinfo_t *hIC = NULL;
1499 HINTERNET r = NULL;
1501 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1502 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1504 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1505 if (!lpwfs)
1507 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1508 return FALSE;
1511 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1513 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1514 goto lend;
1517 if ((!lpszFileName) ||
1518 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1519 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1521 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1522 goto lend;
1525 if (lpwfs->download_in_progress != NULL)
1527 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1528 goto lend;
1531 hIC = lpwfs->lpAppInfo;
1532 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1534 WORKREQUEST workRequest;
1535 struct WORKREQ_FTPOPENFILEW *req;
1537 workRequest.asyncproc = AsyncFtpOpenFileProc;
1538 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1539 req = &workRequest.u.FtpOpenFileW;
1540 req->lpszFilename = heap_strdupW(lpszFileName);
1541 req->dwAccess = fdwAccess;
1542 req->dwFlags = dwFlags;
1543 req->dwContext = dwContext;
1545 INTERNET_AsyncCall(&workRequest);
1546 r = NULL;
1548 else
1550 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1553 lend:
1554 WININET_Release( &lpwfs->hdr );
1556 return r;
1560 /***********************************************************************
1561 * FtpGetFileA (WININET.@)
1563 * Retrieve file from the FTP server
1565 * RETURNS
1566 * TRUE on success
1567 * FALSE on failure
1570 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1571 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1572 DWORD_PTR dwContext)
1574 LPWSTR lpwzRemoteFile;
1575 LPWSTR lpwzNewFile;
1576 BOOL ret;
1578 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1579 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1580 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1581 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1582 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1583 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1584 return ret;
1588 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1590 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1591 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1593 TRACE("%p\n", lpwfs);
1595 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1596 req->lpszNewFile, req->fFailIfExists,
1597 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1598 HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1599 HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1603 /***********************************************************************
1604 * FtpGetFileW (WININET.@)
1606 * Retrieve file from the FTP server
1608 * RETURNS
1609 * TRUE on success
1610 * FALSE on failure
1613 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1614 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1615 DWORD_PTR dwContext)
1617 ftp_session_t *lpwfs;
1618 appinfo_t *hIC = NULL;
1619 BOOL r = FALSE;
1621 if (!lpszRemoteFile || !lpszNewFile)
1623 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1624 return FALSE;
1627 lpwfs = (ftp_session_t*) WININET_GetObject( hInternet );
1628 if (!lpwfs)
1630 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1631 return FALSE;
1634 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1636 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1637 goto lend;
1640 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1642 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1643 goto lend;
1646 if (lpwfs->download_in_progress != NULL)
1648 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1649 goto lend;
1652 hIC = lpwfs->lpAppInfo;
1653 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1655 WORKREQUEST workRequest;
1656 struct WORKREQ_FTPGETFILEW *req;
1658 workRequest.asyncproc = AsyncFtpGetFileProc;
1659 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1660 req = &workRequest.u.FtpGetFileW;
1661 req->lpszRemoteFile = heap_strdupW(lpszRemoteFile);
1662 req->lpszNewFile = heap_strdupW(lpszNewFile);
1663 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1664 req->fFailIfExists = fFailIfExists;
1665 req->dwFlags = dwInternetFlags;
1666 req->dwContext = dwContext;
1668 r = res_to_le(INTERNET_AsyncCall(&workRequest));
1670 else
1672 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1673 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1676 lend:
1677 WININET_Release( &lpwfs->hdr );
1679 return r;
1683 /***********************************************************************
1684 * FTP_FtpGetFileW (Internal)
1686 * Retrieve file from the FTP server
1688 * RETURNS
1689 * TRUE on success
1690 * FALSE on failure
1693 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1694 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1695 DWORD_PTR dwContext)
1697 BOOL bSuccess = FALSE;
1698 HANDLE hFile;
1699 appinfo_t *hIC = NULL;
1701 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1703 /* Clear any error information */
1704 INTERNET_SetLastError(0);
1706 /* Ensure we can write to lpszNewfile by opening it */
1707 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1708 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1709 if (INVALID_HANDLE_VALUE == hFile)
1710 return FALSE;
1712 /* Set up socket to retrieve data */
1713 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1715 INT nDataSocket;
1717 /* Get data socket to server */
1718 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1720 INT nResCode;
1722 /* Receive data */
1723 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1724 closesocket(nDataSocket);
1726 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1727 if (nResCode)
1729 if (nResCode == 226)
1730 bSuccess = TRUE;
1731 else
1732 FTP_SetResponseError(nResCode);
1737 if (lpwfs->lstnSocket != -1)
1739 closesocket(lpwfs->lstnSocket);
1740 lpwfs->lstnSocket = -1;
1743 CloseHandle(hFile);
1745 hIC = lpwfs->lpAppInfo;
1746 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1748 INTERNET_ASYNC_RESULT iar;
1750 iar.dwResult = (DWORD)bSuccess;
1751 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1752 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1753 &iar, sizeof(INTERNET_ASYNC_RESULT));
1756 if (!bSuccess) DeleteFileW(lpszNewFile);
1757 return bSuccess;
1760 /***********************************************************************
1761 * FtpGetFileSize (WININET.@)
1763 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1765 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1767 if (lpdwFileSizeHigh)
1768 *lpdwFileSizeHigh = 0;
1770 return 0;
1773 /***********************************************************************
1774 * FtpDeleteFileA (WININET.@)
1776 * Delete a file on the ftp server
1778 * RETURNS
1779 * TRUE on success
1780 * FALSE on failure
1783 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1785 LPWSTR lpwzFileName;
1786 BOOL ret;
1788 lpwzFileName = heap_strdupAtoW(lpszFileName);
1789 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1790 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1791 return ret;
1794 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1796 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1797 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1799 TRACE("%p\n", lpwfs);
1801 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1802 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1805 /***********************************************************************
1806 * FtpDeleteFileW (WININET.@)
1808 * Delete a file on the ftp server
1810 * RETURNS
1811 * TRUE on success
1812 * FALSE on failure
1815 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1817 ftp_session_t *lpwfs;
1818 appinfo_t *hIC = NULL;
1819 BOOL r = FALSE;
1821 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1822 if (!lpwfs)
1824 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1825 return FALSE;
1828 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1830 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1831 goto lend;
1834 if (lpwfs->download_in_progress != NULL)
1836 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1837 goto lend;
1840 if (!lpszFileName)
1842 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1843 goto lend;
1846 hIC = lpwfs->lpAppInfo;
1847 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1849 WORKREQUEST workRequest;
1850 struct WORKREQ_FTPDELETEFILEW *req;
1852 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1853 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1854 req = &workRequest.u.FtpDeleteFileW;
1855 req->lpszFilename = heap_strdupW(lpszFileName);
1857 r = res_to_le(INTERNET_AsyncCall(&workRequest));
1859 else
1861 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1864 lend:
1865 WININET_Release( &lpwfs->hdr );
1867 return r;
1870 /***********************************************************************
1871 * FTP_FtpDeleteFileW (Internal)
1873 * Delete a file on the ftp server
1875 * RETURNS
1876 * TRUE on success
1877 * FALSE on failure
1880 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1882 INT nResCode;
1883 BOOL bSuccess = FALSE;
1884 appinfo_t *hIC = NULL;
1886 TRACE("%p\n", lpwfs);
1888 /* Clear any error information */
1889 INTERNET_SetLastError(0);
1891 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1892 goto lend;
1894 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1895 if (nResCode)
1897 if (nResCode == 250)
1898 bSuccess = TRUE;
1899 else
1900 FTP_SetResponseError(nResCode);
1902 lend:
1903 hIC = lpwfs->lpAppInfo;
1904 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1906 INTERNET_ASYNC_RESULT iar;
1908 iar.dwResult = (DWORD)bSuccess;
1909 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1910 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1911 &iar, sizeof(INTERNET_ASYNC_RESULT));
1914 return bSuccess;
1918 /***********************************************************************
1919 * FtpRemoveDirectoryA (WININET.@)
1921 * Remove a directory on the ftp server
1923 * RETURNS
1924 * TRUE on success
1925 * FALSE on failure
1928 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1930 LPWSTR lpwzDirectory;
1931 BOOL ret;
1933 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1934 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1935 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1936 return ret;
1939 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1941 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1942 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1944 TRACE("%p\n", lpwfs);
1946 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1947 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1950 /***********************************************************************
1951 * FtpRemoveDirectoryW (WININET.@)
1953 * Remove a directory on the ftp server
1955 * RETURNS
1956 * TRUE on success
1957 * FALSE on failure
1960 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1962 ftp_session_t *lpwfs;
1963 appinfo_t *hIC = NULL;
1964 BOOL r = FALSE;
1966 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1967 if (!lpwfs)
1969 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1970 return FALSE;
1973 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1975 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1976 goto lend;
1979 if (lpwfs->download_in_progress != NULL)
1981 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1982 goto lend;
1985 if (!lpszDirectory)
1987 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1988 goto lend;
1991 hIC = lpwfs->lpAppInfo;
1992 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1994 WORKREQUEST workRequest;
1995 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1997 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1998 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1999 req = &workRequest.u.FtpRemoveDirectoryW;
2000 req->lpszDirectory = heap_strdupW(lpszDirectory);
2002 r = res_to_le(INTERNET_AsyncCall(&workRequest));
2004 else
2006 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
2009 lend:
2010 WININET_Release( &lpwfs->hdr );
2012 return r;
2015 /***********************************************************************
2016 * FTP_FtpRemoveDirectoryW (Internal)
2018 * Remove a directory on the ftp server
2020 * RETURNS
2021 * TRUE on success
2022 * FALSE on failure
2025 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
2027 INT nResCode;
2028 BOOL bSuccess = FALSE;
2029 appinfo_t *hIC = NULL;
2031 TRACE("\n");
2033 /* Clear any error information */
2034 INTERNET_SetLastError(0);
2036 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2037 goto lend;
2039 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2040 if (nResCode)
2042 if (nResCode == 250)
2043 bSuccess = TRUE;
2044 else
2045 FTP_SetResponseError(nResCode);
2048 lend:
2049 hIC = lpwfs->lpAppInfo;
2050 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2052 INTERNET_ASYNC_RESULT iar;
2054 iar.dwResult = (DWORD)bSuccess;
2055 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2056 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2057 &iar, sizeof(INTERNET_ASYNC_RESULT));
2060 return bSuccess;
2064 /***********************************************************************
2065 * FtpRenameFileA (WININET.@)
2067 * Rename a file on the ftp server
2069 * RETURNS
2070 * TRUE on success
2071 * FALSE on failure
2074 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2076 LPWSTR lpwzSrc;
2077 LPWSTR lpwzDest;
2078 BOOL ret;
2080 lpwzSrc = heap_strdupAtoW(lpszSrc);
2081 lpwzDest = heap_strdupAtoW(lpszDest);
2082 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2083 HeapFree(GetProcessHeap(), 0, lpwzSrc);
2084 HeapFree(GetProcessHeap(), 0, lpwzDest);
2085 return ret;
2088 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
2090 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
2091 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
2093 TRACE("%p\n", lpwfs);
2095 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
2096 HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
2097 HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
2100 /***********************************************************************
2101 * FtpRenameFileW (WININET.@)
2103 * Rename a file on the ftp server
2105 * RETURNS
2106 * TRUE on success
2107 * FALSE on failure
2110 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2112 ftp_session_t *lpwfs;
2113 appinfo_t *hIC = NULL;
2114 BOOL r = FALSE;
2116 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
2117 if (!lpwfs)
2119 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2120 return FALSE;
2123 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2125 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2126 goto lend;
2129 if (lpwfs->download_in_progress != NULL)
2131 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2132 goto lend;
2135 if (!lpszSrc || !lpszDest)
2137 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2138 goto lend;
2141 hIC = lpwfs->lpAppInfo;
2142 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2144 WORKREQUEST workRequest;
2145 struct WORKREQ_FTPRENAMEFILEW *req;
2147 workRequest.asyncproc = AsyncFtpRenameFileProc;
2148 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
2149 req = &workRequest.u.FtpRenameFileW;
2150 req->lpszSrcFile = heap_strdupW(lpszSrc);
2151 req->lpszDestFile = heap_strdupW(lpszDest);
2153 r = res_to_le(INTERNET_AsyncCall(&workRequest));
2155 else
2157 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2160 lend:
2161 WININET_Release( &lpwfs->hdr );
2163 return r;
2166 /***********************************************************************
2167 * FTP_FtpRenameFileW (Internal)
2169 * Rename a file on the ftp server
2171 * RETURNS
2172 * TRUE on success
2173 * FALSE on failure
2176 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2178 INT nResCode;
2179 BOOL bSuccess = FALSE;
2180 appinfo_t *hIC = NULL;
2182 TRACE("\n");
2184 /* Clear any error information */
2185 INTERNET_SetLastError(0);
2187 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2188 goto lend;
2190 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2191 if (nResCode == 350)
2193 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2194 goto lend;
2196 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2199 if (nResCode == 250)
2200 bSuccess = TRUE;
2201 else
2202 FTP_SetResponseError(nResCode);
2204 lend:
2205 hIC = lpwfs->lpAppInfo;
2206 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2208 INTERNET_ASYNC_RESULT iar;
2210 iar.dwResult = (DWORD)bSuccess;
2211 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2212 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2213 &iar, sizeof(INTERNET_ASYNC_RESULT));
2216 return bSuccess;
2219 /***********************************************************************
2220 * FtpCommandA (WININET.@)
2222 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2223 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2225 BOOL r;
2226 WCHAR *cmdW;
2228 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2229 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2231 if (fExpectResponse)
2233 FIXME("data connection not supported\n");
2234 return FALSE;
2237 if (!lpszCommand || !lpszCommand[0])
2239 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2240 return FALSE;
2243 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2245 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2246 return FALSE;
2249 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2251 HeapFree(GetProcessHeap(), 0, cmdW);
2252 return r;
2255 /***********************************************************************
2256 * FtpCommandW (WININET.@)
2258 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2259 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2261 BOOL r = FALSE;
2262 ftp_session_t *lpwfs;
2263 LPSTR cmd = NULL;
2264 DWORD len, nBytesSent= 0;
2265 INT nResCode, nRC = 0;
2267 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2268 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2270 if (!lpszCommand || !lpszCommand[0])
2272 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2273 return FALSE;
2276 if (fExpectResponse)
2278 FIXME("data connection not supported\n");
2279 return FALSE;
2282 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
2283 if (!lpwfs)
2285 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2286 return FALSE;
2289 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2291 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2292 goto lend;
2295 if (lpwfs->download_in_progress != NULL)
2297 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2298 goto lend;
2301 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2302 if ((cmd = HeapAlloc(GetProcessHeap(), 0, len )))
2303 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2304 else
2306 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2307 goto lend;
2310 strcat(cmd, szCRLF);
2311 len--;
2313 TRACE("Sending (%s) len(%d)\n", cmd, len);
2314 while ((nBytesSent < len) && (nRC != -1))
2316 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2317 if (nRC != -1)
2319 nBytesSent += nRC;
2320 TRACE("Sent %d bytes\n", nRC);
2324 if (nBytesSent)
2326 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2327 if (nResCode > 0 && nResCode < 400)
2328 r = TRUE;
2329 else
2330 FTP_SetResponseError(nResCode);
2333 lend:
2334 WININET_Release( &lpwfs->hdr );
2335 HeapFree(GetProcessHeap(), 0, cmd);
2336 return r;
2340 /***********************************************************************
2341 * FTPSESSION_Destroy (internal)
2343 * Deallocate session handle
2345 static void FTPSESSION_Destroy(object_header_t *hdr)
2347 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2349 TRACE("\n");
2351 WININET_Release(&lpwfs->lpAppInfo->hdr);
2353 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2354 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2355 HeapFree(GetProcessHeap(), 0, lpwfs->servername);
2356 HeapFree(GetProcessHeap(), 0, lpwfs);
2359 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2361 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2363 TRACE("\n");
2365 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2366 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2368 if (lpwfs->download_in_progress != NULL)
2369 lpwfs->download_in_progress->session_deleted = TRUE;
2371 if (lpwfs->sndSocket != -1)
2372 closesocket(lpwfs->sndSocket);
2374 if (lpwfs->lstnSocket != -1)
2375 closesocket(lpwfs->lstnSocket);
2377 if (lpwfs->pasvSocket != -1)
2378 closesocket(lpwfs->pasvSocket);
2380 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2381 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2384 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2386 switch(option) {
2387 case INTERNET_OPTION_HANDLE_TYPE:
2388 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2390 if (*size < sizeof(ULONG))
2391 return ERROR_INSUFFICIENT_BUFFER;
2393 *size = sizeof(DWORD);
2394 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2395 return ERROR_SUCCESS;
2398 return INET_QueryOption(hdr, option, buffer, size, unicode);
2401 static const object_vtbl_t FTPSESSIONVtbl = {
2402 FTPSESSION_Destroy,
2403 FTPSESSION_CloseConnection,
2404 FTPSESSION_QueryOption,
2405 NULL,
2406 NULL,
2407 NULL,
2408 NULL,
2409 NULL,
2410 NULL
2414 /***********************************************************************
2415 * FTP_Connect (internal)
2417 * Connect to a ftp server
2419 * RETURNS
2420 * HINTERNET a session handle on success
2421 * NULL on failure
2423 * NOTES:
2425 * Windows uses 'anonymous' as the username, when given a NULL username
2426 * and a NULL password. The password is first looked up in:
2428 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2430 * If this entry is not present it uses the current username as the password.
2434 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2435 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2436 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2437 DWORD dwInternalFlags)
2439 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2440 'M','i','c','r','o','s','o','f','t','\\',
2441 'W','i','n','d','o','w','s','\\',
2442 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2443 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2444 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2445 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2446 static const WCHAR szEmpty[] = {'\0'};
2447 struct sockaddr_in socketAddr;
2448 INT nsocket = -1;
2449 UINT sock_namelen;
2450 BOOL bSuccess = FALSE;
2451 ftp_session_t *lpwfs = NULL;
2452 HINTERNET handle = NULL;
2453 char szaddr[INET_ADDRSTRLEN];
2455 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2456 hIC, debugstr_w(lpszServerName),
2457 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2459 assert( hIC->hdr.htype == WH_HINIT );
2461 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2463 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2464 goto lerror;
2467 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(ftp_session_t));
2468 if (NULL == lpwfs)
2470 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2471 goto lerror;
2474 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2475 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2476 else
2477 lpwfs->serverport = nServerPort;
2479 lpwfs->hdr.htype = WH_HFTPSESSION;
2480 lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
2481 lpwfs->hdr.dwFlags = dwFlags;
2482 lpwfs->hdr.dwContext = dwContext;
2483 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2484 lpwfs->hdr.refs = 1;
2485 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2486 lpwfs->download_in_progress = NULL;
2487 lpwfs->sndSocket = -1;
2488 lpwfs->lstnSocket = -1;
2489 lpwfs->pasvSocket = -1;
2491 WININET_AddRef( &hIC->hdr );
2492 lpwfs->lpAppInfo = hIC;
2493 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2495 handle = WININET_AllocHandle( &lpwfs->hdr );
2496 if( !handle )
2498 ERR("Failed to alloc handle\n");
2499 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2500 goto lerror;
2503 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2504 if(strchrW(hIC->lpszProxy, ' '))
2505 FIXME("Several proxies not implemented.\n");
2506 if(hIC->lpszProxyBypass)
2507 FIXME("Proxy bypass is ignored.\n");
2509 if (!lpszUserName || !strlenW(lpszUserName)) {
2510 HKEY key;
2511 WCHAR szPassword[MAX_PATH];
2512 DWORD len = sizeof(szPassword);
2514 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2516 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2517 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2518 /* Nothing in the registry, get the username and use that as the password */
2519 if (!GetUserNameW(szPassword, &len)) {
2520 /* Should never get here, but use an empty password as failsafe */
2521 strcpyW(szPassword, szEmpty);
2524 RegCloseKey(key);
2526 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2527 lpwfs->lpszPassword = heap_strdupW(szPassword);
2529 else {
2530 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2531 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2533 lpwfs->servername = heap_strdupW(lpszServerName);
2535 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2536 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2538 INTERNET_ASYNC_RESULT iar;
2540 iar.dwResult = (DWORD_PTR)handle;
2541 iar.dwError = ERROR_SUCCESS;
2543 SendAsyncCallback(&hIC->hdr, dwContext,
2544 INTERNET_STATUS_HANDLE_CREATED, &iar,
2545 sizeof(INTERNET_ASYNC_RESULT));
2548 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2549 (LPWSTR) lpszServerName, (strlenW(lpszServerName)+1) * sizeof(WCHAR));
2551 sock_namelen = sizeof(socketAddr);
2552 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen))
2554 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2555 goto lerror;
2558 if (socketAddr.sin_family != AF_INET)
2560 WARN("unsupported address family %d\n", socketAddr.sin_family);
2561 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2562 goto lerror;
2565 inet_ntop(socketAddr.sin_family, &socketAddr.sin_addr, szaddr, sizeof(szaddr));
2566 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2567 szaddr, strlen(szaddr)+1);
2569 nsocket = socket(AF_INET,SOCK_STREAM,0);
2570 if (nsocket == -1)
2572 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2573 goto lerror;
2576 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2577 szaddr, strlen(szaddr)+1);
2579 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2581 ERR("Unable to connect (%s)\n", strerror(errno));
2582 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2583 closesocket(nsocket);
2585 else
2587 TRACE("Connected to server\n");
2588 lpwfs->sndSocket = nsocket;
2589 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2590 szaddr, strlen(szaddr)+1);
2592 sock_namelen = sizeof(lpwfs->socketAddress);
2593 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2595 if (FTP_ConnectToHost(lpwfs))
2597 TRACE("Successfully logged into server\n");
2598 bSuccess = TRUE;
2602 lerror:
2603 if (lpwfs) WININET_Release( &lpwfs->hdr );
2605 if (!bSuccess && handle)
2607 WININET_FreeHandle( handle );
2608 handle = NULL;
2611 return handle;
2615 /***********************************************************************
2616 * FTP_ConnectToHost (internal)
2618 * Connect to a ftp server
2620 * RETURNS
2621 * TRUE on success
2622 * NULL on failure
2625 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2627 INT nResCode;
2628 BOOL bSuccess = FALSE;
2630 TRACE("\n");
2631 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2633 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2634 goto lend;
2636 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2637 if (nResCode)
2639 /* Login successful... */
2640 if (nResCode == 230)
2641 bSuccess = TRUE;
2642 /* User name okay, need password... */
2643 else if (nResCode == 331)
2644 bSuccess = FTP_SendPassword(lpwfs);
2645 /* Need account for login... */
2646 else if (nResCode == 332)
2647 bSuccess = FTP_SendAccount(lpwfs);
2648 else
2649 FTP_SetResponseError(nResCode);
2652 TRACE("Returning %d\n", bSuccess);
2653 lend:
2654 return bSuccess;
2658 /***********************************************************************
2659 * FTP_SendCommandA (internal)
2661 * Send command to server
2663 * RETURNS
2664 * TRUE on success
2665 * NULL on failure
2668 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2669 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2671 DWORD len;
2672 CHAR *buf;
2673 DWORD nBytesSent = 0;
2674 int nRC = 0;
2675 DWORD dwParamLen;
2677 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2679 if (lpfnStatusCB)
2681 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2684 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2685 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2686 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2688 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2689 return FALSE;
2691 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2692 dwParamLen ? lpszParam : "", szCRLF);
2694 TRACE("Sending (%s) len(%d)\n", buf, len);
2695 while((nBytesSent < len) && (nRC != -1))
2697 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2698 nBytesSent += nRC;
2701 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2703 if (lpfnStatusCB)
2705 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2706 &nBytesSent, sizeof(DWORD));
2709 TRACE("Sent %d bytes\n", nBytesSent);
2710 return (nRC != -1);
2713 /***********************************************************************
2714 * FTP_SendCommand (internal)
2716 * Send command to server
2718 * RETURNS
2719 * TRUE on success
2720 * NULL on failure
2723 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2724 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2726 BOOL ret;
2727 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2728 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2729 HeapFree(GetProcessHeap(), 0, lpszParamA);
2730 return ret;
2733 /***********************************************************************
2734 * FTP_ReceiveResponse (internal)
2736 * Receive response from server
2738 * RETURNS
2739 * Reply code on success
2740 * 0 on failure
2743 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2745 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2746 DWORD nRecv;
2747 INT rc = 0;
2748 char firstprefix[5];
2749 BOOL multiline = FALSE;
2751 TRACE("socket(%d)\n", lpwfs->sndSocket);
2753 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2755 while(1)
2757 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2758 goto lerror;
2760 if (nRecv >= 3)
2762 if(!multiline)
2764 if(lpszResponse[3] != '-')
2765 break;
2766 else
2767 { /* Start of multiline response. Loop until we get "nnn " */
2768 multiline = TRUE;
2769 memcpy(firstprefix, lpszResponse, 3);
2770 firstprefix[3] = ' ';
2771 firstprefix[4] = '\0';
2774 else
2776 if(!memcmp(firstprefix, lpszResponse, 4))
2777 break;
2782 if (nRecv >= 3)
2784 rc = atoi(lpszResponse);
2786 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2787 &nRecv, sizeof(DWORD));
2790 lerror:
2791 TRACE("return %d\n", rc);
2792 return rc;
2796 /***********************************************************************
2797 * FTP_SendPassword (internal)
2799 * Send password to ftp server
2801 * RETURNS
2802 * TRUE on success
2803 * NULL on failure
2806 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2808 INT nResCode;
2809 BOOL bSuccess = FALSE;
2811 TRACE("\n");
2812 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2813 goto lend;
2815 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2816 if (nResCode)
2818 TRACE("Received reply code %d\n", nResCode);
2819 /* Login successful... */
2820 if (nResCode == 230)
2821 bSuccess = TRUE;
2822 /* Command not implemented, superfluous at the server site... */
2823 /* Need account for login... */
2824 else if (nResCode == 332)
2825 bSuccess = FTP_SendAccount(lpwfs);
2826 else
2827 FTP_SetResponseError(nResCode);
2830 lend:
2831 TRACE("Returning %d\n", bSuccess);
2832 return bSuccess;
2836 /***********************************************************************
2837 * FTP_SendAccount (internal)
2841 * RETURNS
2842 * TRUE on success
2843 * FALSE on failure
2846 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2848 INT nResCode;
2849 BOOL bSuccess = FALSE;
2851 TRACE("\n");
2852 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2853 goto lend;
2855 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2856 if (nResCode)
2857 bSuccess = TRUE;
2858 else
2859 FTP_SetResponseError(nResCode);
2861 lend:
2862 return bSuccess;
2866 /***********************************************************************
2867 * FTP_SendStore (internal)
2869 * Send request to upload file to ftp server
2871 * RETURNS
2872 * TRUE on success
2873 * FALSE on failure
2876 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2878 INT nResCode;
2879 BOOL bSuccess = FALSE;
2881 TRACE("\n");
2882 if (!FTP_InitListenSocket(lpwfs))
2883 goto lend;
2885 if (!FTP_SendType(lpwfs, dwType))
2886 goto lend;
2888 if (!FTP_SendPortOrPasv(lpwfs))
2889 goto lend;
2891 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2892 goto lend;
2893 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2894 if (nResCode)
2896 if (nResCode == 150 || nResCode == 125)
2897 bSuccess = TRUE;
2898 else
2899 FTP_SetResponseError(nResCode);
2902 lend:
2903 if (!bSuccess && lpwfs->lstnSocket != -1)
2905 closesocket(lpwfs->lstnSocket);
2906 lpwfs->lstnSocket = -1;
2909 return bSuccess;
2913 /***********************************************************************
2914 * FTP_InitListenSocket (internal)
2916 * Create a socket to listen for server response
2918 * RETURNS
2919 * TRUE on success
2920 * FALSE on failure
2923 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2925 BOOL bSuccess = FALSE;
2926 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2928 TRACE("\n");
2930 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2931 if (lpwfs->lstnSocket == -1)
2933 TRACE("Unable to create listening socket\n");
2934 goto lend;
2937 /* We obtain our ip addr from the name of the command channel socket */
2938 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2940 /* and get the system to assign us a port */
2941 lpwfs->lstnSocketAddress.sin_port = htons(0);
2943 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2945 TRACE("Unable to bind socket\n");
2946 goto lend;
2949 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2951 TRACE("listen failed\n");
2952 goto lend;
2955 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2956 bSuccess = TRUE;
2958 lend:
2959 if (!bSuccess && lpwfs->lstnSocket != -1)
2961 closesocket(lpwfs->lstnSocket);
2962 lpwfs->lstnSocket = -1;
2965 return bSuccess;
2969 /***********************************************************************
2970 * FTP_SendType (internal)
2972 * Tell server type of data being transferred
2974 * RETURNS
2975 * TRUE on success
2976 * FALSE on failure
2978 * W98SE doesn't cache the type that's currently set
2979 * (i.e. it sends it always),
2980 * so we probably don't want to do that either.
2982 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2984 INT nResCode;
2985 WCHAR type[] = { 'I','\0' };
2986 BOOL bSuccess = FALSE;
2988 TRACE("\n");
2989 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2990 type[0] = 'A';
2992 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2993 goto lend;
2995 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2996 if (nResCode)
2998 if (nResCode == 2)
2999 bSuccess = TRUE;
3000 else
3001 FTP_SetResponseError(nResCode);
3004 lend:
3005 return bSuccess;
3009 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
3010 /***********************************************************************
3011 * FTP_GetFileSize (internal)
3013 * Retrieves from the server the size of the given file
3015 * RETURNS
3016 * TRUE on success
3017 * FALSE on failure
3020 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
3022 INT nResCode;
3023 BOOL bSuccess = FALSE;
3025 TRACE("\n");
3027 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
3028 goto lend;
3030 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3031 if (nResCode)
3033 if (nResCode == 213) {
3034 /* Now parses the output to get the actual file size */
3035 int i;
3036 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3038 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3039 if (lpszResponseBuffer[i] == '\0') return FALSE;
3040 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3042 bSuccess = TRUE;
3043 } else {
3044 FTP_SetResponseError(nResCode);
3048 lend:
3049 return bSuccess;
3051 #endif
3054 /***********************************************************************
3055 * FTP_SendPort (internal)
3057 * Tell server which port to use
3059 * RETURNS
3060 * TRUE on success
3061 * FALSE on failure
3064 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3066 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3067 INT nResCode;
3068 WCHAR szIPAddress[64];
3069 BOOL bSuccess = FALSE;
3070 TRACE("\n");
3072 sprintfW(szIPAddress, szIPFormat,
3073 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
3074 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
3075 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
3076 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
3077 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3078 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3080 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3081 goto lend;
3083 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3084 if (nResCode)
3086 if (nResCode == 200)
3087 bSuccess = TRUE;
3088 else
3089 FTP_SetResponseError(nResCode);
3092 lend:
3093 return bSuccess;
3097 /***********************************************************************
3098 * FTP_DoPassive (internal)
3100 * Tell server that we want to do passive transfers
3101 * and connect data socket
3103 * RETURNS
3104 * TRUE on success
3105 * FALSE on failure
3108 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3110 INT nResCode;
3111 BOOL bSuccess = FALSE;
3113 TRACE("\n");
3114 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3115 goto lend;
3117 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3118 if (nResCode)
3120 if (nResCode == 227)
3122 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3123 LPSTR p;
3124 int f[6];
3125 int i;
3126 char *pAddr, *pPort;
3127 INT nsocket = -1;
3128 struct sockaddr_in dataSocketAddress;
3130 p = lpszResponseBuffer+4; /* skip status code */
3131 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3133 if (*p == '\0')
3135 ERR("no address found in response, aborting\n");
3136 goto lend;
3139 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3140 &f[4], &f[5]) != 6)
3142 ERR("unknown response address format '%s', aborting\n", p);
3143 goto lend;
3145 for (i=0; i < 6; i++)
3146 f[i] = f[i] & 0xff;
3148 dataSocketAddress = lpwfs->socketAddress;
3149 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3150 pPort = (char *)&(dataSocketAddress.sin_port);
3151 pAddr[0] = f[0];
3152 pAddr[1] = f[1];
3153 pAddr[2] = f[2];
3154 pAddr[3] = f[3];
3155 pPort[0] = f[4];
3156 pPort[1] = f[5];
3158 nsocket = socket(AF_INET,SOCK_STREAM,0);
3159 if (nsocket == -1)
3160 goto lend;
3162 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3164 ERR("can't connect passive FTP data port.\n");
3165 closesocket(nsocket);
3166 goto lend;
3168 lpwfs->pasvSocket = nsocket;
3169 bSuccess = TRUE;
3171 else
3172 FTP_SetResponseError(nResCode);
3175 lend:
3176 return bSuccess;
3180 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3182 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3184 if (!FTP_DoPassive(lpwfs))
3185 return FALSE;
3187 else
3189 if (!FTP_SendPort(lpwfs))
3190 return FALSE;
3192 return TRUE;
3196 /***********************************************************************
3197 * FTP_GetDataSocket (internal)
3199 * Either accepts an incoming data socket connection from the server
3200 * or just returns the already opened socket after a PASV command
3201 * in case of passive FTP.
3204 * RETURNS
3205 * TRUE on success
3206 * FALSE on failure
3209 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3211 struct sockaddr_in saddr;
3212 socklen_t addrlen = sizeof(struct sockaddr);
3214 TRACE("\n");
3215 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3217 *nDataSocket = lpwfs->pasvSocket;
3218 lpwfs->pasvSocket = -1;
3220 else
3222 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3223 closesocket(lpwfs->lstnSocket);
3224 lpwfs->lstnSocket = -1;
3226 return *nDataSocket != -1;
3230 /***********************************************************************
3231 * FTP_SendData (internal)
3233 * Send data to the server
3235 * RETURNS
3236 * TRUE on success
3237 * FALSE on failure
3240 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3242 BY_HANDLE_FILE_INFORMATION fi;
3243 DWORD nBytesRead = 0;
3244 DWORD nBytesSent = 0;
3245 DWORD nTotalSent = 0;
3246 DWORD nBytesToSend, nLen;
3247 int nRC = 1;
3248 time_t s_long_time, e_long_time;
3249 LONG nSeconds;
3250 CHAR *lpszBuffer;
3252 TRACE("\n");
3253 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3255 /* Get the size of the file. */
3256 GetFileInformationByHandle(hFile, &fi);
3257 time(&s_long_time);
3261 nBytesToSend = nBytesRead - nBytesSent;
3263 if (nBytesToSend <= 0)
3265 /* Read data from file. */
3266 nBytesSent = 0;
3267 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3268 ERR("Failed reading from file\n");
3270 if (nBytesRead > 0)
3271 nBytesToSend = nBytesRead;
3272 else
3273 break;
3276 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3277 DATA_PACKET_SIZE : nBytesToSend;
3278 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3280 if (nRC != -1)
3282 nBytesSent += nRC;
3283 nTotalSent += nRC;
3286 /* Do some computation to display the status. */
3287 time(&e_long_time);
3288 nSeconds = e_long_time - s_long_time;
3289 if( nSeconds / 60 > 0 )
3291 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3292 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3293 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3295 else
3297 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3298 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3299 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3301 } while (nRC != -1);
3303 TRACE("file transfer complete!\n");
3305 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3307 return nTotalSent;
3311 /***********************************************************************
3312 * FTP_SendRetrieve (internal)
3314 * Send request to retrieve a file
3316 * RETURNS
3317 * Number of bytes to be received on success
3318 * 0 on failure
3321 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3323 INT nResCode;
3324 BOOL ret;
3326 TRACE("\n");
3327 if (!(ret = FTP_InitListenSocket(lpwfs)))
3328 goto lend;
3330 if (!(ret = FTP_SendType(lpwfs, dwType)))
3331 goto lend;
3333 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3334 goto lend;
3336 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3337 goto lend;
3339 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3340 if ((nResCode != 125) && (nResCode != 150)) {
3341 /* That means that we got an error getting the file. */
3342 FTP_SetResponseError(nResCode);
3343 ret = FALSE;
3346 lend:
3347 if (!ret && lpwfs->lstnSocket != -1)
3349 closesocket(lpwfs->lstnSocket);
3350 lpwfs->lstnSocket = -1;
3353 return ret;
3357 /***********************************************************************
3358 * FTP_RetrieveData (internal)
3360 * Retrieve data from server
3362 * RETURNS
3363 * TRUE on success
3364 * FALSE on failure
3367 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3369 DWORD nBytesWritten;
3370 INT nRC = 0;
3371 CHAR *lpszBuffer;
3373 TRACE("\n");
3375 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3376 if (NULL == lpszBuffer)
3378 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3379 return FALSE;
3382 while (nRC != -1)
3384 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3385 if (nRC != -1)
3387 /* other side closed socket. */
3388 if (nRC == 0)
3389 goto recv_end;
3390 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3394 TRACE("Data transfer complete\n");
3396 recv_end:
3397 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3399 return (nRC != -1);
3402 /***********************************************************************
3403 * FTPFINDNEXT_Destroy (internal)
3405 * Deallocate session handle
3407 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3409 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3410 DWORD i;
3412 TRACE("\n");
3414 WININET_Release(&lpwfn->lpFtpSession->hdr);
3416 for (i = 0; i < lpwfn->size; i++)
3418 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3421 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3422 HeapFree(GetProcessHeap(), 0, lpwfn);
3425 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3427 WIN32_FIND_DATAW *find_data = data;
3428 DWORD res = ERROR_SUCCESS;
3430 TRACE("index(%d) size(%d)\n", find->index, find->size);
3432 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3434 if (find->index < find->size) {
3435 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3436 find->index++;
3438 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3439 }else {
3440 res = ERROR_NO_MORE_FILES;
3443 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3445 INTERNET_ASYNC_RESULT iar;
3447 iar.dwResult = (res == ERROR_SUCCESS);
3448 iar.dwError = res;
3450 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3451 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3452 sizeof(INTERNET_ASYNC_RESULT));
3455 return res;
3458 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3460 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3462 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3465 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3467 switch(option) {
3468 case INTERNET_OPTION_HANDLE_TYPE:
3469 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3471 if (*size < sizeof(ULONG))
3472 return ERROR_INSUFFICIENT_BUFFER;
3474 *size = sizeof(DWORD);
3475 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3476 return ERROR_SUCCESS;
3479 return INET_QueryOption(hdr, option, buffer, size, unicode);
3482 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3484 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3486 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3488 WORKREQUEST workRequest;
3489 struct WORKREQ_FTPFINDNEXTW *req;
3491 workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3492 workRequest.hdr = WININET_AddRef( &find->hdr );
3493 req = &workRequest.u.FtpFindNextW;
3494 req->lpFindFileData = data;
3496 INTERNET_AsyncCall(&workRequest);
3498 return ERROR_SUCCESS;
3501 return FTPFINDNEXT_FindNextFileProc(find, data);
3504 static const object_vtbl_t FTPFINDNEXTVtbl = {
3505 FTPFINDNEXT_Destroy,
3506 NULL,
3507 FTPFINDNEXT_QueryOption,
3508 NULL,
3509 NULL,
3510 NULL,
3511 NULL,
3512 NULL,
3513 NULL,
3514 FTPFINDNEXT_FindNextFileW
3517 /***********************************************************************
3518 * FTP_ReceiveFileList (internal)
3520 * Read file list from server
3522 * RETURNS
3523 * Handle to file list on success
3524 * NULL on failure
3527 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3528 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3530 DWORD dwSize = 0;
3531 LPFILEPROPERTIESW lpafp = NULL;
3532 LPWININETFTPFINDNEXTW lpwfn = NULL;
3533 HINTERNET handle = 0;
3535 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3537 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3539 if(lpFindFileData)
3540 FTP_ConvertFileProp(lpafp, lpFindFileData);
3542 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3543 if (lpwfn)
3545 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3546 lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3547 lpwfn->hdr.dwContext = dwContext;
3548 lpwfn->hdr.refs = 1;
3549 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3550 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3551 lpwfn->size = dwSize;
3552 lpwfn->lpafp = lpafp;
3554 WININET_AddRef( &lpwfs->hdr );
3555 lpwfn->lpFtpSession = lpwfs;
3556 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3558 handle = WININET_AllocHandle( &lpwfn->hdr );
3562 if( lpwfn )
3563 WININET_Release( &lpwfn->hdr );
3565 TRACE("Matched %d files\n", dwSize);
3566 return handle;
3570 /***********************************************************************
3571 * FTP_ConvertFileProp (internal)
3573 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3575 * RETURNS
3576 * TRUE on success
3577 * FALSE on failure
3580 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3582 BOOL bSuccess = FALSE;
3584 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3586 if (lpafp)
3588 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3589 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3590 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3592 /* Not all fields are filled in */
3593 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3594 lpFindFileData->nFileSizeLow = lpafp->nSize;
3596 if (lpafp->bIsDirectory)
3597 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3599 if (lpafp->lpszName)
3600 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3602 bSuccess = TRUE;
3605 return bSuccess;
3608 /***********************************************************************
3609 * FTP_ParseNextFile (internal)
3611 * Parse the next line in file listing
3613 * RETURNS
3614 * TRUE on success
3615 * FALSE on failure
3617 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3619 static const char szSpace[] = " \t";
3620 DWORD nBufLen;
3621 char *pszLine;
3622 char *pszToken;
3623 char *pszTmp;
3624 BOOL found = FALSE;
3625 int i;
3627 lpfp->lpszName = NULL;
3628 do {
3629 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3630 return FALSE;
3632 pszToken = strtok(pszLine, szSpace);
3633 /* ls format
3634 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3636 * For instance:
3637 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3639 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3640 if(!FTP_ParsePermission(pszToken, lpfp))
3641 lpfp->bIsDirectory = FALSE;
3642 for(i=0; i<=3; i++) {
3643 if(!(pszToken = strtok(NULL, szSpace)))
3644 break;
3646 if(!pszToken) continue;
3647 if(lpfp->bIsDirectory) {
3648 TRACE("Is directory\n");
3649 lpfp->nSize = 0;
3651 else {
3652 TRACE("Size: %s\n", pszToken);
3653 lpfp->nSize = atol(pszToken);
3656 lpfp->tmLastModified.wSecond = 0;
3657 lpfp->tmLastModified.wMinute = 0;
3658 lpfp->tmLastModified.wHour = 0;
3659 lpfp->tmLastModified.wDay = 0;
3660 lpfp->tmLastModified.wMonth = 0;
3661 lpfp->tmLastModified.wYear = 0;
3663 /* Determine month */
3664 pszToken = strtok(NULL, szSpace);
3665 if(!pszToken) continue;
3666 if(strlen(pszToken) >= 3) {
3667 pszToken[3] = 0;
3668 if((pszTmp = StrStrIA(szMonths, pszToken)))
3669 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3671 /* Determine day */
3672 pszToken = strtok(NULL, szSpace);
3673 if(!pszToken) continue;
3674 lpfp->tmLastModified.wDay = atoi(pszToken);
3675 /* Determine time or year */
3676 pszToken = strtok(NULL, szSpace);
3677 if(!pszToken) continue;
3678 if((pszTmp = strchr(pszToken, ':'))) {
3679 SYSTEMTIME curr_time;
3680 *pszTmp = 0;
3681 pszTmp++;
3682 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3683 lpfp->tmLastModified.wHour = atoi(pszToken);
3684 GetLocalTime( &curr_time );
3685 lpfp->tmLastModified.wYear = curr_time.wYear;
3687 else {
3688 lpfp->tmLastModified.wYear = atoi(pszToken);
3689 lpfp->tmLastModified.wHour = 12;
3691 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3692 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3693 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3695 pszToken = strtok(NULL, szSpace);
3696 if(!pszToken) continue;
3697 lpfp->lpszName = heap_strdupAtoW(pszToken);
3698 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3700 /* NT way of parsing ... :
3702 07-13-03 08:55PM <DIR> sakpatch
3703 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3705 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3706 int mon, mday, year, hour, min;
3707 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3709 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3710 lpfp->tmLastModified.wDay = mday;
3711 lpfp->tmLastModified.wMonth = mon;
3712 lpfp->tmLastModified.wYear = year;
3714 /* Hacky and bad Y2K protection :-) */
3715 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3717 pszToken = strtok(NULL, szSpace);
3718 if(!pszToken) continue;
3719 sscanf(pszToken, "%d:%d", &hour, &min);
3720 lpfp->tmLastModified.wHour = hour;
3721 lpfp->tmLastModified.wMinute = min;
3722 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3723 lpfp->tmLastModified.wHour += 12;
3725 lpfp->tmLastModified.wSecond = 0;
3727 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3728 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3729 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3731 pszToken = strtok(NULL, szSpace);
3732 if(!pszToken) continue;
3733 if(!strcasecmp(pszToken, "<DIR>")) {
3734 lpfp->bIsDirectory = TRUE;
3735 lpfp->nSize = 0;
3736 TRACE("Is directory\n");
3738 else {
3739 lpfp->bIsDirectory = FALSE;
3740 lpfp->nSize = atol(pszToken);
3741 TRACE("Size: %d\n", lpfp->nSize);
3744 pszToken = strtok(NULL, szSpace);
3745 if(!pszToken) continue;
3746 lpfp->lpszName = heap_strdupAtoW(pszToken);
3747 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3749 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3750 else if(pszToken[0] == '+') {
3751 FIXME("EPLF Format not implemented\n");
3754 if(lpfp->lpszName) {
3755 if((lpszSearchFile == NULL) ||
3756 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3757 found = TRUE;
3758 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3760 else {
3761 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3762 lpfp->lpszName = NULL;
3765 } while(!found);
3766 return TRUE;
3769 /***********************************************************************
3770 * FTP_ParseDirectory (internal)
3772 * Parse string of directory information
3774 * RETURNS
3775 * TRUE on success
3776 * FALSE on failure
3778 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3779 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3781 BOOL bSuccess = TRUE;
3782 INT sizeFilePropArray = 500;/*20; */
3783 INT indexFilePropArray = -1;
3785 TRACE("\n");
3787 /* Allocate initial file properties array */
3788 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3789 if (!*lpafp)
3790 return FALSE;
3792 do {
3793 if (indexFilePropArray+1 >= sizeFilePropArray)
3795 LPFILEPROPERTIESW tmpafp;
3797 sizeFilePropArray *= 2;
3798 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3799 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3800 if (NULL == tmpafp)
3802 bSuccess = FALSE;
3803 break;
3806 *lpafp = tmpafp;
3808 indexFilePropArray++;
3809 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3811 if (bSuccess && indexFilePropArray)
3813 if (indexFilePropArray < sizeFilePropArray - 1)
3815 LPFILEPROPERTIESW tmpafp;
3817 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3818 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3819 if (NULL != tmpafp)
3820 *lpafp = tmpafp;
3822 *dwfp = indexFilePropArray;
3824 else
3826 HeapFree(GetProcessHeap(), 0, *lpafp);
3827 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3828 bSuccess = FALSE;
3831 return bSuccess;
3835 /***********************************************************************
3836 * FTP_ParsePermission (internal)
3838 * Parse permission string of directory information
3840 * RETURNS
3841 * TRUE on success
3842 * FALSE on failure
3845 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3847 BOOL bSuccess = TRUE;
3848 unsigned short nPermission = 0;
3849 INT nPos = 1;
3850 INT nLast = 9;
3852 TRACE("\n");
3853 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3855 bSuccess = FALSE;
3856 return bSuccess;
3859 lpfp->bIsDirectory = (*lpszPermission == 'd');
3862 switch (nPos)
3864 case 1:
3865 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3866 break;
3867 case 2:
3868 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3869 break;
3870 case 3:
3871 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3872 break;
3873 case 4:
3874 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3875 break;
3876 case 5:
3877 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3878 break;
3879 case 6:
3880 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3881 break;
3882 case 7:
3883 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3884 break;
3885 case 8:
3886 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3887 break;
3888 case 9:
3889 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3890 break;
3892 nPos++;
3893 }while (nPos <= nLast);
3895 lpfp->permissions = nPermission;
3896 return bSuccess;
3900 /***********************************************************************
3901 * FTP_SetResponseError (internal)
3903 * Set the appropriate error code for a given response from the server
3905 * RETURNS
3908 static DWORD FTP_SetResponseError(DWORD dwResponse)
3910 DWORD dwCode = 0;
3912 switch(dwResponse)
3914 case 425: /* Cannot open data connection. */
3915 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3916 break;
3918 case 426: /* Connection closed, transer aborted. */
3919 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3920 break;
3922 case 530: /* Not logged in. Login incorrect. */
3923 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3924 break;
3926 case 421: /* Service not available - Server may be shutting down. */
3927 case 450: /* File action not taken. File may be busy. */
3928 case 451: /* Action aborted. Server error. */
3929 case 452: /* Action not taken. Insufficient storage space on server. */
3930 case 500: /* Syntax error. Command unrecognized. */
3931 case 501: /* Syntax error. Error in parameters or arguments. */
3932 case 502: /* Command not implemented. */
3933 case 503: /* Bad sequence of commands. */
3934 case 504: /* Command not implemented for that parameter. */
3935 case 532: /* Need account for storing files */
3936 case 550: /* File action not taken. File not found or no access. */
3937 case 551: /* Requested action aborted. Page type unknown */
3938 case 552: /* Action aborted. Exceeded storage allocation */
3939 case 553: /* Action not taken. File name not allowed. */
3941 default:
3942 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3943 break;
3946 INTERNET_SetLastError(dwCode);
3947 return dwCode;