mshtml: Implement nsIStandardURL interface in nsWineURI object.
[wine/multimedia.git] / dlls / wininet / ftp.c
blob74316b93a3f4b48c39864ea6b6bedeb9175f2200
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 NULL,
1301 FTPFILE_ReadFile,
1302 FTPFILE_ReadFileExA,
1303 FTPFILE_ReadFileExW,
1304 FTPFILE_WriteFile,
1305 FTPFILE_QueryDataAvailable,
1306 NULL
1309 /***********************************************************************
1310 * FTP_FtpOpenFileW (Internal)
1312 * Open a remote file for writing or reading
1314 * RETURNS
1315 * HINTERNET handle on success
1316 * NULL on failure
1319 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1320 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1321 DWORD_PTR dwContext)
1323 INT nDataSocket;
1324 BOOL bSuccess = FALSE;
1325 ftp_file_t *lpwh = NULL;
1326 appinfo_t *hIC = NULL;
1328 TRACE("\n");
1330 /* Clear any error information */
1331 INTERNET_SetLastError(0);
1333 if (GENERIC_READ == fdwAccess)
1335 /* Set up socket to retrieve data */
1336 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1338 else if (GENERIC_WRITE == fdwAccess)
1340 /* Set up socket to send data */
1341 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1344 /* Get data socket to server */
1345 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1347 lpwh = alloc_object(&lpwfs->hdr, &FTPFILEVtbl, sizeof(ftp_file_t));
1348 lpwh->hdr.htype = WH_HFILE;
1349 lpwh->hdr.dwFlags = dwFlags;
1350 lpwh->hdr.dwContext = dwContext;
1351 lpwh->nDataSocket = nDataSocket;
1352 lpwh->cache_file = NULL;
1353 lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1354 lpwh->session_deleted = FALSE;
1356 WININET_AddRef( &lpwfs->hdr );
1357 lpwh->lpFtpSession = lpwfs;
1358 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1360 /* Indicate that a download is currently in progress */
1361 lpwfs->download_in_progress = lpwh;
1364 if (lpwfs->lstnSocket != -1)
1366 closesocket(lpwfs->lstnSocket);
1367 lpwfs->lstnSocket = -1;
1370 if (bSuccess && fdwAccess == GENERIC_READ)
1372 WCHAR filename[MAX_PATH + 1];
1373 URL_COMPONENTSW uc;
1374 DWORD len;
1376 memset(&uc, 0, sizeof(uc));
1377 uc.dwStructSize = sizeof(uc);
1378 uc.nScheme = INTERNET_SCHEME_FTP;
1379 uc.lpszHostName = lpwfs->servername;
1380 uc.nPort = lpwfs->serverport;
1381 uc.lpszUserName = lpwfs->lpszUserName;
1382 uc.lpszUrlPath = heap_strdupW(lpszFileName);
1384 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1386 WCHAR *url = 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 if(lpwh)
1430 WININET_Release( &lpwh->hdr );
1431 return FALSE;
1434 return lpwh->hdr.hInternet;
1438 /***********************************************************************
1439 * FtpOpenFileA (WININET.@)
1441 * Open a remote file for writing or reading
1443 * RETURNS
1444 * HINTERNET handle on success
1445 * NULL on failure
1448 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1449 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1450 DWORD_PTR dwContext)
1452 LPWSTR lpwzFileName;
1453 HINTERNET ret;
1455 lpwzFileName = heap_strdupAtoW(lpszFileName);
1456 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1457 heap_free(lpwzFileName);
1458 return ret;
1462 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1464 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1465 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1467 TRACE("%p\n", lpwfs);
1469 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1470 req->dwAccess, req->dwFlags, req->dwContext);
1471 heap_free(req->lpszFilename);
1474 /***********************************************************************
1475 * FtpOpenFileW (WININET.@)
1477 * Open a remote file for writing or reading
1479 * RETURNS
1480 * HINTERNET handle on success
1481 * NULL on failure
1484 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1485 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1486 DWORD_PTR dwContext)
1488 ftp_session_t *lpwfs;
1489 appinfo_t *hIC = NULL;
1490 HINTERNET r = NULL;
1492 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1493 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1495 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1496 if (!lpwfs)
1498 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1499 return FALSE;
1502 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1504 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1505 goto lend;
1508 if ((!lpszFileName) ||
1509 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1510 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1512 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1513 goto lend;
1516 if (lpwfs->download_in_progress != NULL)
1518 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1519 goto lend;
1522 hIC = lpwfs->lpAppInfo;
1523 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1525 WORKREQUEST workRequest;
1526 struct WORKREQ_FTPOPENFILEW *req;
1528 workRequest.asyncproc = AsyncFtpOpenFileProc;
1529 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1530 req = &workRequest.u.FtpOpenFileW;
1531 req->lpszFilename = heap_strdupW(lpszFileName);
1532 req->dwAccess = fdwAccess;
1533 req->dwFlags = dwFlags;
1534 req->dwContext = dwContext;
1536 INTERNET_AsyncCall(&workRequest);
1537 r = NULL;
1539 else
1541 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1544 lend:
1545 WININET_Release( &lpwfs->hdr );
1547 return r;
1551 /***********************************************************************
1552 * FtpGetFileA (WININET.@)
1554 * Retrieve file from the FTP server
1556 * RETURNS
1557 * TRUE on success
1558 * FALSE on failure
1561 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1562 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1563 DWORD_PTR dwContext)
1565 LPWSTR lpwzRemoteFile;
1566 LPWSTR lpwzNewFile;
1567 BOOL ret;
1569 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1570 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1571 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1572 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1573 heap_free(lpwzRemoteFile);
1574 heap_free(lpwzNewFile);
1575 return ret;
1579 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1581 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1582 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1584 TRACE("%p\n", lpwfs);
1586 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1587 req->lpszNewFile, req->fFailIfExists,
1588 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1589 heap_free(req->lpszRemoteFile);
1590 heap_free(req->lpszNewFile);
1594 /***********************************************************************
1595 * FtpGetFileW (WININET.@)
1597 * Retrieve file from the FTP server
1599 * RETURNS
1600 * TRUE on success
1601 * FALSE on failure
1604 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1605 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1606 DWORD_PTR dwContext)
1608 ftp_session_t *lpwfs;
1609 appinfo_t *hIC = NULL;
1610 BOOL r = FALSE;
1612 if (!lpszRemoteFile || !lpszNewFile)
1614 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1615 return FALSE;
1618 lpwfs = (ftp_session_t*) get_handle_object( hInternet );
1619 if (!lpwfs)
1621 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1622 return FALSE;
1625 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1627 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1628 goto lend;
1631 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1633 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1634 goto lend;
1637 if (lpwfs->download_in_progress != NULL)
1639 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1640 goto lend;
1643 hIC = lpwfs->lpAppInfo;
1644 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1646 WORKREQUEST workRequest;
1647 struct WORKREQ_FTPGETFILEW *req;
1649 workRequest.asyncproc = AsyncFtpGetFileProc;
1650 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1651 req = &workRequest.u.FtpGetFileW;
1652 req->lpszRemoteFile = heap_strdupW(lpszRemoteFile);
1653 req->lpszNewFile = heap_strdupW(lpszNewFile);
1654 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1655 req->fFailIfExists = fFailIfExists;
1656 req->dwFlags = dwInternetFlags;
1657 req->dwContext = dwContext;
1659 r = res_to_le(INTERNET_AsyncCall(&workRequest));
1661 else
1663 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1664 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1667 lend:
1668 WININET_Release( &lpwfs->hdr );
1670 return r;
1674 /***********************************************************************
1675 * FTP_FtpGetFileW (Internal)
1677 * Retrieve file from the FTP server
1679 * RETURNS
1680 * TRUE on success
1681 * FALSE on failure
1684 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1685 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1686 DWORD_PTR dwContext)
1688 BOOL bSuccess = FALSE;
1689 HANDLE hFile;
1690 appinfo_t *hIC = NULL;
1692 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1694 /* Clear any error information */
1695 INTERNET_SetLastError(0);
1697 /* Ensure we can write to lpszNewfile by opening it */
1698 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1699 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1700 if (INVALID_HANDLE_VALUE == hFile)
1701 return FALSE;
1703 /* Set up socket to retrieve data */
1704 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1706 INT nDataSocket;
1708 /* Get data socket to server */
1709 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1711 INT nResCode;
1713 /* Receive data */
1714 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1715 closesocket(nDataSocket);
1717 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1718 if (nResCode)
1720 if (nResCode == 226)
1721 bSuccess = TRUE;
1722 else
1723 FTP_SetResponseError(nResCode);
1728 if (lpwfs->lstnSocket != -1)
1730 closesocket(lpwfs->lstnSocket);
1731 lpwfs->lstnSocket = -1;
1734 CloseHandle(hFile);
1736 hIC = lpwfs->lpAppInfo;
1737 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1739 INTERNET_ASYNC_RESULT iar;
1741 iar.dwResult = (DWORD)bSuccess;
1742 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1743 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1744 &iar, sizeof(INTERNET_ASYNC_RESULT));
1747 if (!bSuccess) DeleteFileW(lpszNewFile);
1748 return bSuccess;
1751 /***********************************************************************
1752 * FtpGetFileSize (WININET.@)
1754 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1756 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1758 if (lpdwFileSizeHigh)
1759 *lpdwFileSizeHigh = 0;
1761 return 0;
1764 /***********************************************************************
1765 * FtpDeleteFileA (WININET.@)
1767 * Delete a file on the ftp server
1769 * RETURNS
1770 * TRUE on success
1771 * FALSE on failure
1774 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1776 LPWSTR lpwzFileName;
1777 BOOL ret;
1779 lpwzFileName = heap_strdupAtoW(lpszFileName);
1780 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1781 heap_free(lpwzFileName);
1782 return ret;
1785 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1787 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1788 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1790 TRACE("%p\n", lpwfs);
1792 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1793 heap_free(req->lpszFilename);
1796 /***********************************************************************
1797 * FtpDeleteFileW (WININET.@)
1799 * Delete a file on the ftp server
1801 * RETURNS
1802 * TRUE on success
1803 * FALSE on failure
1806 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1808 ftp_session_t *lpwfs;
1809 appinfo_t *hIC = NULL;
1810 BOOL r = FALSE;
1812 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1813 if (!lpwfs)
1815 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1816 return FALSE;
1819 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1821 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1822 goto lend;
1825 if (lpwfs->download_in_progress != NULL)
1827 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1828 goto lend;
1831 if (!lpszFileName)
1833 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1834 goto lend;
1837 hIC = lpwfs->lpAppInfo;
1838 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1840 WORKREQUEST workRequest;
1841 struct WORKREQ_FTPDELETEFILEW *req;
1843 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1844 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1845 req = &workRequest.u.FtpDeleteFileW;
1846 req->lpszFilename = heap_strdupW(lpszFileName);
1848 r = res_to_le(INTERNET_AsyncCall(&workRequest));
1850 else
1852 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1855 lend:
1856 WININET_Release( &lpwfs->hdr );
1858 return r;
1861 /***********************************************************************
1862 * FTP_FtpDeleteFileW (Internal)
1864 * Delete a file on the ftp server
1866 * RETURNS
1867 * TRUE on success
1868 * FALSE on failure
1871 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1873 INT nResCode;
1874 BOOL bSuccess = FALSE;
1875 appinfo_t *hIC = NULL;
1877 TRACE("%p\n", lpwfs);
1879 /* Clear any error information */
1880 INTERNET_SetLastError(0);
1882 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1883 goto lend;
1885 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1886 if (nResCode)
1888 if (nResCode == 250)
1889 bSuccess = TRUE;
1890 else
1891 FTP_SetResponseError(nResCode);
1893 lend:
1894 hIC = lpwfs->lpAppInfo;
1895 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1897 INTERNET_ASYNC_RESULT iar;
1899 iar.dwResult = (DWORD)bSuccess;
1900 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1901 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1902 &iar, sizeof(INTERNET_ASYNC_RESULT));
1905 return bSuccess;
1909 /***********************************************************************
1910 * FtpRemoveDirectoryA (WININET.@)
1912 * Remove a directory on the ftp server
1914 * RETURNS
1915 * TRUE on success
1916 * FALSE on failure
1919 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1921 LPWSTR lpwzDirectory;
1922 BOOL ret;
1924 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1925 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1926 heap_free(lpwzDirectory);
1927 return ret;
1930 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1932 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1933 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1935 TRACE("%p\n", lpwfs);
1937 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1938 heap_free(req->lpszDirectory);
1941 /***********************************************************************
1942 * FtpRemoveDirectoryW (WININET.@)
1944 * Remove a directory on the ftp server
1946 * RETURNS
1947 * TRUE on success
1948 * FALSE on failure
1951 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1953 ftp_session_t *lpwfs;
1954 appinfo_t *hIC = NULL;
1955 BOOL r = FALSE;
1957 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1958 if (!lpwfs)
1960 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1961 return FALSE;
1964 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1966 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1967 goto lend;
1970 if (lpwfs->download_in_progress != NULL)
1972 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1973 goto lend;
1976 if (!lpszDirectory)
1978 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1979 goto lend;
1982 hIC = lpwfs->lpAppInfo;
1983 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1985 WORKREQUEST workRequest;
1986 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1988 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1989 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1990 req = &workRequest.u.FtpRemoveDirectoryW;
1991 req->lpszDirectory = heap_strdupW(lpszDirectory);
1993 r = res_to_le(INTERNET_AsyncCall(&workRequest));
1995 else
1997 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
2000 lend:
2001 WININET_Release( &lpwfs->hdr );
2003 return r;
2006 /***********************************************************************
2007 * FTP_FtpRemoveDirectoryW (Internal)
2009 * Remove a directory on the ftp server
2011 * RETURNS
2012 * TRUE on success
2013 * FALSE on failure
2016 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
2018 INT nResCode;
2019 BOOL bSuccess = FALSE;
2020 appinfo_t *hIC = NULL;
2022 TRACE("\n");
2024 /* Clear any error information */
2025 INTERNET_SetLastError(0);
2027 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2028 goto lend;
2030 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2031 if (nResCode)
2033 if (nResCode == 250)
2034 bSuccess = TRUE;
2035 else
2036 FTP_SetResponseError(nResCode);
2039 lend:
2040 hIC = lpwfs->lpAppInfo;
2041 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2043 INTERNET_ASYNC_RESULT iar;
2045 iar.dwResult = (DWORD)bSuccess;
2046 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2047 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2048 &iar, sizeof(INTERNET_ASYNC_RESULT));
2051 return bSuccess;
2055 /***********************************************************************
2056 * FtpRenameFileA (WININET.@)
2058 * Rename a file on the ftp server
2060 * RETURNS
2061 * TRUE on success
2062 * FALSE on failure
2065 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2067 LPWSTR lpwzSrc;
2068 LPWSTR lpwzDest;
2069 BOOL ret;
2071 lpwzSrc = heap_strdupAtoW(lpszSrc);
2072 lpwzDest = heap_strdupAtoW(lpszDest);
2073 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2074 heap_free(lpwzSrc);
2075 heap_free(lpwzDest);
2076 return ret;
2079 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
2081 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
2082 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
2084 TRACE("%p\n", lpwfs);
2086 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
2087 heap_free(req->lpszSrcFile);
2088 heap_free(req->lpszDestFile);
2091 /***********************************************************************
2092 * FtpRenameFileW (WININET.@)
2094 * Rename a file on the ftp server
2096 * RETURNS
2097 * TRUE on success
2098 * FALSE on failure
2101 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2103 ftp_session_t *lpwfs;
2104 appinfo_t *hIC = NULL;
2105 BOOL r = FALSE;
2107 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
2108 if (!lpwfs)
2110 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2111 return FALSE;
2114 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2116 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2117 goto lend;
2120 if (lpwfs->download_in_progress != NULL)
2122 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2123 goto lend;
2126 if (!lpszSrc || !lpszDest)
2128 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2129 goto lend;
2132 hIC = lpwfs->lpAppInfo;
2133 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2135 WORKREQUEST workRequest;
2136 struct WORKREQ_FTPRENAMEFILEW *req;
2138 workRequest.asyncproc = AsyncFtpRenameFileProc;
2139 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
2140 req = &workRequest.u.FtpRenameFileW;
2141 req->lpszSrcFile = heap_strdupW(lpszSrc);
2142 req->lpszDestFile = heap_strdupW(lpszDest);
2144 r = res_to_le(INTERNET_AsyncCall(&workRequest));
2146 else
2148 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2151 lend:
2152 WININET_Release( &lpwfs->hdr );
2154 return r;
2157 /***********************************************************************
2158 * FTP_FtpRenameFileW (Internal)
2160 * Rename a file on the ftp server
2162 * RETURNS
2163 * TRUE on success
2164 * FALSE on failure
2167 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2169 INT nResCode;
2170 BOOL bSuccess = FALSE;
2171 appinfo_t *hIC = NULL;
2173 TRACE("\n");
2175 /* Clear any error information */
2176 INTERNET_SetLastError(0);
2178 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2179 goto lend;
2181 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2182 if (nResCode == 350)
2184 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2185 goto lend;
2187 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2190 if (nResCode == 250)
2191 bSuccess = TRUE;
2192 else
2193 FTP_SetResponseError(nResCode);
2195 lend:
2196 hIC = lpwfs->lpAppInfo;
2197 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2199 INTERNET_ASYNC_RESULT iar;
2201 iar.dwResult = (DWORD)bSuccess;
2202 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2203 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2204 &iar, sizeof(INTERNET_ASYNC_RESULT));
2207 return bSuccess;
2210 /***********************************************************************
2211 * FtpCommandA (WININET.@)
2213 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2214 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2216 BOOL r;
2217 WCHAR *cmdW;
2219 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2220 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2222 if (fExpectResponse)
2224 FIXME("data connection not supported\n");
2225 return FALSE;
2228 if (!lpszCommand || !lpszCommand[0])
2230 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2231 return FALSE;
2234 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2236 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2237 return FALSE;
2240 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2242 heap_free(cmdW);
2243 return r;
2246 /***********************************************************************
2247 * FtpCommandW (WININET.@)
2249 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2250 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2252 BOOL r = FALSE;
2253 ftp_session_t *lpwfs;
2254 LPSTR cmd = NULL;
2255 DWORD len, nBytesSent= 0;
2256 INT nResCode, nRC = 0;
2258 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2259 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2261 if (!lpszCommand || !lpszCommand[0])
2263 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2264 return FALSE;
2267 if (fExpectResponse)
2269 FIXME("data connection not supported\n");
2270 return FALSE;
2273 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
2274 if (!lpwfs)
2276 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2277 return FALSE;
2280 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2282 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2283 goto lend;
2286 if (lpwfs->download_in_progress != NULL)
2288 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2289 goto lend;
2292 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2293 if ((cmd = heap_alloc(len)))
2294 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2295 else
2297 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2298 goto lend;
2301 strcat(cmd, szCRLF);
2302 len--;
2304 TRACE("Sending (%s) len(%d)\n", cmd, len);
2305 while ((nBytesSent < len) && (nRC != -1))
2307 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2308 if (nRC != -1)
2310 nBytesSent += nRC;
2311 TRACE("Sent %d bytes\n", nRC);
2315 if (nBytesSent)
2317 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2318 if (nResCode > 0 && nResCode < 400)
2319 r = TRUE;
2320 else
2321 FTP_SetResponseError(nResCode);
2324 lend:
2325 WININET_Release( &lpwfs->hdr );
2326 heap_free( cmd );
2327 return r;
2331 /***********************************************************************
2332 * FTPSESSION_Destroy (internal)
2334 * Deallocate session handle
2336 static void FTPSESSION_Destroy(object_header_t *hdr)
2338 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2340 TRACE("\n");
2342 WININET_Release(&lpwfs->lpAppInfo->hdr);
2344 heap_free(lpwfs->lpszPassword);
2345 heap_free(lpwfs->lpszUserName);
2346 heap_free(lpwfs->servername);
2349 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2351 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2353 TRACE("\n");
2355 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2356 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2358 if (lpwfs->download_in_progress != NULL)
2359 lpwfs->download_in_progress->session_deleted = TRUE;
2361 if (lpwfs->sndSocket != -1)
2362 closesocket(lpwfs->sndSocket);
2364 if (lpwfs->lstnSocket != -1)
2365 closesocket(lpwfs->lstnSocket);
2367 if (lpwfs->pasvSocket != -1)
2368 closesocket(lpwfs->pasvSocket);
2370 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2371 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2374 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2376 switch(option) {
2377 case INTERNET_OPTION_HANDLE_TYPE:
2378 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2380 if (*size < sizeof(ULONG))
2381 return ERROR_INSUFFICIENT_BUFFER;
2383 *size = sizeof(DWORD);
2384 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2385 return ERROR_SUCCESS;
2388 return INET_QueryOption(hdr, option, buffer, size, unicode);
2391 static const object_vtbl_t FTPSESSIONVtbl = {
2392 FTPSESSION_Destroy,
2393 FTPSESSION_CloseConnection,
2394 FTPSESSION_QueryOption,
2395 NULL,
2396 NULL,
2397 NULL,
2398 NULL,
2399 NULL,
2400 NULL
2404 /***********************************************************************
2405 * FTP_Connect (internal)
2407 * Connect to a ftp server
2409 * RETURNS
2410 * HINTERNET a session handle on success
2411 * NULL on failure
2413 * NOTES:
2415 * Windows uses 'anonymous' as the username, when given a NULL username
2416 * and a NULL password. The password is first looked up in:
2418 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2420 * If this entry is not present it uses the current username as the password.
2424 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2425 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2426 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2427 DWORD dwInternalFlags)
2429 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2430 'M','i','c','r','o','s','o','f','t','\\',
2431 'W','i','n','d','o','w','s','\\',
2432 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2433 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2434 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2435 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2436 static const WCHAR szEmpty[] = {'\0'};
2437 struct sockaddr_in socketAddr;
2438 INT nsocket = -1;
2439 UINT sock_namelen;
2440 BOOL bSuccess = FALSE;
2441 ftp_session_t *lpwfs = NULL;
2442 char szaddr[INET_ADDRSTRLEN];
2444 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2445 hIC, debugstr_w(lpszServerName),
2446 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2448 assert( hIC->hdr.htype == WH_HINIT );
2450 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2452 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2453 return NULL;
2456 lpwfs = alloc_object(&hIC->hdr, &FTPSESSIONVtbl, sizeof(ftp_session_t));
2457 if (NULL == lpwfs)
2459 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2460 return NULL;
2463 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2464 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2465 else
2466 lpwfs->serverport = nServerPort;
2468 lpwfs->hdr.htype = WH_HFTPSESSION;
2469 lpwfs->hdr.dwFlags = dwFlags;
2470 lpwfs->hdr.dwContext = dwContext;
2471 lpwfs->hdr.dwInternalFlags |= dwInternalFlags;
2472 lpwfs->download_in_progress = NULL;
2473 lpwfs->sndSocket = -1;
2474 lpwfs->lstnSocket = -1;
2475 lpwfs->pasvSocket = -1;
2477 WININET_AddRef( &hIC->hdr );
2478 lpwfs->lpAppInfo = hIC;
2479 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2481 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
2482 if(strchrW(hIC->proxy, ' '))
2483 FIXME("Several proxies not implemented.\n");
2484 if(hIC->proxyBypass)
2485 FIXME("Proxy bypass is ignored.\n");
2487 if (!lpszUserName || !strlenW(lpszUserName)) {
2488 HKEY key;
2489 WCHAR szPassword[MAX_PATH];
2490 DWORD len = sizeof(szPassword);
2492 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2494 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2495 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2496 /* Nothing in the registry, get the username and use that as the password */
2497 if (!GetUserNameW(szPassword, &len)) {
2498 /* Should never get here, but use an empty password as failsafe */
2499 strcpyW(szPassword, szEmpty);
2502 RegCloseKey(key);
2504 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2505 lpwfs->lpszPassword = heap_strdupW(szPassword);
2507 else {
2508 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2509 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2511 lpwfs->servername = heap_strdupW(lpszServerName);
2513 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2514 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2516 INTERNET_ASYNC_RESULT iar;
2518 iar.dwResult = (DWORD_PTR)lpwfs->hdr.hInternet;
2519 iar.dwError = ERROR_SUCCESS;
2521 SendAsyncCallback(&hIC->hdr, dwContext,
2522 INTERNET_STATUS_HANDLE_CREATED, &iar,
2523 sizeof(INTERNET_ASYNC_RESULT));
2526 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2527 (LPWSTR) lpszServerName, (strlenW(lpszServerName)+1) * sizeof(WCHAR));
2529 sock_namelen = sizeof(socketAddr);
2530 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen))
2532 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2533 goto lerror;
2536 if (socketAddr.sin_family != AF_INET)
2538 WARN("unsupported address family %d\n", socketAddr.sin_family);
2539 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2540 goto lerror;
2543 inet_ntop(socketAddr.sin_family, &socketAddr.sin_addr, szaddr, sizeof(szaddr));
2544 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2545 szaddr, strlen(szaddr)+1);
2547 nsocket = socket(AF_INET,SOCK_STREAM,0);
2548 if (nsocket == -1)
2550 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2551 goto lerror;
2554 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2555 szaddr, strlen(szaddr)+1);
2557 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2559 ERR("Unable to connect (%s)\n", strerror(errno));
2560 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2561 closesocket(nsocket);
2563 else
2565 TRACE("Connected to server\n");
2566 lpwfs->sndSocket = nsocket;
2567 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2568 szaddr, strlen(szaddr)+1);
2570 sock_namelen = sizeof(lpwfs->socketAddress);
2571 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2573 if (FTP_ConnectToHost(lpwfs))
2575 TRACE("Successfully logged into server\n");
2576 bSuccess = TRUE;
2580 lerror:
2581 if (!bSuccess)
2583 if(lpwfs)
2584 WININET_Release( &lpwfs->hdr );
2585 return NULL;
2588 return lpwfs->hdr.hInternet;
2592 /***********************************************************************
2593 * FTP_ConnectToHost (internal)
2595 * Connect to a ftp server
2597 * RETURNS
2598 * TRUE on success
2599 * NULL on failure
2602 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2604 INT nResCode;
2605 BOOL bSuccess = FALSE;
2607 TRACE("\n");
2608 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2610 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2611 goto lend;
2613 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2614 if (nResCode)
2616 /* Login successful... */
2617 if (nResCode == 230)
2618 bSuccess = TRUE;
2619 /* User name okay, need password... */
2620 else if (nResCode == 331)
2621 bSuccess = FTP_SendPassword(lpwfs);
2622 /* Need account for login... */
2623 else if (nResCode == 332)
2624 bSuccess = FTP_SendAccount(lpwfs);
2625 else
2626 FTP_SetResponseError(nResCode);
2629 TRACE("Returning %d\n", bSuccess);
2630 lend:
2631 return bSuccess;
2635 /***********************************************************************
2636 * FTP_SendCommandA (internal)
2638 * Send command to server
2640 * RETURNS
2641 * TRUE on success
2642 * NULL on failure
2645 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2646 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2648 DWORD len;
2649 CHAR *buf;
2650 DWORD nBytesSent = 0;
2651 int nRC = 0;
2652 DWORD dwParamLen;
2654 TRACE("%d: (%s) %d\n", ftpCmd, debugstr_a(lpszParam), nSocket);
2656 if (lpfnStatusCB)
2658 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2661 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2662 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2663 if (NULL == (buf = heap_alloc(len+1)))
2665 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2666 return FALSE;
2668 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2669 dwParamLen ? lpszParam : "", szCRLF);
2671 TRACE("Sending (%s) len(%d)\n", buf, len);
2672 while((nBytesSent < len) && (nRC != -1))
2674 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2675 nBytesSent += nRC;
2677 heap_free(buf);
2679 if (lpfnStatusCB)
2681 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2682 &nBytesSent, sizeof(DWORD));
2685 TRACE("Sent %d bytes\n", nBytesSent);
2686 return (nRC != -1);
2689 /***********************************************************************
2690 * FTP_SendCommand (internal)
2692 * Send command to server
2694 * RETURNS
2695 * TRUE on success
2696 * NULL on failure
2699 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2700 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2702 BOOL ret;
2703 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2704 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2705 heap_free(lpszParamA);
2706 return ret;
2709 /***********************************************************************
2710 * FTP_ReceiveResponse (internal)
2712 * Receive response from server
2714 * RETURNS
2715 * Reply code on success
2716 * 0 on failure
2719 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2721 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2722 DWORD nRecv;
2723 INT rc = 0;
2724 char firstprefix[5];
2725 BOOL multiline = FALSE;
2727 TRACE("socket(%d)\n", lpwfs->sndSocket);
2729 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2731 while(1)
2733 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2734 goto lerror;
2736 if (nRecv >= 3)
2738 if(!multiline)
2740 if(lpszResponse[3] != '-')
2741 break;
2742 else
2743 { /* Start of multiline response. Loop until we get "nnn " */
2744 multiline = TRUE;
2745 memcpy(firstprefix, lpszResponse, 3);
2746 firstprefix[3] = ' ';
2747 firstprefix[4] = '\0';
2750 else
2752 if(!memcmp(firstprefix, lpszResponse, 4))
2753 break;
2758 if (nRecv >= 3)
2760 rc = atoi(lpszResponse);
2762 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2763 &nRecv, sizeof(DWORD));
2766 lerror:
2767 TRACE("return %d\n", rc);
2768 return rc;
2772 /***********************************************************************
2773 * FTP_SendPassword (internal)
2775 * Send password to ftp server
2777 * RETURNS
2778 * TRUE on success
2779 * NULL on failure
2782 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2784 INT nResCode;
2785 BOOL bSuccess = FALSE;
2787 TRACE("\n");
2788 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2789 goto lend;
2791 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2792 if (nResCode)
2794 TRACE("Received reply code %d\n", nResCode);
2795 /* Login successful... */
2796 if (nResCode == 230)
2797 bSuccess = TRUE;
2798 /* Command not implemented, superfluous at the server site... */
2799 /* Need account for login... */
2800 else if (nResCode == 332)
2801 bSuccess = FTP_SendAccount(lpwfs);
2802 else
2803 FTP_SetResponseError(nResCode);
2806 lend:
2807 TRACE("Returning %d\n", bSuccess);
2808 return bSuccess;
2812 /***********************************************************************
2813 * FTP_SendAccount (internal)
2817 * RETURNS
2818 * TRUE on success
2819 * FALSE on failure
2822 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2824 INT nResCode;
2825 BOOL bSuccess = FALSE;
2827 TRACE("\n");
2828 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2829 goto lend;
2831 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2832 if (nResCode)
2833 bSuccess = TRUE;
2834 else
2835 FTP_SetResponseError(nResCode);
2837 lend:
2838 return bSuccess;
2842 /***********************************************************************
2843 * FTP_SendStore (internal)
2845 * Send request to upload file to ftp server
2847 * RETURNS
2848 * TRUE on success
2849 * FALSE on failure
2852 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2854 INT nResCode;
2855 BOOL bSuccess = FALSE;
2857 TRACE("\n");
2858 if (!FTP_InitListenSocket(lpwfs))
2859 goto lend;
2861 if (!FTP_SendType(lpwfs, dwType))
2862 goto lend;
2864 if (!FTP_SendPortOrPasv(lpwfs))
2865 goto lend;
2867 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2868 goto lend;
2869 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2870 if (nResCode)
2872 if (nResCode == 150 || nResCode == 125)
2873 bSuccess = TRUE;
2874 else
2875 FTP_SetResponseError(nResCode);
2878 lend:
2879 if (!bSuccess && lpwfs->lstnSocket != -1)
2881 closesocket(lpwfs->lstnSocket);
2882 lpwfs->lstnSocket = -1;
2885 return bSuccess;
2889 /***********************************************************************
2890 * FTP_InitListenSocket (internal)
2892 * Create a socket to listen for server response
2894 * RETURNS
2895 * TRUE on success
2896 * FALSE on failure
2899 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2901 BOOL bSuccess = FALSE;
2902 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2904 TRACE("\n");
2906 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2907 if (lpwfs->lstnSocket == -1)
2909 TRACE("Unable to create listening socket\n");
2910 goto lend;
2913 /* We obtain our ip addr from the name of the command channel socket */
2914 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2916 /* and get the system to assign us a port */
2917 lpwfs->lstnSocketAddress.sin_port = htons(0);
2919 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2921 TRACE("Unable to bind socket\n");
2922 goto lend;
2925 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2927 TRACE("listen failed\n");
2928 goto lend;
2931 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2932 bSuccess = TRUE;
2934 lend:
2935 if (!bSuccess && lpwfs->lstnSocket != -1)
2937 closesocket(lpwfs->lstnSocket);
2938 lpwfs->lstnSocket = -1;
2941 return bSuccess;
2945 /***********************************************************************
2946 * FTP_SendType (internal)
2948 * Tell server type of data being transferred
2950 * RETURNS
2951 * TRUE on success
2952 * FALSE on failure
2954 * W98SE doesn't cache the type that's currently set
2955 * (i.e. it sends it always),
2956 * so we probably don't want to do that either.
2958 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2960 INT nResCode;
2961 WCHAR type[] = { 'I','\0' };
2962 BOOL bSuccess = FALSE;
2964 TRACE("\n");
2965 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2966 type[0] = 'A';
2968 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2969 goto lend;
2971 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2972 if (nResCode)
2974 if (nResCode == 2)
2975 bSuccess = TRUE;
2976 else
2977 FTP_SetResponseError(nResCode);
2980 lend:
2981 return bSuccess;
2985 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2986 /***********************************************************************
2987 * FTP_GetFileSize (internal)
2989 * Retrieves from the server the size of the given file
2991 * RETURNS
2992 * TRUE on success
2993 * FALSE on failure
2996 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2998 INT nResCode;
2999 BOOL bSuccess = FALSE;
3001 TRACE("\n");
3003 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
3004 goto lend;
3006 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3007 if (nResCode)
3009 if (nResCode == 213) {
3010 /* Now parses the output to get the actual file size */
3011 int i;
3012 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3014 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3015 if (lpszResponseBuffer[i] == '\0') return FALSE;
3016 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3018 bSuccess = TRUE;
3019 } else {
3020 FTP_SetResponseError(nResCode);
3024 lend:
3025 return bSuccess;
3027 #endif
3030 /***********************************************************************
3031 * FTP_SendPort (internal)
3033 * Tell server which port to use
3035 * RETURNS
3036 * TRUE on success
3037 * FALSE on failure
3040 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3042 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3043 INT nResCode;
3044 WCHAR szIPAddress[64];
3045 BOOL bSuccess = FALSE;
3046 TRACE("\n");
3048 sprintfW(szIPAddress, szIPFormat,
3049 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
3050 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
3051 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
3052 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
3053 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3054 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3056 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3057 goto lend;
3059 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3060 if (nResCode)
3062 if (nResCode == 200)
3063 bSuccess = TRUE;
3064 else
3065 FTP_SetResponseError(nResCode);
3068 lend:
3069 return bSuccess;
3073 /***********************************************************************
3074 * FTP_DoPassive (internal)
3076 * Tell server that we want to do passive transfers
3077 * and connect data socket
3079 * RETURNS
3080 * TRUE on success
3081 * FALSE on failure
3084 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3086 INT nResCode;
3087 BOOL bSuccess = FALSE;
3089 TRACE("\n");
3090 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3091 goto lend;
3093 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3094 if (nResCode)
3096 if (nResCode == 227)
3098 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3099 LPSTR p;
3100 int f[6];
3101 int i;
3102 char *pAddr, *pPort;
3103 INT nsocket = -1;
3104 struct sockaddr_in dataSocketAddress;
3106 p = lpszResponseBuffer+4; /* skip status code */
3107 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3109 if (*p == '\0')
3111 ERR("no address found in response, aborting\n");
3112 goto lend;
3115 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3116 &f[4], &f[5]) != 6)
3118 ERR("unknown response address format '%s', aborting\n", p);
3119 goto lend;
3121 for (i=0; i < 6; i++)
3122 f[i] = f[i] & 0xff;
3124 dataSocketAddress = lpwfs->socketAddress;
3125 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3126 pPort = (char *)&(dataSocketAddress.sin_port);
3127 pAddr[0] = f[0];
3128 pAddr[1] = f[1];
3129 pAddr[2] = f[2];
3130 pAddr[3] = f[3];
3131 pPort[0] = f[4];
3132 pPort[1] = f[5];
3134 nsocket = socket(AF_INET,SOCK_STREAM,0);
3135 if (nsocket == -1)
3136 goto lend;
3138 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3140 ERR("can't connect passive FTP data port.\n");
3141 closesocket(nsocket);
3142 goto lend;
3144 lpwfs->pasvSocket = nsocket;
3145 bSuccess = TRUE;
3147 else
3148 FTP_SetResponseError(nResCode);
3151 lend:
3152 return bSuccess;
3156 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3158 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3160 if (!FTP_DoPassive(lpwfs))
3161 return FALSE;
3163 else
3165 if (!FTP_SendPort(lpwfs))
3166 return FALSE;
3168 return TRUE;
3172 /***********************************************************************
3173 * FTP_GetDataSocket (internal)
3175 * Either accepts an incoming data socket connection from the server
3176 * or just returns the already opened socket after a PASV command
3177 * in case of passive FTP.
3180 * RETURNS
3181 * TRUE on success
3182 * FALSE on failure
3185 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3187 struct sockaddr_in saddr;
3188 socklen_t addrlen = sizeof(struct sockaddr);
3190 TRACE("\n");
3191 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3193 *nDataSocket = lpwfs->pasvSocket;
3194 lpwfs->pasvSocket = -1;
3196 else
3198 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3199 closesocket(lpwfs->lstnSocket);
3200 lpwfs->lstnSocket = -1;
3202 return *nDataSocket != -1;
3206 /***********************************************************************
3207 * FTP_SendData (internal)
3209 * Send data to the server
3211 * RETURNS
3212 * TRUE on success
3213 * FALSE on failure
3216 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3218 BY_HANDLE_FILE_INFORMATION fi;
3219 DWORD nBytesRead = 0;
3220 DWORD nBytesSent = 0;
3221 DWORD nTotalSent = 0;
3222 DWORD nBytesToSend, nLen;
3223 int nRC = 1;
3224 time_t s_long_time, e_long_time;
3225 LONG nSeconds;
3226 CHAR *lpszBuffer;
3228 TRACE("\n");
3229 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3231 /* Get the size of the file. */
3232 GetFileInformationByHandle(hFile, &fi);
3233 time(&s_long_time);
3237 nBytesToSend = nBytesRead - nBytesSent;
3239 if (nBytesToSend <= 0)
3241 /* Read data from file. */
3242 nBytesSent = 0;
3243 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3244 ERR("Failed reading from file\n");
3246 if (nBytesRead > 0)
3247 nBytesToSend = nBytesRead;
3248 else
3249 break;
3252 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3253 DATA_PACKET_SIZE : nBytesToSend;
3254 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3256 if (nRC != -1)
3258 nBytesSent += nRC;
3259 nTotalSent += nRC;
3262 /* Do some computation to display the status. */
3263 time(&e_long_time);
3264 nSeconds = e_long_time - s_long_time;
3265 if( nSeconds / 60 > 0 )
3267 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3268 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3269 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3271 else
3273 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3274 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3275 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3277 } while (nRC != -1);
3279 TRACE("file transfer complete!\n");
3281 heap_free(lpszBuffer);
3282 return nTotalSent;
3286 /***********************************************************************
3287 * FTP_SendRetrieve (internal)
3289 * Send request to retrieve a file
3291 * RETURNS
3292 * Number of bytes to be received on success
3293 * 0 on failure
3296 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3298 INT nResCode;
3299 BOOL ret;
3301 TRACE("\n");
3302 if (!(ret = FTP_InitListenSocket(lpwfs)))
3303 goto lend;
3305 if (!(ret = FTP_SendType(lpwfs, dwType)))
3306 goto lend;
3308 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3309 goto lend;
3311 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3312 goto lend;
3314 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3315 if ((nResCode != 125) && (nResCode != 150)) {
3316 /* That means that we got an error getting the file. */
3317 FTP_SetResponseError(nResCode);
3318 ret = FALSE;
3321 lend:
3322 if (!ret && lpwfs->lstnSocket != -1)
3324 closesocket(lpwfs->lstnSocket);
3325 lpwfs->lstnSocket = -1;
3328 return ret;
3332 /***********************************************************************
3333 * FTP_RetrieveData (internal)
3335 * Retrieve data from server
3337 * RETURNS
3338 * TRUE on success
3339 * FALSE on failure
3342 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3344 DWORD nBytesWritten;
3345 INT nRC = 0;
3346 CHAR *lpszBuffer;
3348 TRACE("\n");
3350 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3351 if (NULL == lpszBuffer)
3353 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3354 return FALSE;
3357 while (nRC != -1)
3359 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3360 if (nRC != -1)
3362 /* other side closed socket. */
3363 if (nRC == 0)
3364 goto recv_end;
3365 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3369 TRACE("Data transfer complete\n");
3371 recv_end:
3372 heap_free(lpszBuffer);
3373 return (nRC != -1);
3376 /***********************************************************************
3377 * FTPFINDNEXT_Destroy (internal)
3379 * Deallocate session handle
3381 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3383 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3384 DWORD i;
3386 TRACE("\n");
3388 WININET_Release(&lpwfn->lpFtpSession->hdr);
3390 for (i = 0; i < lpwfn->size; i++)
3392 heap_free(lpwfn->lpafp[i].lpszName);
3394 heap_free(lpwfn->lpafp);
3397 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3399 WIN32_FIND_DATAW *find_data = data;
3400 DWORD res = ERROR_SUCCESS;
3402 TRACE("index(%d) size(%d)\n", find->index, find->size);
3404 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3406 if (find->index < find->size) {
3407 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3408 find->index++;
3410 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3411 }else {
3412 res = ERROR_NO_MORE_FILES;
3415 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3417 INTERNET_ASYNC_RESULT iar;
3419 iar.dwResult = (res == ERROR_SUCCESS);
3420 iar.dwError = res;
3422 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3423 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3424 sizeof(INTERNET_ASYNC_RESULT));
3427 return res;
3430 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3432 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3434 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3437 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3439 switch(option) {
3440 case INTERNET_OPTION_HANDLE_TYPE:
3441 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3443 if (*size < sizeof(ULONG))
3444 return ERROR_INSUFFICIENT_BUFFER;
3446 *size = sizeof(DWORD);
3447 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3448 return ERROR_SUCCESS;
3451 return INET_QueryOption(hdr, option, buffer, size, unicode);
3454 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3456 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3458 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3460 WORKREQUEST workRequest;
3461 struct WORKREQ_FTPFINDNEXTW *req;
3463 workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3464 workRequest.hdr = WININET_AddRef( &find->hdr );
3465 req = &workRequest.u.FtpFindNextW;
3466 req->lpFindFileData = data;
3468 INTERNET_AsyncCall(&workRequest);
3470 return ERROR_SUCCESS;
3473 return FTPFINDNEXT_FindNextFileProc(find, data);
3476 static const object_vtbl_t FTPFINDNEXTVtbl = {
3477 FTPFINDNEXT_Destroy,
3478 NULL,
3479 FTPFINDNEXT_QueryOption,
3480 NULL,
3481 NULL,
3482 NULL,
3483 NULL,
3484 NULL,
3485 NULL,
3486 FTPFINDNEXT_FindNextFileW
3489 /***********************************************************************
3490 * FTP_ReceiveFileList (internal)
3492 * Read file list from server
3494 * RETURNS
3495 * Handle to file list on success
3496 * NULL on failure
3499 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3500 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3502 DWORD dwSize = 0;
3503 LPFILEPROPERTIESW lpafp = NULL;
3504 LPWININETFTPFINDNEXTW lpwfn = NULL;
3506 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3508 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3510 if(lpFindFileData)
3511 FTP_ConvertFileProp(lpafp, lpFindFileData);
3513 lpwfn = alloc_object(&lpwfs->hdr, &FTPFINDNEXTVtbl, sizeof(WININETFTPFINDNEXTW));
3514 if (lpwfn)
3516 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3517 lpwfn->hdr.dwContext = dwContext;
3518 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3519 lpwfn->size = dwSize;
3520 lpwfn->lpafp = lpafp;
3522 WININET_AddRef( &lpwfs->hdr );
3523 lpwfn->lpFtpSession = lpwfs;
3524 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3528 TRACE("Matched %d files\n", dwSize);
3529 return lpwfn ? lpwfn->hdr.hInternet : NULL;
3533 /***********************************************************************
3534 * FTP_ConvertFileProp (internal)
3536 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3538 * RETURNS
3539 * TRUE on success
3540 * FALSE on failure
3543 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3545 BOOL bSuccess = FALSE;
3547 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3549 if (lpafp)
3551 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3552 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3553 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3555 /* Not all fields are filled in */
3556 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3557 lpFindFileData->nFileSizeLow = lpafp->nSize;
3559 if (lpafp->bIsDirectory)
3560 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3562 if (lpafp->lpszName)
3563 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3565 bSuccess = TRUE;
3568 return bSuccess;
3571 /***********************************************************************
3572 * FTP_ParseNextFile (internal)
3574 * Parse the next line in file listing
3576 * RETURNS
3577 * TRUE on success
3578 * FALSE on failure
3580 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3582 static const char szSpace[] = " \t";
3583 DWORD nBufLen;
3584 char *pszLine;
3585 char *pszToken;
3586 char *pszTmp;
3587 BOOL found = FALSE;
3588 int i;
3590 lpfp->lpszName = NULL;
3591 do {
3592 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3593 return FALSE;
3595 pszToken = strtok(pszLine, szSpace);
3596 /* ls format
3597 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3599 * For instance:
3600 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3602 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3603 if(!FTP_ParsePermission(pszToken, lpfp))
3604 lpfp->bIsDirectory = FALSE;
3605 for(i=0; i<=3; i++) {
3606 if(!(pszToken = strtok(NULL, szSpace)))
3607 break;
3609 if(!pszToken) continue;
3610 if(lpfp->bIsDirectory) {
3611 TRACE("Is directory\n");
3612 lpfp->nSize = 0;
3614 else {
3615 TRACE("Size: %s\n", pszToken);
3616 lpfp->nSize = atol(pszToken);
3619 lpfp->tmLastModified.wSecond = 0;
3620 lpfp->tmLastModified.wMinute = 0;
3621 lpfp->tmLastModified.wHour = 0;
3622 lpfp->tmLastModified.wDay = 0;
3623 lpfp->tmLastModified.wMonth = 0;
3624 lpfp->tmLastModified.wYear = 0;
3626 /* Determine month */
3627 pszToken = strtok(NULL, szSpace);
3628 if(!pszToken) continue;
3629 if(strlen(pszToken) >= 3) {
3630 pszToken[3] = 0;
3631 if((pszTmp = StrStrIA(szMonths, pszToken)))
3632 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3634 /* Determine day */
3635 pszToken = strtok(NULL, szSpace);
3636 if(!pszToken) continue;
3637 lpfp->tmLastModified.wDay = atoi(pszToken);
3638 /* Determine time or year */
3639 pszToken = strtok(NULL, szSpace);
3640 if(!pszToken) continue;
3641 if((pszTmp = strchr(pszToken, ':'))) {
3642 SYSTEMTIME curr_time;
3643 *pszTmp = 0;
3644 pszTmp++;
3645 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3646 lpfp->tmLastModified.wHour = atoi(pszToken);
3647 GetLocalTime( &curr_time );
3648 lpfp->tmLastModified.wYear = curr_time.wYear;
3650 else {
3651 lpfp->tmLastModified.wYear = atoi(pszToken);
3652 lpfp->tmLastModified.wHour = 12;
3654 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3655 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3656 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3658 pszToken = strtok(NULL, szSpace);
3659 if(!pszToken) continue;
3660 lpfp->lpszName = heap_strdupAtoW(pszToken);
3661 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3663 /* NT way of parsing ... :
3665 07-13-03 08:55PM <DIR> sakpatch
3666 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3668 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3669 int mon, mday, year, hour, min;
3670 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3672 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3673 lpfp->tmLastModified.wDay = mday;
3674 lpfp->tmLastModified.wMonth = mon;
3675 lpfp->tmLastModified.wYear = year;
3677 /* Hacky and bad Y2K protection :-) */
3678 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3680 pszToken = strtok(NULL, szSpace);
3681 if(!pszToken) continue;
3682 sscanf(pszToken, "%d:%d", &hour, &min);
3683 lpfp->tmLastModified.wHour = hour;
3684 lpfp->tmLastModified.wMinute = min;
3685 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3686 lpfp->tmLastModified.wHour += 12;
3688 lpfp->tmLastModified.wSecond = 0;
3690 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3691 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3692 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3694 pszToken = strtok(NULL, szSpace);
3695 if(!pszToken) continue;
3696 if(!strcasecmp(pszToken, "<DIR>")) {
3697 lpfp->bIsDirectory = TRUE;
3698 lpfp->nSize = 0;
3699 TRACE("Is directory\n");
3701 else {
3702 lpfp->bIsDirectory = FALSE;
3703 lpfp->nSize = atol(pszToken);
3704 TRACE("Size: %d\n", lpfp->nSize);
3707 pszToken = strtok(NULL, szSpace);
3708 if(!pszToken) continue;
3709 lpfp->lpszName = heap_strdupAtoW(pszToken);
3710 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3712 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3713 else if(pszToken[0] == '+') {
3714 FIXME("EPLF Format not implemented\n");
3717 if(lpfp->lpszName) {
3718 if((lpszSearchFile == NULL) ||
3719 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3720 found = TRUE;
3721 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3723 else {
3724 heap_free(lpfp->lpszName);
3725 lpfp->lpszName = NULL;
3728 } while(!found);
3729 return TRUE;
3732 /***********************************************************************
3733 * FTP_ParseDirectory (internal)
3735 * Parse string of directory information
3737 * RETURNS
3738 * TRUE on success
3739 * FALSE on failure
3741 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3742 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3744 BOOL bSuccess = TRUE;
3745 INT sizeFilePropArray = 500;/*20; */
3746 INT indexFilePropArray = -1;
3748 TRACE("\n");
3750 /* Allocate initial file properties array */
3751 *lpafp = heap_alloc_zero(sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3752 if (!*lpafp)
3753 return FALSE;
3755 do {
3756 if (indexFilePropArray+1 >= sizeFilePropArray)
3758 LPFILEPROPERTIESW tmpafp;
3760 sizeFilePropArray *= 2;
3761 tmpafp = heap_realloc_zero(*lpafp, sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3762 if (NULL == tmpafp)
3764 bSuccess = FALSE;
3765 break;
3768 *lpafp = tmpafp;
3770 indexFilePropArray++;
3771 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3773 if (bSuccess && indexFilePropArray)
3775 if (indexFilePropArray < sizeFilePropArray - 1)
3777 LPFILEPROPERTIESW tmpafp;
3779 tmpafp = heap_realloc(*lpafp, sizeof(FILEPROPERTIESW)*indexFilePropArray);
3780 if (NULL != tmpafp)
3781 *lpafp = tmpafp;
3783 *dwfp = indexFilePropArray;
3785 else
3787 heap_free(*lpafp);
3788 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3789 bSuccess = FALSE;
3792 return bSuccess;
3796 /***********************************************************************
3797 * FTP_ParsePermission (internal)
3799 * Parse permission string of directory information
3801 * RETURNS
3802 * TRUE on success
3803 * FALSE on failure
3806 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3808 BOOL bSuccess = TRUE;
3809 unsigned short nPermission = 0;
3810 INT nPos = 1;
3811 INT nLast = 9;
3813 TRACE("\n");
3814 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3816 bSuccess = FALSE;
3817 return bSuccess;
3820 lpfp->bIsDirectory = (*lpszPermission == 'd');
3823 switch (nPos)
3825 case 1:
3826 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3827 break;
3828 case 2:
3829 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3830 break;
3831 case 3:
3832 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3833 break;
3834 case 4:
3835 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3836 break;
3837 case 5:
3838 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3839 break;
3840 case 6:
3841 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3842 break;
3843 case 7:
3844 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3845 break;
3846 case 8:
3847 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3848 break;
3849 case 9:
3850 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3851 break;
3853 nPos++;
3854 }while (nPos <= nLast);
3856 lpfp->permissions = nPermission;
3857 return bSuccess;
3861 /***********************************************************************
3862 * FTP_SetResponseError (internal)
3864 * Set the appropriate error code for a given response from the server
3866 * RETURNS
3869 static DWORD FTP_SetResponseError(DWORD dwResponse)
3871 DWORD dwCode = 0;
3873 switch(dwResponse)
3875 case 425: /* Cannot open data connection. */
3876 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3877 break;
3879 case 426: /* Connection closed, transer aborted. */
3880 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3881 break;
3883 case 530: /* Not logged in. Login incorrect. */
3884 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3885 break;
3887 case 421: /* Service not available - Server may be shutting down. */
3888 case 450: /* File action not taken. File may be busy. */
3889 case 451: /* Action aborted. Server error. */
3890 case 452: /* Action not taken. Insufficient storage space on server. */
3891 case 500: /* Syntax error. Command unrecognized. */
3892 case 501: /* Syntax error. Error in parameters or arguments. */
3893 case 502: /* Command not implemented. */
3894 case 503: /* Bad sequence of commands. */
3895 case 504: /* Command not implemented for that parameter. */
3896 case 532: /* Need account for storing files */
3897 case 550: /* File action not taken. File not found or no access. */
3898 case 551: /* Requested action aborted. Page type unknown */
3899 case 552: /* Action aborted. Exceeded storage allocation */
3900 case 553: /* Action not taken. File name not allowed. */
3902 default:
3903 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3904 break;
3907 INTERNET_SetLastError(dwCode);
3908 return dwCode;