ntdll: Expand previous patch to use pthread_mutex
[wine/multimedia.git] / dlls / wininet / ftp.c
blob0c7ca12c90cce7dad98e54f302af93bc96c64ce8
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 heap_free(lpwzLocalFile);
250 heap_free(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 heap_free(req->lpszLocalFile);
265 heap_free(req->lpszNewRemoteFile);
268 /***********************************************************************
269 * FtpPutFileW (WININET.@)
271 * Uploads a file to the FTP server
273 * RETURNS
274 * TRUE on success
275 * FALSE on failure
278 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
279 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
281 ftp_session_t *lpwfs;
282 appinfo_t *hIC = NULL;
283 BOOL r = FALSE;
285 if (!lpszLocalFile || !lpszNewRemoteFile)
287 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
288 return FALSE;
291 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
292 if (!lpwfs)
294 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
295 return FALSE;
298 if (WH_HFTPSESSION != lpwfs->hdr.htype)
300 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
301 goto lend;
304 if (lpwfs->download_in_progress != NULL)
306 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
307 goto lend;
310 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
312 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
313 goto lend;
316 hIC = lpwfs->lpAppInfo;
317 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
319 WORKREQUEST workRequest;
320 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
322 workRequest.asyncproc = AsyncFtpPutFileProc;
323 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
324 req->lpszLocalFile = heap_strdupW(lpszLocalFile);
325 req->lpszNewRemoteFile = heap_strdupW(lpszNewRemoteFile);
326 req->dwFlags = dwFlags;
327 req->dwContext = dwContext;
329 r = res_to_le(INTERNET_AsyncCall(&workRequest));
331 else
333 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
334 lpszNewRemoteFile, dwFlags, dwContext);
337 lend:
338 WININET_Release( &lpwfs->hdr );
340 return r;
343 /***********************************************************************
344 * FTP_FtpPutFileW (Internal)
346 * Uploads a file to the FTP server
348 * RETURNS
349 * TRUE on success
350 * FALSE on failure
353 static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
354 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
356 HANDLE hFile;
357 BOOL bSuccess = FALSE;
358 appinfo_t *hIC = NULL;
359 INT nResCode;
361 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
363 /* Clear any error information */
364 INTERNET_SetLastError(0);
366 /* Open file to be uploaded */
367 if (INVALID_HANDLE_VALUE ==
368 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
369 /* Let CreateFile set the appropriate error */
370 return FALSE;
372 hIC = lpwfs->lpAppInfo;
374 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
376 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
378 INT nDataSocket;
380 /* Get data socket to server */
381 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
383 FTP_SendData(lpwfs, nDataSocket, hFile);
384 closesocket(nDataSocket);
385 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
386 if (nResCode)
388 if (nResCode == 226)
389 bSuccess = TRUE;
390 else
391 FTP_SetResponseError(nResCode);
396 if (lpwfs->lstnSocket != -1)
398 closesocket(lpwfs->lstnSocket);
399 lpwfs->lstnSocket = -1;
402 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
404 INTERNET_ASYNC_RESULT iar;
406 iar.dwResult = (DWORD)bSuccess;
407 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
408 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
409 &iar, sizeof(INTERNET_ASYNC_RESULT));
412 CloseHandle(hFile);
414 return bSuccess;
418 /***********************************************************************
419 * FtpSetCurrentDirectoryA (WININET.@)
421 * Change the working directory on the FTP server
423 * RETURNS
424 * TRUE on success
425 * FALSE on failure
428 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
430 LPWSTR lpwzDirectory;
431 BOOL ret;
433 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
434 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
435 heap_free(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 heap_free(req->lpszDirectory);
451 /***********************************************************************
452 * FtpSetCurrentDirectoryW (WININET.@)
454 * Change the working directory on the FTP server
456 * RETURNS
457 * TRUE on success
458 * FALSE on failure
461 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
463 ftp_session_t *lpwfs = NULL;
464 appinfo_t *hIC = NULL;
465 BOOL r = FALSE;
467 if (!lpszDirectory)
469 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
470 goto lend;
473 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
474 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
476 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
477 goto lend;
480 if (lpwfs->download_in_progress != NULL)
482 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
483 goto lend;
486 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
488 hIC = lpwfs->lpAppInfo;
489 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
491 WORKREQUEST workRequest;
492 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
494 workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
495 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
496 req = &workRequest.u.FtpSetCurrentDirectoryW;
497 req->lpszDirectory = heap_strdupW(lpszDirectory);
499 r = res_to_le(INTERNET_AsyncCall(&workRequest));
501 else
503 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
506 lend:
507 if( lpwfs )
508 WININET_Release( &lpwfs->hdr );
510 return r;
514 /***********************************************************************
515 * FTP_FtpSetCurrentDirectoryW (Internal)
517 * Change the working directory on the FTP server
519 * RETURNS
520 * TRUE on success
521 * FALSE on failure
524 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
526 INT nResCode;
527 appinfo_t *hIC = NULL;
528 DWORD bSuccess = FALSE;
530 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
532 /* Clear any error information */
533 INTERNET_SetLastError(0);
535 hIC = lpwfs->lpAppInfo;
536 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
537 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
538 goto lend;
540 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
542 if (nResCode)
544 if (nResCode == 250)
545 bSuccess = TRUE;
546 else
547 FTP_SetResponseError(nResCode);
550 lend:
551 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
553 INTERNET_ASYNC_RESULT iar;
555 iar.dwResult = bSuccess;
556 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
557 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
558 &iar, sizeof(INTERNET_ASYNC_RESULT));
560 return bSuccess;
564 /***********************************************************************
565 * FtpCreateDirectoryA (WININET.@)
567 * Create new directory on the FTP server
569 * RETURNS
570 * TRUE on success
571 * FALSE on failure
574 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
576 LPWSTR lpwzDirectory;
577 BOOL ret;
579 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
580 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
581 heap_free(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 heap_free(req->lpszDirectory);
597 /***********************************************************************
598 * FtpCreateDirectoryW (WININET.@)
600 * Create new directory on the FTP server
602 * RETURNS
603 * TRUE on success
604 * FALSE on failure
607 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
609 ftp_session_t *lpwfs;
610 appinfo_t *hIC = NULL;
611 BOOL r = FALSE;
613 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
614 if (!lpwfs)
616 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
617 return FALSE;
620 if (WH_HFTPSESSION != lpwfs->hdr.htype)
622 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
623 goto lend;
626 if (lpwfs->download_in_progress != NULL)
628 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
629 goto lend;
632 if (!lpszDirectory)
634 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
635 goto lend;
638 hIC = lpwfs->lpAppInfo;
639 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
641 WORKREQUEST workRequest;
642 struct WORKREQ_FTPCREATEDIRECTORYW *req;
644 workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
645 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
646 req = &workRequest.u.FtpCreateDirectoryW;
647 req->lpszDirectory = heap_strdupW(lpszDirectory);
649 r = res_to_le(INTERNET_AsyncCall(&workRequest));
651 else
653 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
655 lend:
656 WININET_Release( &lpwfs->hdr );
658 return r;
662 /***********************************************************************
663 * FTP_FtpCreateDirectoryW (Internal)
665 * Create new directory on the FTP server
667 * RETURNS
668 * TRUE on success
669 * FALSE on failure
672 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
674 INT nResCode;
675 BOOL bSuccess = FALSE;
676 appinfo_t *hIC = NULL;
678 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
680 /* Clear any error information */
681 INTERNET_SetLastError(0);
683 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
684 goto lend;
686 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
687 if (nResCode)
689 if (nResCode == 257)
690 bSuccess = TRUE;
691 else
692 FTP_SetResponseError(nResCode);
695 lend:
696 hIC = lpwfs->lpAppInfo;
697 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
699 INTERNET_ASYNC_RESULT iar;
701 iar.dwResult = (DWORD)bSuccess;
702 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
703 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
704 &iar, sizeof(INTERNET_ASYNC_RESULT));
707 return bSuccess;
710 /***********************************************************************
711 * FtpFindFirstFileA (WININET.@)
713 * Search the specified directory
715 * RETURNS
716 * HINTERNET on success
717 * NULL on failure
720 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
721 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
723 LPWSTR lpwzSearchFile;
724 WIN32_FIND_DATAW wfd;
725 LPWIN32_FIND_DATAW lpFindFileDataW;
726 HINTERNET ret;
728 lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
729 lpFindFileDataW = lpFindFileData?&wfd:NULL;
730 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
731 heap_free(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 heap_free(req->lpszSearchFile);
752 /***********************************************************************
753 * FtpFindFirstFileW (WININET.@)
755 * Search the specified directory
757 * RETURNS
758 * HINTERNET on success
759 * NULL on failure
762 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
763 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
765 ftp_session_t *lpwfs;
766 appinfo_t *hIC = NULL;
767 HINTERNET r = NULL;
769 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
770 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
772 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
773 goto lend;
776 if (lpwfs->download_in_progress != NULL)
778 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
779 goto lend;
782 hIC = lpwfs->lpAppInfo;
783 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
785 WORKREQUEST workRequest;
786 struct WORKREQ_FTPFINDFIRSTFILEW *req;
788 workRequest.asyncproc = AsyncFtpFindFirstFileProc;
789 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
790 req = &workRequest.u.FtpFindFirstFileW;
791 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : heap_strdupW(lpszSearchFile);
792 req->lpFindFileData = lpFindFileData;
793 req->dwFlags = dwFlags;
794 req->dwContext= dwContext;
796 INTERNET_AsyncCall(&workRequest);
797 r = NULL;
799 else
801 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
802 dwFlags, dwContext);
804 lend:
805 if( lpwfs )
806 WININET_Release( &lpwfs->hdr );
808 return r;
812 /***********************************************************************
813 * FTP_FtpFindFirstFileW (Internal)
815 * Search the specified directory
817 * RETURNS
818 * HINTERNET on success
819 * NULL on failure
822 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
823 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
825 INT nResCode;
826 appinfo_t *hIC = NULL;
827 HINTERNET hFindNext = NULL;
829 TRACE("\n");
831 /* Clear any error information */
832 INTERNET_SetLastError(0);
834 if (!FTP_InitListenSocket(lpwfs))
835 goto lend;
837 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
838 goto lend;
840 if (!FTP_SendPortOrPasv(lpwfs))
841 goto lend;
843 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
844 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
845 goto lend;
847 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
848 if (nResCode)
850 if (nResCode == 125 || nResCode == 150)
852 INT nDataSocket;
854 /* Get data socket to server */
855 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
857 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
858 closesocket(nDataSocket);
859 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
860 if (nResCode != 226 && nResCode != 250)
861 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
864 else
865 FTP_SetResponseError(nResCode);
868 lend:
869 if (lpwfs->lstnSocket != -1)
871 closesocket(lpwfs->lstnSocket);
872 lpwfs->lstnSocket = -1;
875 hIC = lpwfs->lpAppInfo;
876 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
878 INTERNET_ASYNC_RESULT iar;
880 if (hFindNext)
882 iar.dwResult = (DWORD_PTR)hFindNext;
883 iar.dwError = ERROR_SUCCESS;
884 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
885 &iar, sizeof(INTERNET_ASYNC_RESULT));
888 iar.dwResult = (DWORD_PTR)hFindNext;
889 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
890 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
891 &iar, sizeof(INTERNET_ASYNC_RESULT));
894 return hFindNext;
898 /***********************************************************************
899 * FtpGetCurrentDirectoryA (WININET.@)
901 * Retrieves the current directory
903 * RETURNS
904 * TRUE on success
905 * FALSE on failure
908 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
909 LPDWORD lpdwCurrentDirectory)
911 WCHAR *dir = NULL;
912 DWORD len;
913 BOOL ret;
915 if(lpdwCurrentDirectory) {
916 len = *lpdwCurrentDirectory;
917 if(lpszCurrentDirectory)
919 dir = heap_alloc(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 heap_free(dir);
934 return ret;
938 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
940 struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
941 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
943 TRACE("%p\n", lpwfs);
945 FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
948 /***********************************************************************
949 * FtpGetCurrentDirectoryW (WININET.@)
951 * Retrieves the current directory
953 * RETURNS
954 * TRUE on success
955 * FALSE on failure
958 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
959 LPDWORD lpdwCurrentDirectory)
961 ftp_session_t *lpwfs;
962 appinfo_t *hIC = NULL;
963 BOOL r = FALSE;
965 TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
967 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
968 if (NULL == lpwfs)
970 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
971 goto lend;
974 if (WH_HFTPSESSION != lpwfs->hdr.htype)
976 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
977 goto lend;
980 if (!lpdwCurrentDirectory)
982 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
983 goto lend;
986 if (lpszCurrentDirectory == NULL)
988 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
989 goto lend;
992 if (lpwfs->download_in_progress != NULL)
994 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
995 goto lend;
998 hIC = lpwfs->lpAppInfo;
999 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1001 WORKREQUEST workRequest;
1002 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
1004 workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
1005 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1006 req = &workRequest.u.FtpGetCurrentDirectoryW;
1007 req->lpszDirectory = lpszCurrentDirectory;
1008 req->lpdwDirectory = lpdwCurrentDirectory;
1010 r = res_to_le(INTERNET_AsyncCall(&workRequest));
1012 else
1014 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
1015 lpdwCurrentDirectory);
1018 lend:
1019 if( lpwfs )
1020 WININET_Release( &lpwfs->hdr );
1022 return r;
1026 /***********************************************************************
1027 * FTP_FtpGetCurrentDirectoryW (Internal)
1029 * Retrieves the current directory
1031 * RETURNS
1032 * TRUE on success
1033 * FALSE on failure
1036 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
1037 LPDWORD lpdwCurrentDirectory)
1039 INT nResCode;
1040 appinfo_t *hIC = NULL;
1041 DWORD bSuccess = FALSE;
1043 /* Clear any error information */
1044 INTERNET_SetLastError(0);
1046 hIC = lpwfs->lpAppInfo;
1047 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1048 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1049 goto lend;
1051 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1052 if (nResCode)
1054 if (nResCode == 257) /* Extract directory name */
1056 DWORD firstpos, lastpos, len;
1057 LPWSTR lpszResponseBuffer = heap_strdupAtoW(INTERNET_GetResponseBuffer());
1059 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1061 if ('"' == lpszResponseBuffer[lastpos])
1063 if (!firstpos)
1064 firstpos = lastpos;
1065 else
1066 break;
1069 len = lastpos - firstpos;
1070 if (*lpdwCurrentDirectory >= len)
1072 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1073 lpszCurrentDirectory[len - 1] = 0;
1074 *lpdwCurrentDirectory = len;
1075 bSuccess = TRUE;
1077 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1079 heap_free(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 heap_free(lpwh->cache_file);
1120 if (!lpwh->session_deleted)
1121 lpwfs->download_in_progress = NULL;
1123 if (lpwh->nDataSocket != -1)
1124 closesocket(lpwh->nDataSocket);
1126 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1127 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1129 WININET_Release(&lpwh->lpFtpSession->hdr);
1132 static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1134 switch(option) {
1135 case INTERNET_OPTION_HANDLE_TYPE:
1136 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1138 if (*size < sizeof(ULONG))
1139 return ERROR_INSUFFICIENT_BUFFER;
1141 *size = sizeof(DWORD);
1142 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1143 return ERROR_SUCCESS;
1144 case INTERNET_OPTION_DATAFILE_NAME:
1146 DWORD required;
1147 ftp_file_t *file = (ftp_file_t *)hdr;
1149 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1151 if (!file->cache_file)
1153 *size = 0;
1154 return ERROR_INTERNET_ITEM_NOT_FOUND;
1156 if (unicode)
1158 required = (lstrlenW(file->cache_file) + 1) * sizeof(WCHAR);
1159 if (*size < required)
1160 return ERROR_INSUFFICIENT_BUFFER;
1162 *size = required;
1163 memcpy(buffer, file->cache_file, *size);
1164 return ERROR_SUCCESS;
1166 else
1168 required = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, NULL, 0, NULL, NULL);
1169 if (required > *size)
1170 return ERROR_INSUFFICIENT_BUFFER;
1172 *size = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, buffer, *size, NULL, NULL);
1173 return ERROR_SUCCESS;
1177 return INET_QueryOption(hdr, option, buffer, size, unicode);
1180 static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
1182 ftp_file_t *file = (ftp_file_t*)hdr;
1183 int res;
1184 DWORD error;
1186 if (file->nDataSocket == -1)
1187 return ERROR_INTERNET_DISCONNECTED;
1189 /* FIXME: FTP should use NETCON_ stuff */
1190 res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1191 *read = res>0 ? res : 0;
1193 error = res >= 0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME */
1194 if (error == ERROR_SUCCESS && file->cache_file)
1196 DWORD bytes_written;
1198 if (!WriteFile(file->cache_file_handle, buffer, *read, &bytes_written, NULL))
1199 WARN("WriteFile failed: %u\n", GetLastError());
1201 return error;
1204 static DWORD FTPFILE_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
1205 DWORD flags, DWORD_PTR context)
1207 return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1210 static DWORD FTPFILE_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
1211 DWORD flags, DWORD_PTR context)
1213 return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1216 static DWORD FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1218 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1219 int res;
1221 res = send(lpwh->nDataSocket, buffer, size, 0);
1223 *written = res>0 ? res : 0;
1224 return res >= 0 ? ERROR_SUCCESS : sock_get_error(errno);
1227 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
1229 INTERNET_ASYNC_RESULT iar;
1230 BYTE buffer[4096];
1231 int available;
1233 TRACE("%p\n", file);
1235 available = recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1237 if(available != -1) {
1238 iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1239 iar.dwError = first_notif ? 0 : available;
1240 }else {
1241 iar.dwResult = 0;
1242 iar.dwError = INTERNET_GetLastError();
1245 INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1246 sizeof(INTERNET_ASYNC_RESULT));
1249 static void FTPFILE_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
1251 ftp_file_t *file = (ftp_file_t*)workRequest->hdr;
1253 FTP_ReceiveRequestData(file, FALSE);
1256 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1258 ftp_file_t *file = (ftp_file_t*) hdr;
1259 int retval, unread = 0;
1261 TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
1263 #ifdef FIONREAD
1264 retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
1265 if (!retval)
1266 TRACE("%d bytes of queued, but unread data\n", unread);
1267 #else
1268 FIXME("FIONREAD not available\n");
1269 #endif
1271 *available = unread;
1273 if(!unread) {
1274 BYTE byte;
1276 *available = 0;
1278 retval = recv(file->nDataSocket, &byte, 1, MSG_PEEK);
1279 if(retval > 0) {
1280 WORKREQUEST workRequest;
1282 *available = 0;
1283 workRequest.asyncproc = FTPFILE_AsyncQueryDataAvailableProc;
1284 workRequest.hdr = WININET_AddRef( &file->hdr );
1286 INTERNET_AsyncCall(&workRequest);
1288 return ERROR_IO_PENDING;
1292 return ERROR_SUCCESS;
1296 static const object_vtbl_t FTPFILEVtbl = {
1297 FTPFILE_Destroy,
1298 NULL,
1299 FTPFILE_QueryOption,
1300 INET_SetOption,
1301 FTPFILE_ReadFile,
1302 FTPFILE_ReadFileExA,
1303 FTPFILE_ReadFileExW,
1304 FTPFILE_WriteFile,
1305 FTPFILE_QueryDataAvailable,
1306 NULL
1309 /***********************************************************************
1310 * FTP_FtpOpenFileW (Internal)
1312 * Open a remote file for writing or reading
1314 * RETURNS
1315 * HINTERNET handle on success
1316 * NULL on failure
1319 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1320 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1321 DWORD_PTR dwContext)
1323 INT nDataSocket;
1324 BOOL bSuccess = FALSE;
1325 ftp_file_t *lpwh = NULL;
1326 appinfo_t *hIC = NULL;
1328 TRACE("\n");
1330 /* Clear any error information */
1331 INTERNET_SetLastError(0);
1333 if (GENERIC_READ == fdwAccess)
1335 /* Set up socket to retrieve data */
1336 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1338 else if (GENERIC_WRITE == fdwAccess)
1340 /* Set up socket to send data */
1341 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1344 /* Get data socket to server */
1345 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1347 lpwh = alloc_object(&lpwfs->hdr, &FTPFILEVtbl, sizeof(ftp_file_t));
1348 lpwh->hdr.htype = WH_HFILE;
1349 lpwh->hdr.dwFlags = dwFlags;
1350 lpwh->hdr.dwContext = dwContext;
1351 lpwh->nDataSocket = nDataSocket;
1352 lpwh->cache_file = NULL;
1353 lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1354 lpwh->session_deleted = FALSE;
1356 WININET_AddRef( &lpwfs->hdr );
1357 lpwh->lpFtpSession = lpwfs;
1358 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1360 /* Indicate that a download is currently in progress */
1361 lpwfs->download_in_progress = lpwh;
1364 if (lpwfs->lstnSocket != -1)
1366 closesocket(lpwfs->lstnSocket);
1367 lpwfs->lstnSocket = -1;
1370 if (bSuccess && fdwAccess == GENERIC_READ)
1372 WCHAR filename[MAX_PATH + 1];
1373 URL_COMPONENTSW uc;
1374 DWORD len;
1376 memset(&uc, 0, sizeof(uc));
1377 uc.dwStructSize = sizeof(uc);
1378 uc.nScheme = INTERNET_SCHEME_FTP;
1379 uc.lpszHostName = lpwfs->servername;
1380 uc.nPort = lpwfs->serverport;
1381 uc.lpszUserName = lpwfs->lpszUserName;
1382 uc.lpszUrlPath = heap_strdupW(lpszFileName);
1384 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1386 WCHAR *url = heap_alloc(len * sizeof(WCHAR));
1388 if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1390 lpwh->cache_file = heap_strdupW(filename);
1391 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1392 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1393 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1395 WARN("Could not create cache file: %u\n", GetLastError());
1396 heap_free(lpwh->cache_file);
1397 lpwh->cache_file = NULL;
1400 heap_free(url);
1402 heap_free(uc.lpszUrlPath);
1405 hIC = lpwfs->lpAppInfo;
1406 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1408 INTERNET_ASYNC_RESULT iar;
1410 if (lpwh)
1412 iar.dwResult = (DWORD_PTR)lpwh->hdr.hInternet;
1413 iar.dwError = ERROR_SUCCESS;
1414 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1415 &iar, sizeof(INTERNET_ASYNC_RESULT));
1418 if(bSuccess) {
1419 FTP_ReceiveRequestData(lpwh, TRUE);
1420 }else {
1421 iar.dwResult = 0;
1422 iar.dwError = INTERNET_GetLastError();
1423 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1424 &iar, sizeof(INTERNET_ASYNC_RESULT));
1428 if(!bSuccess)
1429 return FALSE;
1431 return lpwh->hdr.hInternet;
1435 /***********************************************************************
1436 * FtpOpenFileA (WININET.@)
1438 * Open a remote file for writing or reading
1440 * RETURNS
1441 * HINTERNET handle on success
1442 * NULL on failure
1445 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1446 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1447 DWORD_PTR dwContext)
1449 LPWSTR lpwzFileName;
1450 HINTERNET ret;
1452 lpwzFileName = heap_strdupAtoW(lpszFileName);
1453 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1454 heap_free(lpwzFileName);
1455 return ret;
1459 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1461 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1462 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1464 TRACE("%p\n", lpwfs);
1466 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1467 req->dwAccess, req->dwFlags, req->dwContext);
1468 heap_free(req->lpszFilename);
1471 /***********************************************************************
1472 * FtpOpenFileW (WININET.@)
1474 * Open a remote file for writing or reading
1476 * RETURNS
1477 * HINTERNET handle on success
1478 * NULL on failure
1481 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1482 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1483 DWORD_PTR dwContext)
1485 ftp_session_t *lpwfs;
1486 appinfo_t *hIC = NULL;
1487 HINTERNET r = NULL;
1489 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1490 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1492 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1493 if (!lpwfs)
1495 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1496 return FALSE;
1499 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1501 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1502 goto lend;
1505 if ((!lpszFileName) ||
1506 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1507 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1509 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1510 goto lend;
1513 if (lpwfs->download_in_progress != NULL)
1515 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1516 goto lend;
1519 hIC = lpwfs->lpAppInfo;
1520 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1522 WORKREQUEST workRequest;
1523 struct WORKREQ_FTPOPENFILEW *req;
1525 workRequest.asyncproc = AsyncFtpOpenFileProc;
1526 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1527 req = &workRequest.u.FtpOpenFileW;
1528 req->lpszFilename = heap_strdupW(lpszFileName);
1529 req->dwAccess = fdwAccess;
1530 req->dwFlags = dwFlags;
1531 req->dwContext = dwContext;
1533 INTERNET_AsyncCall(&workRequest);
1534 r = NULL;
1536 else
1538 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1541 lend:
1542 WININET_Release( &lpwfs->hdr );
1544 return r;
1548 /***********************************************************************
1549 * FtpGetFileA (WININET.@)
1551 * Retrieve file from the FTP server
1553 * RETURNS
1554 * TRUE on success
1555 * FALSE on failure
1558 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1559 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1560 DWORD_PTR dwContext)
1562 LPWSTR lpwzRemoteFile;
1563 LPWSTR lpwzNewFile;
1564 BOOL ret;
1566 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1567 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1568 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1569 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1570 heap_free(lpwzRemoteFile);
1571 heap_free(lpwzNewFile);
1572 return ret;
1576 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1578 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1579 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1581 TRACE("%p\n", lpwfs);
1583 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1584 req->lpszNewFile, req->fFailIfExists,
1585 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1586 heap_free(req->lpszRemoteFile);
1587 heap_free(req->lpszNewFile);
1591 /***********************************************************************
1592 * FtpGetFileW (WININET.@)
1594 * Retrieve file from the FTP server
1596 * RETURNS
1597 * TRUE on success
1598 * FALSE on failure
1601 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1602 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1603 DWORD_PTR dwContext)
1605 ftp_session_t *lpwfs;
1606 appinfo_t *hIC = NULL;
1607 BOOL r = FALSE;
1609 if (!lpszRemoteFile || !lpszNewFile)
1611 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1612 return FALSE;
1615 lpwfs = (ftp_session_t*) get_handle_object( hInternet );
1616 if (!lpwfs)
1618 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1619 return FALSE;
1622 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1624 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1625 goto lend;
1628 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1630 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1631 goto lend;
1634 if (lpwfs->download_in_progress != NULL)
1636 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1637 goto lend;
1640 hIC = lpwfs->lpAppInfo;
1641 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1643 WORKREQUEST workRequest;
1644 struct WORKREQ_FTPGETFILEW *req;
1646 workRequest.asyncproc = AsyncFtpGetFileProc;
1647 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1648 req = &workRequest.u.FtpGetFileW;
1649 req->lpszRemoteFile = heap_strdupW(lpszRemoteFile);
1650 req->lpszNewFile = heap_strdupW(lpszNewFile);
1651 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1652 req->fFailIfExists = fFailIfExists;
1653 req->dwFlags = dwInternetFlags;
1654 req->dwContext = dwContext;
1656 r = res_to_le(INTERNET_AsyncCall(&workRequest));
1658 else
1660 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1661 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1664 lend:
1665 WININET_Release( &lpwfs->hdr );
1667 return r;
1671 /***********************************************************************
1672 * FTP_FtpGetFileW (Internal)
1674 * Retrieve file from the FTP server
1676 * RETURNS
1677 * TRUE on success
1678 * FALSE on failure
1681 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1682 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1683 DWORD_PTR dwContext)
1685 BOOL bSuccess = FALSE;
1686 HANDLE hFile;
1687 appinfo_t *hIC = NULL;
1689 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1691 /* Clear any error information */
1692 INTERNET_SetLastError(0);
1694 /* Ensure we can write to lpszNewfile by opening it */
1695 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1696 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1697 if (INVALID_HANDLE_VALUE == hFile)
1698 return FALSE;
1700 /* Set up socket to retrieve data */
1701 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1703 INT nDataSocket;
1705 /* Get data socket to server */
1706 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1708 INT nResCode;
1710 /* Receive data */
1711 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1712 closesocket(nDataSocket);
1714 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1715 if (nResCode)
1717 if (nResCode == 226)
1718 bSuccess = TRUE;
1719 else
1720 FTP_SetResponseError(nResCode);
1725 if (lpwfs->lstnSocket != -1)
1727 closesocket(lpwfs->lstnSocket);
1728 lpwfs->lstnSocket = -1;
1731 CloseHandle(hFile);
1733 hIC = lpwfs->lpAppInfo;
1734 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1736 INTERNET_ASYNC_RESULT iar;
1738 iar.dwResult = (DWORD)bSuccess;
1739 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1740 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1741 &iar, sizeof(INTERNET_ASYNC_RESULT));
1744 if (!bSuccess) DeleteFileW(lpszNewFile);
1745 return bSuccess;
1748 /***********************************************************************
1749 * FtpGetFileSize (WININET.@)
1751 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1753 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1755 if (lpdwFileSizeHigh)
1756 *lpdwFileSizeHigh = 0;
1758 return 0;
1761 /***********************************************************************
1762 * FtpDeleteFileA (WININET.@)
1764 * Delete a file on the ftp server
1766 * RETURNS
1767 * TRUE on success
1768 * FALSE on failure
1771 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1773 LPWSTR lpwzFileName;
1774 BOOL ret;
1776 lpwzFileName = heap_strdupAtoW(lpszFileName);
1777 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1778 heap_free(lpwzFileName);
1779 return ret;
1782 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1784 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1785 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1787 TRACE("%p\n", lpwfs);
1789 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1790 heap_free(req->lpszFilename);
1793 /***********************************************************************
1794 * FtpDeleteFileW (WININET.@)
1796 * Delete a file on the ftp server
1798 * RETURNS
1799 * TRUE on success
1800 * FALSE on failure
1803 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1805 ftp_session_t *lpwfs;
1806 appinfo_t *hIC = NULL;
1807 BOOL r = FALSE;
1809 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1810 if (!lpwfs)
1812 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1813 return FALSE;
1816 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1818 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1819 goto lend;
1822 if (lpwfs->download_in_progress != NULL)
1824 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1825 goto lend;
1828 if (!lpszFileName)
1830 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1831 goto lend;
1834 hIC = lpwfs->lpAppInfo;
1835 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1837 WORKREQUEST workRequest;
1838 struct WORKREQ_FTPDELETEFILEW *req;
1840 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1841 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1842 req = &workRequest.u.FtpDeleteFileW;
1843 req->lpszFilename = heap_strdupW(lpszFileName);
1845 r = res_to_le(INTERNET_AsyncCall(&workRequest));
1847 else
1849 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1852 lend:
1853 WININET_Release( &lpwfs->hdr );
1855 return r;
1858 /***********************************************************************
1859 * FTP_FtpDeleteFileW (Internal)
1861 * Delete a file on the ftp server
1863 * RETURNS
1864 * TRUE on success
1865 * FALSE on failure
1868 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1870 INT nResCode;
1871 BOOL bSuccess = FALSE;
1872 appinfo_t *hIC = NULL;
1874 TRACE("%p\n", lpwfs);
1876 /* Clear any error information */
1877 INTERNET_SetLastError(0);
1879 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1880 goto lend;
1882 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1883 if (nResCode)
1885 if (nResCode == 250)
1886 bSuccess = TRUE;
1887 else
1888 FTP_SetResponseError(nResCode);
1890 lend:
1891 hIC = lpwfs->lpAppInfo;
1892 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1894 INTERNET_ASYNC_RESULT iar;
1896 iar.dwResult = (DWORD)bSuccess;
1897 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1898 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1899 &iar, sizeof(INTERNET_ASYNC_RESULT));
1902 return bSuccess;
1906 /***********************************************************************
1907 * FtpRemoveDirectoryA (WININET.@)
1909 * Remove a directory on the ftp server
1911 * RETURNS
1912 * TRUE on success
1913 * FALSE on failure
1916 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1918 LPWSTR lpwzDirectory;
1919 BOOL ret;
1921 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1922 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1923 heap_free(lpwzDirectory);
1924 return ret;
1927 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1929 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1930 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1932 TRACE("%p\n", lpwfs);
1934 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1935 heap_free(req->lpszDirectory);
1938 /***********************************************************************
1939 * FtpRemoveDirectoryW (WININET.@)
1941 * Remove a directory on the ftp server
1943 * RETURNS
1944 * TRUE on success
1945 * FALSE on failure
1948 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1950 ftp_session_t *lpwfs;
1951 appinfo_t *hIC = NULL;
1952 BOOL r = FALSE;
1954 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1955 if (!lpwfs)
1957 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1958 return FALSE;
1961 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1963 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1964 goto lend;
1967 if (lpwfs->download_in_progress != NULL)
1969 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1970 goto lend;
1973 if (!lpszDirectory)
1975 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1976 goto lend;
1979 hIC = lpwfs->lpAppInfo;
1980 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1982 WORKREQUEST workRequest;
1983 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1985 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1986 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1987 req = &workRequest.u.FtpRemoveDirectoryW;
1988 req->lpszDirectory = heap_strdupW(lpszDirectory);
1990 r = res_to_le(INTERNET_AsyncCall(&workRequest));
1992 else
1994 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1997 lend:
1998 WININET_Release( &lpwfs->hdr );
2000 return r;
2003 /***********************************************************************
2004 * FTP_FtpRemoveDirectoryW (Internal)
2006 * Remove a directory on the ftp server
2008 * RETURNS
2009 * TRUE on success
2010 * FALSE on failure
2013 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
2015 INT nResCode;
2016 BOOL bSuccess = FALSE;
2017 appinfo_t *hIC = NULL;
2019 TRACE("\n");
2021 /* Clear any error information */
2022 INTERNET_SetLastError(0);
2024 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2025 goto lend;
2027 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2028 if (nResCode)
2030 if (nResCode == 250)
2031 bSuccess = TRUE;
2032 else
2033 FTP_SetResponseError(nResCode);
2036 lend:
2037 hIC = lpwfs->lpAppInfo;
2038 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2040 INTERNET_ASYNC_RESULT iar;
2042 iar.dwResult = (DWORD)bSuccess;
2043 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2044 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2045 &iar, sizeof(INTERNET_ASYNC_RESULT));
2048 return bSuccess;
2052 /***********************************************************************
2053 * FtpRenameFileA (WININET.@)
2055 * Rename a file on the ftp server
2057 * RETURNS
2058 * TRUE on success
2059 * FALSE on failure
2062 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2064 LPWSTR lpwzSrc;
2065 LPWSTR lpwzDest;
2066 BOOL ret;
2068 lpwzSrc = heap_strdupAtoW(lpszSrc);
2069 lpwzDest = heap_strdupAtoW(lpszDest);
2070 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2071 heap_free(lpwzSrc);
2072 heap_free(lpwzDest);
2073 return ret;
2076 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
2078 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
2079 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
2081 TRACE("%p\n", lpwfs);
2083 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
2084 heap_free(req->lpszSrcFile);
2085 heap_free(req->lpszDestFile);
2088 /***********************************************************************
2089 * FtpRenameFileW (WININET.@)
2091 * Rename a file on the ftp server
2093 * RETURNS
2094 * TRUE on success
2095 * FALSE on failure
2098 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2100 ftp_session_t *lpwfs;
2101 appinfo_t *hIC = NULL;
2102 BOOL r = FALSE;
2104 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
2105 if (!lpwfs)
2107 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2108 return FALSE;
2111 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2113 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2114 goto lend;
2117 if (lpwfs->download_in_progress != NULL)
2119 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2120 goto lend;
2123 if (!lpszSrc || !lpszDest)
2125 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2126 goto lend;
2129 hIC = lpwfs->lpAppInfo;
2130 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2132 WORKREQUEST workRequest;
2133 struct WORKREQ_FTPRENAMEFILEW *req;
2135 workRequest.asyncproc = AsyncFtpRenameFileProc;
2136 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
2137 req = &workRequest.u.FtpRenameFileW;
2138 req->lpszSrcFile = heap_strdupW(lpszSrc);
2139 req->lpszDestFile = heap_strdupW(lpszDest);
2141 r = res_to_le(INTERNET_AsyncCall(&workRequest));
2143 else
2145 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2148 lend:
2149 WININET_Release( &lpwfs->hdr );
2151 return r;
2154 /***********************************************************************
2155 * FTP_FtpRenameFileW (Internal)
2157 * Rename a file on the ftp server
2159 * RETURNS
2160 * TRUE on success
2161 * FALSE on failure
2164 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2166 INT nResCode;
2167 BOOL bSuccess = FALSE;
2168 appinfo_t *hIC = NULL;
2170 TRACE("\n");
2172 /* Clear any error information */
2173 INTERNET_SetLastError(0);
2175 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2176 goto lend;
2178 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2179 if (nResCode == 350)
2181 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2182 goto lend;
2184 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2187 if (nResCode == 250)
2188 bSuccess = TRUE;
2189 else
2190 FTP_SetResponseError(nResCode);
2192 lend:
2193 hIC = lpwfs->lpAppInfo;
2194 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2196 INTERNET_ASYNC_RESULT iar;
2198 iar.dwResult = (DWORD)bSuccess;
2199 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2200 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2201 &iar, sizeof(INTERNET_ASYNC_RESULT));
2204 return bSuccess;
2207 /***********************************************************************
2208 * FtpCommandA (WININET.@)
2210 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2211 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2213 BOOL r;
2214 WCHAR *cmdW;
2216 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2217 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2219 if (fExpectResponse)
2221 FIXME("data connection not supported\n");
2222 return FALSE;
2225 if (!lpszCommand || !lpszCommand[0])
2227 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2228 return FALSE;
2231 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2233 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2234 return FALSE;
2237 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2239 heap_free(cmdW);
2240 return r;
2243 /***********************************************************************
2244 * FtpCommandW (WININET.@)
2246 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2247 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2249 BOOL r = FALSE;
2250 ftp_session_t *lpwfs;
2251 LPSTR cmd = NULL;
2252 DWORD len, nBytesSent= 0;
2253 INT nResCode, nRC = 0;
2255 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2256 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2258 if (!lpszCommand || !lpszCommand[0])
2260 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2261 return FALSE;
2264 if (fExpectResponse)
2266 FIXME("data connection not supported\n");
2267 return FALSE;
2270 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
2271 if (!lpwfs)
2273 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2274 return FALSE;
2277 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2279 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2280 goto lend;
2283 if (lpwfs->download_in_progress != NULL)
2285 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2286 goto lend;
2289 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2290 if ((cmd = heap_alloc(len)))
2291 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2292 else
2294 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2295 goto lend;
2298 strcat(cmd, szCRLF);
2299 len--;
2301 TRACE("Sending (%s) len(%d)\n", cmd, len);
2302 while ((nBytesSent < len) && (nRC != -1))
2304 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2305 if (nRC != -1)
2307 nBytesSent += nRC;
2308 TRACE("Sent %d bytes\n", nRC);
2312 if (nBytesSent)
2314 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2315 if (nResCode > 0 && nResCode < 400)
2316 r = TRUE;
2317 else
2318 FTP_SetResponseError(nResCode);
2321 lend:
2322 WININET_Release( &lpwfs->hdr );
2323 heap_free( cmd );
2324 return r;
2328 /***********************************************************************
2329 * FTPSESSION_Destroy (internal)
2331 * Deallocate session handle
2333 static void FTPSESSION_Destroy(object_header_t *hdr)
2335 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2337 TRACE("\n");
2339 WININET_Release(&lpwfs->lpAppInfo->hdr);
2341 heap_free(lpwfs->lpszPassword);
2342 heap_free(lpwfs->lpszUserName);
2343 heap_free(lpwfs->servername);
2346 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2348 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2350 TRACE("\n");
2352 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2353 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2355 if (lpwfs->download_in_progress != NULL)
2356 lpwfs->download_in_progress->session_deleted = TRUE;
2358 if (lpwfs->sndSocket != -1)
2359 closesocket(lpwfs->sndSocket);
2361 if (lpwfs->lstnSocket != -1)
2362 closesocket(lpwfs->lstnSocket);
2364 if (lpwfs->pasvSocket != -1)
2365 closesocket(lpwfs->pasvSocket);
2367 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2368 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2371 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2373 switch(option) {
2374 case INTERNET_OPTION_HANDLE_TYPE:
2375 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2377 if (*size < sizeof(ULONG))
2378 return ERROR_INSUFFICIENT_BUFFER;
2380 *size = sizeof(DWORD);
2381 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2382 return ERROR_SUCCESS;
2385 return INET_QueryOption(hdr, option, buffer, size, unicode);
2388 static const object_vtbl_t FTPSESSIONVtbl = {
2389 FTPSESSION_Destroy,
2390 FTPSESSION_CloseConnection,
2391 FTPSESSION_QueryOption,
2392 INET_SetOption,
2393 NULL,
2394 NULL,
2395 NULL,
2396 NULL,
2397 NULL
2401 /***********************************************************************
2402 * FTP_Connect (internal)
2404 * Connect to a ftp server
2406 * RETURNS
2407 * HINTERNET a session handle on success
2408 * NULL on failure
2410 * NOTES:
2412 * Windows uses 'anonymous' as the username, when given a NULL username
2413 * and a NULL password. The password is first looked up in:
2415 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2417 * If this entry is not present it uses the current username as the password.
2421 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2422 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2423 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2424 DWORD dwInternalFlags)
2426 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2427 'M','i','c','r','o','s','o','f','t','\\',
2428 'W','i','n','d','o','w','s','\\',
2429 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2430 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2431 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2432 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2433 static const WCHAR szEmpty[] = {'\0'};
2434 struct sockaddr_in socketAddr;
2435 INT nsocket = -1;
2436 UINT sock_namelen;
2437 BOOL bSuccess = FALSE;
2438 ftp_session_t *lpwfs = NULL;
2439 char szaddr[INET_ADDRSTRLEN];
2441 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2442 hIC, debugstr_w(lpszServerName),
2443 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2445 assert( hIC->hdr.htype == WH_HINIT );
2447 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2449 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2450 return NULL;
2453 lpwfs = alloc_object(&hIC->hdr, &FTPSESSIONVtbl, sizeof(ftp_session_t));
2454 if (NULL == lpwfs)
2456 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2457 return NULL;
2460 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2461 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2462 else
2463 lpwfs->serverport = nServerPort;
2465 lpwfs->hdr.htype = WH_HFTPSESSION;
2466 lpwfs->hdr.dwFlags = dwFlags;
2467 lpwfs->hdr.dwContext = dwContext;
2468 lpwfs->hdr.dwInternalFlags |= dwInternalFlags;
2469 lpwfs->download_in_progress = NULL;
2470 lpwfs->sndSocket = -1;
2471 lpwfs->lstnSocket = -1;
2472 lpwfs->pasvSocket = -1;
2474 WININET_AddRef( &hIC->hdr );
2475 lpwfs->lpAppInfo = hIC;
2476 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2478 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
2479 if(strchrW(hIC->proxy, ' '))
2480 FIXME("Several proxies not implemented.\n");
2481 if(hIC->proxyBypass)
2482 FIXME("Proxy bypass is ignored.\n");
2484 if (!lpszUserName || !strlenW(lpszUserName)) {
2485 HKEY key;
2486 WCHAR szPassword[MAX_PATH];
2487 DWORD len = sizeof(szPassword);
2489 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2491 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2492 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2493 /* Nothing in the registry, get the username and use that as the password */
2494 if (!GetUserNameW(szPassword, &len)) {
2495 /* Should never get here, but use an empty password as failsafe */
2496 strcpyW(szPassword, szEmpty);
2499 RegCloseKey(key);
2501 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2502 lpwfs->lpszPassword = heap_strdupW(szPassword);
2504 else {
2505 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2506 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2508 lpwfs->servername = heap_strdupW(lpszServerName);
2510 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2511 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2513 INTERNET_ASYNC_RESULT iar;
2515 iar.dwResult = (DWORD_PTR)lpwfs->hdr.hInternet;
2516 iar.dwError = ERROR_SUCCESS;
2518 SendAsyncCallback(&hIC->hdr, dwContext,
2519 INTERNET_STATUS_HANDLE_CREATED, &iar,
2520 sizeof(INTERNET_ASYNC_RESULT));
2523 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2524 (LPWSTR) lpszServerName, (strlenW(lpszServerName)+1) * sizeof(WCHAR));
2526 sock_namelen = sizeof(socketAddr);
2527 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen))
2529 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2530 goto lerror;
2533 if (socketAddr.sin_family != AF_INET)
2535 WARN("unsupported address family %d\n", socketAddr.sin_family);
2536 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2537 goto lerror;
2540 inet_ntop(socketAddr.sin_family, &socketAddr.sin_addr, szaddr, sizeof(szaddr));
2541 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2542 szaddr, strlen(szaddr)+1);
2544 nsocket = socket(AF_INET,SOCK_STREAM,0);
2545 if (nsocket == -1)
2547 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2548 goto lerror;
2551 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2552 szaddr, strlen(szaddr)+1);
2554 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2556 ERR("Unable to connect (%s)\n", strerror(errno));
2557 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2558 closesocket(nsocket);
2560 else
2562 TRACE("Connected to server\n");
2563 lpwfs->sndSocket = nsocket;
2564 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2565 szaddr, strlen(szaddr)+1);
2567 sock_namelen = sizeof(lpwfs->socketAddress);
2568 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2570 if (FTP_ConnectToHost(lpwfs))
2572 TRACE("Successfully logged into server\n");
2573 bSuccess = TRUE;
2577 lerror:
2578 if (!bSuccess)
2580 if(lpwfs)
2581 WININET_Release( &lpwfs->hdr );
2582 return NULL;
2585 return lpwfs->hdr.hInternet;
2589 /***********************************************************************
2590 * FTP_ConnectToHost (internal)
2592 * Connect to a ftp server
2594 * RETURNS
2595 * TRUE on success
2596 * NULL on failure
2599 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2601 INT nResCode;
2602 BOOL bSuccess = FALSE;
2604 TRACE("\n");
2605 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2607 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2608 goto lend;
2610 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2611 if (nResCode)
2613 /* Login successful... */
2614 if (nResCode == 230)
2615 bSuccess = TRUE;
2616 /* User name okay, need password... */
2617 else if (nResCode == 331)
2618 bSuccess = FTP_SendPassword(lpwfs);
2619 /* Need account for login... */
2620 else if (nResCode == 332)
2621 bSuccess = FTP_SendAccount(lpwfs);
2622 else
2623 FTP_SetResponseError(nResCode);
2626 TRACE("Returning %d\n", bSuccess);
2627 lend:
2628 return bSuccess;
2632 /***********************************************************************
2633 * FTP_SendCommandA (internal)
2635 * Send command to server
2637 * RETURNS
2638 * TRUE on success
2639 * NULL on failure
2642 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2643 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2645 DWORD len;
2646 CHAR *buf;
2647 DWORD nBytesSent = 0;
2648 int nRC = 0;
2649 DWORD dwParamLen;
2651 TRACE("%d: (%s) %d\n", ftpCmd, debugstr_a(lpszParam), nSocket);
2653 if (lpfnStatusCB)
2655 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2658 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2659 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2660 if (NULL == (buf = heap_alloc(len+1)))
2662 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2663 return FALSE;
2665 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2666 dwParamLen ? lpszParam : "", szCRLF);
2668 TRACE("Sending (%s) len(%d)\n", buf, len);
2669 while((nBytesSent < len) && (nRC != -1))
2671 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2672 nBytesSent += nRC;
2674 heap_free(buf);
2676 if (lpfnStatusCB)
2678 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2679 &nBytesSent, sizeof(DWORD));
2682 TRACE("Sent %d bytes\n", nBytesSent);
2683 return (nRC != -1);
2686 /***********************************************************************
2687 * FTP_SendCommand (internal)
2689 * Send command to server
2691 * RETURNS
2692 * TRUE on success
2693 * NULL on failure
2696 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2697 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2699 BOOL ret;
2700 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2701 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2702 heap_free(lpszParamA);
2703 return ret;
2706 /***********************************************************************
2707 * FTP_ReceiveResponse (internal)
2709 * Receive response from server
2711 * RETURNS
2712 * Reply code on success
2713 * 0 on failure
2716 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2718 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2719 DWORD nRecv;
2720 INT rc = 0;
2721 char firstprefix[5];
2722 BOOL multiline = FALSE;
2724 TRACE("socket(%d)\n", lpwfs->sndSocket);
2726 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2728 while(1)
2730 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2731 goto lerror;
2733 if (nRecv >= 3)
2735 if(!multiline)
2737 if(lpszResponse[3] != '-')
2738 break;
2739 else
2740 { /* Start of multiline response. Loop until we get "nnn " */
2741 multiline = TRUE;
2742 memcpy(firstprefix, lpszResponse, 3);
2743 firstprefix[3] = ' ';
2744 firstprefix[4] = '\0';
2747 else
2749 if(!memcmp(firstprefix, lpszResponse, 4))
2750 break;
2755 if (nRecv >= 3)
2757 rc = atoi(lpszResponse);
2759 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2760 &nRecv, sizeof(DWORD));
2763 lerror:
2764 TRACE("return %d\n", rc);
2765 return rc;
2769 /***********************************************************************
2770 * FTP_SendPassword (internal)
2772 * Send password to ftp server
2774 * RETURNS
2775 * TRUE on success
2776 * NULL on failure
2779 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2781 INT nResCode;
2782 BOOL bSuccess = FALSE;
2784 TRACE("\n");
2785 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2786 goto lend;
2788 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2789 if (nResCode)
2791 TRACE("Received reply code %d\n", nResCode);
2792 /* Login successful... */
2793 if (nResCode == 230)
2794 bSuccess = TRUE;
2795 /* Command not implemented, superfluous at the server site... */
2796 /* Need account for login... */
2797 else if (nResCode == 332)
2798 bSuccess = FTP_SendAccount(lpwfs);
2799 else
2800 FTP_SetResponseError(nResCode);
2803 lend:
2804 TRACE("Returning %d\n", bSuccess);
2805 return bSuccess;
2809 /***********************************************************************
2810 * FTP_SendAccount (internal)
2814 * RETURNS
2815 * TRUE on success
2816 * FALSE on failure
2819 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2821 INT nResCode;
2822 BOOL bSuccess = FALSE;
2824 TRACE("\n");
2825 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2826 goto lend;
2828 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2829 if (nResCode)
2830 bSuccess = TRUE;
2831 else
2832 FTP_SetResponseError(nResCode);
2834 lend:
2835 return bSuccess;
2839 /***********************************************************************
2840 * FTP_SendStore (internal)
2842 * Send request to upload file to ftp server
2844 * RETURNS
2845 * TRUE on success
2846 * FALSE on failure
2849 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2851 INT nResCode;
2852 BOOL bSuccess = FALSE;
2854 TRACE("\n");
2855 if (!FTP_InitListenSocket(lpwfs))
2856 goto lend;
2858 if (!FTP_SendType(lpwfs, dwType))
2859 goto lend;
2861 if (!FTP_SendPortOrPasv(lpwfs))
2862 goto lend;
2864 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2865 goto lend;
2866 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2867 if (nResCode)
2869 if (nResCode == 150 || nResCode == 125)
2870 bSuccess = TRUE;
2871 else
2872 FTP_SetResponseError(nResCode);
2875 lend:
2876 if (!bSuccess && lpwfs->lstnSocket != -1)
2878 closesocket(lpwfs->lstnSocket);
2879 lpwfs->lstnSocket = -1;
2882 return bSuccess;
2886 /***********************************************************************
2887 * FTP_InitListenSocket (internal)
2889 * Create a socket to listen for server response
2891 * RETURNS
2892 * TRUE on success
2893 * FALSE on failure
2896 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2898 BOOL bSuccess = FALSE;
2899 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2901 TRACE("\n");
2903 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2904 if (lpwfs->lstnSocket == -1)
2906 TRACE("Unable to create listening socket\n");
2907 goto lend;
2910 /* We obtain our ip addr from the name of the command channel socket */
2911 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2913 /* and get the system to assign us a port */
2914 lpwfs->lstnSocketAddress.sin_port = htons(0);
2916 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2918 TRACE("Unable to bind socket\n");
2919 goto lend;
2922 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2924 TRACE("listen failed\n");
2925 goto lend;
2928 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2929 bSuccess = TRUE;
2931 lend:
2932 if (!bSuccess && lpwfs->lstnSocket != -1)
2934 closesocket(lpwfs->lstnSocket);
2935 lpwfs->lstnSocket = -1;
2938 return bSuccess;
2942 /***********************************************************************
2943 * FTP_SendType (internal)
2945 * Tell server type of data being transferred
2947 * RETURNS
2948 * TRUE on success
2949 * FALSE on failure
2951 * W98SE doesn't cache the type that's currently set
2952 * (i.e. it sends it always),
2953 * so we probably don't want to do that either.
2955 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2957 INT nResCode;
2958 WCHAR type[] = { 'I','\0' };
2959 BOOL bSuccess = FALSE;
2961 TRACE("\n");
2962 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2963 type[0] = 'A';
2965 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2966 goto lend;
2968 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2969 if (nResCode)
2971 if (nResCode == 2)
2972 bSuccess = TRUE;
2973 else
2974 FTP_SetResponseError(nResCode);
2977 lend:
2978 return bSuccess;
2982 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2983 /***********************************************************************
2984 * FTP_GetFileSize (internal)
2986 * Retrieves from the server the size of the given file
2988 * RETURNS
2989 * TRUE on success
2990 * FALSE on failure
2993 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2995 INT nResCode;
2996 BOOL bSuccess = FALSE;
2998 TRACE("\n");
3000 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
3001 goto lend;
3003 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3004 if (nResCode)
3006 if (nResCode == 213) {
3007 /* Now parses the output to get the actual file size */
3008 int i;
3009 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3011 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3012 if (lpszResponseBuffer[i] == '\0') return FALSE;
3013 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3015 bSuccess = TRUE;
3016 } else {
3017 FTP_SetResponseError(nResCode);
3021 lend:
3022 return bSuccess;
3024 #endif
3027 /***********************************************************************
3028 * FTP_SendPort (internal)
3030 * Tell server which port to use
3032 * RETURNS
3033 * TRUE on success
3034 * FALSE on failure
3037 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3039 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3040 INT nResCode;
3041 WCHAR szIPAddress[64];
3042 BOOL bSuccess = FALSE;
3043 TRACE("\n");
3045 sprintfW(szIPAddress, szIPFormat,
3046 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
3047 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
3048 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
3049 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
3050 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3051 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3053 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3054 goto lend;
3056 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3057 if (nResCode)
3059 if (nResCode == 200)
3060 bSuccess = TRUE;
3061 else
3062 FTP_SetResponseError(nResCode);
3065 lend:
3066 return bSuccess;
3070 /***********************************************************************
3071 * FTP_DoPassive (internal)
3073 * Tell server that we want to do passive transfers
3074 * and connect data socket
3076 * RETURNS
3077 * TRUE on success
3078 * FALSE on failure
3081 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3083 INT nResCode;
3084 BOOL bSuccess = FALSE;
3086 TRACE("\n");
3087 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3088 goto lend;
3090 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3091 if (nResCode)
3093 if (nResCode == 227)
3095 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3096 LPSTR p;
3097 int f[6];
3098 int i;
3099 char *pAddr, *pPort;
3100 INT nsocket = -1;
3101 struct sockaddr_in dataSocketAddress;
3103 p = lpszResponseBuffer+4; /* skip status code */
3104 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3106 if (*p == '\0')
3108 ERR("no address found in response, aborting\n");
3109 goto lend;
3112 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3113 &f[4], &f[5]) != 6)
3115 ERR("unknown response address format '%s', aborting\n", p);
3116 goto lend;
3118 for (i=0; i < 6; i++)
3119 f[i] = f[i] & 0xff;
3121 dataSocketAddress = lpwfs->socketAddress;
3122 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3123 pPort = (char *)&(dataSocketAddress.sin_port);
3124 pAddr[0] = f[0];
3125 pAddr[1] = f[1];
3126 pAddr[2] = f[2];
3127 pAddr[3] = f[3];
3128 pPort[0] = f[4];
3129 pPort[1] = f[5];
3131 nsocket = socket(AF_INET,SOCK_STREAM,0);
3132 if (nsocket == -1)
3133 goto lend;
3135 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3137 ERR("can't connect passive FTP data port.\n");
3138 closesocket(nsocket);
3139 goto lend;
3141 lpwfs->pasvSocket = nsocket;
3142 bSuccess = TRUE;
3144 else
3145 FTP_SetResponseError(nResCode);
3148 lend:
3149 return bSuccess;
3153 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3155 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3157 if (!FTP_DoPassive(lpwfs))
3158 return FALSE;
3160 else
3162 if (!FTP_SendPort(lpwfs))
3163 return FALSE;
3165 return TRUE;
3169 /***********************************************************************
3170 * FTP_GetDataSocket (internal)
3172 * Either accepts an incoming data socket connection from the server
3173 * or just returns the already opened socket after a PASV command
3174 * in case of passive FTP.
3177 * RETURNS
3178 * TRUE on success
3179 * FALSE on failure
3182 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3184 struct sockaddr_in saddr;
3185 socklen_t addrlen = sizeof(struct sockaddr);
3187 TRACE("\n");
3188 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3190 *nDataSocket = lpwfs->pasvSocket;
3191 lpwfs->pasvSocket = -1;
3193 else
3195 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3196 closesocket(lpwfs->lstnSocket);
3197 lpwfs->lstnSocket = -1;
3199 return *nDataSocket != -1;
3203 /***********************************************************************
3204 * FTP_SendData (internal)
3206 * Send data to the server
3208 * RETURNS
3209 * TRUE on success
3210 * FALSE on failure
3213 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3215 BY_HANDLE_FILE_INFORMATION fi;
3216 DWORD nBytesRead = 0;
3217 DWORD nBytesSent = 0;
3218 DWORD nTotalSent = 0;
3219 DWORD nBytesToSend, nLen;
3220 int nRC = 1;
3221 time_t s_long_time, e_long_time;
3222 LONG nSeconds;
3223 CHAR *lpszBuffer;
3225 TRACE("\n");
3226 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3228 /* Get the size of the file. */
3229 GetFileInformationByHandle(hFile, &fi);
3230 time(&s_long_time);
3234 nBytesToSend = nBytesRead - nBytesSent;
3236 if (nBytesToSend <= 0)
3238 /* Read data from file. */
3239 nBytesSent = 0;
3240 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3241 ERR("Failed reading from file\n");
3243 if (nBytesRead > 0)
3244 nBytesToSend = nBytesRead;
3245 else
3246 break;
3249 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3250 DATA_PACKET_SIZE : nBytesToSend;
3251 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3253 if (nRC != -1)
3255 nBytesSent += nRC;
3256 nTotalSent += nRC;
3259 /* Do some computation to display the status. */
3260 time(&e_long_time);
3261 nSeconds = e_long_time - s_long_time;
3262 if( nSeconds / 60 > 0 )
3264 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3265 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3266 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3268 else
3270 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3271 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3272 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3274 } while (nRC != -1);
3276 TRACE("file transfer complete!\n");
3278 heap_free(lpszBuffer);
3279 return nTotalSent;
3283 /***********************************************************************
3284 * FTP_SendRetrieve (internal)
3286 * Send request to retrieve a file
3288 * RETURNS
3289 * Number of bytes to be received on success
3290 * 0 on failure
3293 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3295 INT nResCode;
3296 BOOL ret;
3298 TRACE("\n");
3299 if (!(ret = FTP_InitListenSocket(lpwfs)))
3300 goto lend;
3302 if (!(ret = FTP_SendType(lpwfs, dwType)))
3303 goto lend;
3305 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3306 goto lend;
3308 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3309 goto lend;
3311 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3312 if ((nResCode != 125) && (nResCode != 150)) {
3313 /* That means that we got an error getting the file. */
3314 FTP_SetResponseError(nResCode);
3315 ret = FALSE;
3318 lend:
3319 if (!ret && lpwfs->lstnSocket != -1)
3321 closesocket(lpwfs->lstnSocket);
3322 lpwfs->lstnSocket = -1;
3325 return ret;
3329 /***********************************************************************
3330 * FTP_RetrieveData (internal)
3332 * Retrieve data from server
3334 * RETURNS
3335 * TRUE on success
3336 * FALSE on failure
3339 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3341 DWORD nBytesWritten;
3342 INT nRC = 0;
3343 CHAR *lpszBuffer;
3345 TRACE("\n");
3347 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3348 if (NULL == lpszBuffer)
3350 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3351 return FALSE;
3354 while (nRC != -1)
3356 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3357 if (nRC != -1)
3359 /* other side closed socket. */
3360 if (nRC == 0)
3361 goto recv_end;
3362 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3366 TRACE("Data transfer complete\n");
3368 recv_end:
3369 heap_free(lpszBuffer);
3370 return (nRC != -1);
3373 /***********************************************************************
3374 * FTPFINDNEXT_Destroy (internal)
3376 * Deallocate session handle
3378 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3380 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3381 DWORD i;
3383 TRACE("\n");
3385 WININET_Release(&lpwfn->lpFtpSession->hdr);
3387 for (i = 0; i < lpwfn->size; i++)
3389 heap_free(lpwfn->lpafp[i].lpszName);
3391 heap_free(lpwfn->lpafp);
3394 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3396 WIN32_FIND_DATAW *find_data = data;
3397 DWORD res = ERROR_SUCCESS;
3399 TRACE("index(%d) size(%d)\n", find->index, find->size);
3401 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3403 if (find->index < find->size) {
3404 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3405 find->index++;
3407 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3408 }else {
3409 res = ERROR_NO_MORE_FILES;
3412 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3414 INTERNET_ASYNC_RESULT iar;
3416 iar.dwResult = (res == ERROR_SUCCESS);
3417 iar.dwError = res;
3419 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3420 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3421 sizeof(INTERNET_ASYNC_RESULT));
3424 return res;
3427 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3429 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3431 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3434 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3436 switch(option) {
3437 case INTERNET_OPTION_HANDLE_TYPE:
3438 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3440 if (*size < sizeof(ULONG))
3441 return ERROR_INSUFFICIENT_BUFFER;
3443 *size = sizeof(DWORD);
3444 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3445 return ERROR_SUCCESS;
3448 return INET_QueryOption(hdr, option, buffer, size, unicode);
3451 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3453 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3455 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3457 WORKREQUEST workRequest;
3458 struct WORKREQ_FTPFINDNEXTW *req;
3460 workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3461 workRequest.hdr = WININET_AddRef( &find->hdr );
3462 req = &workRequest.u.FtpFindNextW;
3463 req->lpFindFileData = data;
3465 INTERNET_AsyncCall(&workRequest);
3467 return ERROR_SUCCESS;
3470 return FTPFINDNEXT_FindNextFileProc(find, data);
3473 static const object_vtbl_t FTPFINDNEXTVtbl = {
3474 FTPFINDNEXT_Destroy,
3475 NULL,
3476 FTPFINDNEXT_QueryOption,
3477 INET_SetOption,
3478 NULL,
3479 NULL,
3480 NULL,
3481 NULL,
3482 NULL,
3483 FTPFINDNEXT_FindNextFileW
3486 /***********************************************************************
3487 * FTP_ReceiveFileList (internal)
3489 * Read file list from server
3491 * RETURNS
3492 * Handle to file list on success
3493 * NULL on failure
3496 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3497 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3499 DWORD dwSize = 0;
3500 LPFILEPROPERTIESW lpafp = NULL;
3501 LPWININETFTPFINDNEXTW lpwfn = NULL;
3503 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3505 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3507 if(lpFindFileData)
3508 FTP_ConvertFileProp(lpafp, lpFindFileData);
3510 lpwfn = alloc_object(&lpwfs->hdr, &FTPFINDNEXTVtbl, sizeof(WININETFTPFINDNEXTW));
3511 if (lpwfn)
3513 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3514 lpwfn->hdr.dwContext = dwContext;
3515 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3516 lpwfn->size = dwSize;
3517 lpwfn->lpafp = lpafp;
3519 WININET_AddRef( &lpwfs->hdr );
3520 lpwfn->lpFtpSession = lpwfs;
3521 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3525 TRACE("Matched %d files\n", dwSize);
3526 return lpwfn ? lpwfn->hdr.hInternet : NULL;
3530 /***********************************************************************
3531 * FTP_ConvertFileProp (internal)
3533 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3535 * RETURNS
3536 * TRUE on success
3537 * FALSE on failure
3540 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3542 BOOL bSuccess = FALSE;
3544 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3546 if (lpafp)
3548 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3549 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3550 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3552 /* Not all fields are filled in */
3553 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3554 lpFindFileData->nFileSizeLow = lpafp->nSize;
3556 if (lpafp->bIsDirectory)
3557 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3559 if (lpafp->lpszName)
3560 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3562 bSuccess = TRUE;
3565 return bSuccess;
3568 /***********************************************************************
3569 * FTP_ParseNextFile (internal)
3571 * Parse the next line in file listing
3573 * RETURNS
3574 * TRUE on success
3575 * FALSE on failure
3577 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3579 static const char szSpace[] = " \t";
3580 DWORD nBufLen;
3581 char *pszLine;
3582 char *pszToken;
3583 char *pszTmp;
3584 BOOL found = FALSE;
3585 int i;
3587 lpfp->lpszName = NULL;
3588 do {
3589 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3590 return FALSE;
3592 pszToken = strtok(pszLine, szSpace);
3593 /* ls format
3594 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3596 * For instance:
3597 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3599 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3600 if(!FTP_ParsePermission(pszToken, lpfp))
3601 lpfp->bIsDirectory = FALSE;
3602 for(i=0; i<=3; i++) {
3603 if(!(pszToken = strtok(NULL, szSpace)))
3604 break;
3606 if(!pszToken) continue;
3607 if(lpfp->bIsDirectory) {
3608 TRACE("Is directory\n");
3609 lpfp->nSize = 0;
3611 else {
3612 TRACE("Size: %s\n", pszToken);
3613 lpfp->nSize = atol(pszToken);
3616 lpfp->tmLastModified.wSecond = 0;
3617 lpfp->tmLastModified.wMinute = 0;
3618 lpfp->tmLastModified.wHour = 0;
3619 lpfp->tmLastModified.wDay = 0;
3620 lpfp->tmLastModified.wMonth = 0;
3621 lpfp->tmLastModified.wYear = 0;
3623 /* Determine month */
3624 pszToken = strtok(NULL, szSpace);
3625 if(!pszToken) continue;
3626 if(strlen(pszToken) >= 3) {
3627 pszToken[3] = 0;
3628 if((pszTmp = StrStrIA(szMonths, pszToken)))
3629 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3631 /* Determine day */
3632 pszToken = strtok(NULL, szSpace);
3633 if(!pszToken) continue;
3634 lpfp->tmLastModified.wDay = atoi(pszToken);
3635 /* Determine time or year */
3636 pszToken = strtok(NULL, szSpace);
3637 if(!pszToken) continue;
3638 if((pszTmp = strchr(pszToken, ':'))) {
3639 SYSTEMTIME curr_time;
3640 *pszTmp = 0;
3641 pszTmp++;
3642 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3643 lpfp->tmLastModified.wHour = atoi(pszToken);
3644 GetLocalTime( &curr_time );
3645 lpfp->tmLastModified.wYear = curr_time.wYear;
3647 else {
3648 lpfp->tmLastModified.wYear = atoi(pszToken);
3649 lpfp->tmLastModified.wHour = 12;
3651 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3652 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3653 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3655 pszToken = strtok(NULL, szSpace);
3656 if(!pszToken) continue;
3657 lpfp->lpszName = heap_strdupAtoW(pszToken);
3658 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3660 /* NT way of parsing ... :
3662 07-13-03 08:55PM <DIR> sakpatch
3663 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3665 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3666 int mon, mday, year, hour, min;
3667 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3669 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3670 lpfp->tmLastModified.wDay = mday;
3671 lpfp->tmLastModified.wMonth = mon;
3672 lpfp->tmLastModified.wYear = year;
3674 /* Hacky and bad Y2K protection :-) */
3675 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3677 pszToken = strtok(NULL, szSpace);
3678 if(!pszToken) continue;
3679 sscanf(pszToken, "%d:%d", &hour, &min);
3680 lpfp->tmLastModified.wHour = hour;
3681 lpfp->tmLastModified.wMinute = min;
3682 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3683 lpfp->tmLastModified.wHour += 12;
3685 lpfp->tmLastModified.wSecond = 0;
3687 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3688 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3689 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3691 pszToken = strtok(NULL, szSpace);
3692 if(!pszToken) continue;
3693 if(!strcasecmp(pszToken, "<DIR>")) {
3694 lpfp->bIsDirectory = TRUE;
3695 lpfp->nSize = 0;
3696 TRACE("Is directory\n");
3698 else {
3699 lpfp->bIsDirectory = FALSE;
3700 lpfp->nSize = atol(pszToken);
3701 TRACE("Size: %d\n", lpfp->nSize);
3704 pszToken = strtok(NULL, szSpace);
3705 if(!pszToken) continue;
3706 lpfp->lpszName = heap_strdupAtoW(pszToken);
3707 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3709 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3710 else if(pszToken[0] == '+') {
3711 FIXME("EPLF Format not implemented\n");
3714 if(lpfp->lpszName) {
3715 if((lpszSearchFile == NULL) ||
3716 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3717 found = TRUE;
3718 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3720 else {
3721 heap_free(lpfp->lpszName);
3722 lpfp->lpszName = NULL;
3725 } while(!found);
3726 return TRUE;
3729 /***********************************************************************
3730 * FTP_ParseDirectory (internal)
3732 * Parse string of directory information
3734 * RETURNS
3735 * TRUE on success
3736 * FALSE on failure
3738 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3739 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3741 BOOL bSuccess = TRUE;
3742 INT sizeFilePropArray = 500;/*20; */
3743 INT indexFilePropArray = -1;
3745 TRACE("\n");
3747 /* Allocate initial file properties array */
3748 *lpafp = heap_alloc_zero(sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3749 if (!*lpafp)
3750 return FALSE;
3752 do {
3753 if (indexFilePropArray+1 >= sizeFilePropArray)
3755 LPFILEPROPERTIESW tmpafp;
3757 sizeFilePropArray *= 2;
3758 tmpafp = heap_realloc_zero(*lpafp, sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3759 if (NULL == tmpafp)
3761 bSuccess = FALSE;
3762 break;
3765 *lpafp = tmpafp;
3767 indexFilePropArray++;
3768 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3770 if (bSuccess && indexFilePropArray)
3772 if (indexFilePropArray < sizeFilePropArray - 1)
3774 LPFILEPROPERTIESW tmpafp;
3776 tmpafp = heap_realloc(*lpafp, sizeof(FILEPROPERTIESW)*indexFilePropArray);
3777 if (NULL != tmpafp)
3778 *lpafp = tmpafp;
3780 *dwfp = indexFilePropArray;
3782 else
3784 heap_free(*lpafp);
3785 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3786 bSuccess = FALSE;
3789 return bSuccess;
3793 /***********************************************************************
3794 * FTP_ParsePermission (internal)
3796 * Parse permission string of directory information
3798 * RETURNS
3799 * TRUE on success
3800 * FALSE on failure
3803 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3805 BOOL bSuccess = TRUE;
3806 unsigned short nPermission = 0;
3807 INT nPos = 1;
3808 INT nLast = 9;
3810 TRACE("\n");
3811 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3813 bSuccess = FALSE;
3814 return bSuccess;
3817 lpfp->bIsDirectory = (*lpszPermission == 'd');
3820 switch (nPos)
3822 case 1:
3823 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3824 break;
3825 case 2:
3826 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3827 break;
3828 case 3:
3829 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3830 break;
3831 case 4:
3832 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3833 break;
3834 case 5:
3835 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3836 break;
3837 case 6:
3838 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3839 break;
3840 case 7:
3841 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3842 break;
3843 case 8:
3844 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3845 break;
3846 case 9:
3847 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3848 break;
3850 nPos++;
3851 }while (nPos <= nLast);
3853 lpfp->permissions = nPermission;
3854 return bSuccess;
3858 /***********************************************************************
3859 * FTP_SetResponseError (internal)
3861 * Set the appropriate error code for a given response from the server
3863 * RETURNS
3866 static DWORD FTP_SetResponseError(DWORD dwResponse)
3868 DWORD dwCode = 0;
3870 switch(dwResponse)
3872 case 425: /* Cannot open data connection. */
3873 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3874 break;
3876 case 426: /* Connection closed, transer aborted. */
3877 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3878 break;
3880 case 530: /* Not logged in. Login incorrect. */
3881 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3882 break;
3884 case 421: /* Service not available - Server may be shutting down. */
3885 case 450: /* File action not taken. File may be busy. */
3886 case 451: /* Action aborted. Server error. */
3887 case 452: /* Action not taken. Insufficient storage space on server. */
3888 case 500: /* Syntax error. Command unrecognized. */
3889 case 501: /* Syntax error. Error in parameters or arguments. */
3890 case 502: /* Command not implemented. */
3891 case 503: /* Bad sequence of commands. */
3892 case 504: /* Command not implemented for that parameter. */
3893 case 532: /* Need account for storing files */
3894 case 550: /* File action not taken. File not found or no access. */
3895 case 551: /* Requested action aborted. Page type unknown */
3896 case 552: /* Action aborted. Exceeded storage allocation */
3897 case 553: /* Action not taken. File name not allowed. */
3899 default:
3900 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3901 break;
3904 INTERNET_SetLastError(dwCode);
3905 return dwCode;