msvcrt/tests: Remove a space before a '\n'.
[wine/gsoc-2012-control.git] / dlls / wininet / ftp.c
blob028930063c56b6c404300edc003f876ba4a6ca8c
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_UNISTD_H
47 # include <unistd.h>
48 #endif
49 #include <time.h>
50 #include <assert.h>
52 #include "windef.h"
53 #include "winbase.h"
54 #include "wingdi.h"
55 #include "winuser.h"
56 #include "wininet.h"
57 #include "winnls.h"
58 #include "winerror.h"
59 #include "winreg.h"
60 #include "winternl.h"
61 #include "shlwapi.h"
63 #include "wine/debug.h"
64 #include "internet.h"
66 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
68 typedef struct _WININETFTPSESSIONW WININETFTPSESSIONW;
70 typedef struct
72 WININETHANDLEHEADER hdr;
73 WININETFTPSESSIONW *lpFtpSession;
74 BOOL session_deleted;
75 int nDataSocket;
76 } WININETFTPFILE, *LPWININETFTPFILE;
78 typedef struct _WININETFTPSESSIONW
80 WININETHANDLEHEADER hdr;
81 WININETAPPINFOW *lpAppInfo;
82 int sndSocket;
83 int lstnSocket;
84 int pasvSocket; /* data socket connected by us in case of passive FTP */
85 LPWININETFTPFILE download_in_progress;
86 struct sockaddr_in socketAddress;
87 struct sockaddr_in lstnSocketAddress;
88 LPWSTR lpszPassword;
89 LPWSTR lpszUserName;
90 } *LPWININETFTPSESSIONW;
92 typedef struct
94 BOOL bIsDirectory;
95 LPWSTR lpszName;
96 DWORD nSize;
97 SYSTEMTIME tmLastModified;
98 unsigned short permissions;
99 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
101 typedef struct
103 WININETHANDLEHEADER hdr;
104 WININETFTPSESSIONW *lpFtpSession;
105 DWORD index;
106 DWORD size;
107 LPFILEPROPERTIESW lpafp;
108 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
110 #define DATA_PACKET_SIZE 0x2000
111 #define szCRLF "\r\n"
112 #define MAX_BACKLOG 5
114 /* Testing shows that Windows only accepts dwFlags where the last
115 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
117 #define FTP_CONDITION_MASK 0x0007
119 typedef enum {
120 /* FTP commands with arguments. */
121 FTP_CMD_ACCT,
122 FTP_CMD_CWD,
123 FTP_CMD_DELE,
124 FTP_CMD_MKD,
125 FTP_CMD_PASS,
126 FTP_CMD_PORT,
127 FTP_CMD_RETR,
128 FTP_CMD_RMD,
129 FTP_CMD_RNFR,
130 FTP_CMD_RNTO,
131 FTP_CMD_STOR,
132 FTP_CMD_TYPE,
133 FTP_CMD_USER,
134 FTP_CMD_SIZE,
136 /* FTP commands without arguments. */
137 FTP_CMD_ABOR,
138 FTP_CMD_LIST,
139 FTP_CMD_NLST,
140 FTP_CMD_PASV,
141 FTP_CMD_PWD,
142 FTP_CMD_QUIT,
143 } FTP_COMMAND;
145 static const CHAR *const szFtpCommands[] = {
146 "ACCT",
147 "CWD",
148 "DELE",
149 "MKD",
150 "PASS",
151 "PORT",
152 "RETR",
153 "RMD",
154 "RNFR",
155 "RNTO",
156 "STOR",
157 "TYPE",
158 "USER",
159 "SIZE",
160 "ABOR",
161 "LIST",
162 "NLST",
163 "PASV",
164 "PWD",
165 "QUIT",
168 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
169 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
171 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
172 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext);
173 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
174 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket);
175 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile);
176 static INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD_PTR dwContext);
177 static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
178 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile);
179 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs);
180 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs);
181 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs);
182 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs);
183 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType);
184 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs);
185 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs);
186 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs);
187 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
188 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
189 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
190 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
191 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
192 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
193 static DWORD FTP_SetResponseError(DWORD dwResponse);
194 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
195 static BOOL FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
196 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
197 static BOOL FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
198 static BOOL FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
199 static HINTERNET FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
200 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
201 static BOOL FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
202 LPDWORD lpdwCurrentDirectory);
203 static BOOL FTP_FtpRenameFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest);
204 static BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
205 static BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName);
206 static HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName,
207 DWORD fdwAccess, DWORD dwFlags, DWORD_PTR dwContext);
208 static BOOL FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
209 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
210 DWORD_PTR dwContext);
213 /***********************************************************************
214 * FtpPutFileA (WININET.@)
216 * Uploads a file to the FTP server
218 * RETURNS
219 * TRUE on success
220 * FALSE on failure
223 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
224 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
226 LPWSTR lpwzLocalFile;
227 LPWSTR lpwzNewRemoteFile;
228 BOOL ret;
230 lpwzLocalFile = lpszLocalFile?WININET_strdup_AtoW(lpszLocalFile):NULL;
231 lpwzNewRemoteFile = lpszNewRemoteFile?WININET_strdup_AtoW(lpszNewRemoteFile):NULL;
232 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
233 dwFlags, dwContext);
234 HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
235 HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
236 return ret;
239 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
241 struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
242 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
244 TRACE("%p\n", lpwfs);
246 FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
247 req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
249 HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
250 HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
253 /***********************************************************************
254 * FtpPutFileW (WININET.@)
256 * Uploads a file to the FTP server
258 * RETURNS
259 * TRUE on success
260 * FALSE on failure
263 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
264 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
266 LPWININETFTPSESSIONW lpwfs;
267 LPWININETAPPINFOW hIC = NULL;
268 BOOL r = FALSE;
270 if (!lpszLocalFile || !lpszNewRemoteFile)
272 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
273 return FALSE;
276 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
277 if (!lpwfs)
279 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
280 return FALSE;
283 if (WH_HFTPSESSION != lpwfs->hdr.htype)
285 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
286 goto lend;
289 if (lpwfs->download_in_progress != NULL)
291 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
292 goto lend;
295 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
297 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
298 goto lend;
301 hIC = lpwfs->lpAppInfo;
302 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
304 WORKREQUEST workRequest;
305 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
307 workRequest.asyncproc = AsyncFtpPutFileProc;
308 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
309 req->lpszLocalFile = WININET_strdupW(lpszLocalFile);
310 req->lpszNewRemoteFile = WININET_strdupW(lpszNewRemoteFile);
311 req->dwFlags = dwFlags;
312 req->dwContext = dwContext;
314 r = INTERNET_AsyncCall(&workRequest);
316 else
318 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
319 lpszNewRemoteFile, dwFlags, dwContext);
322 lend:
323 WININET_Release( &lpwfs->hdr );
325 return r;
328 /***********************************************************************
329 * FTP_FtpPutFileW (Internal)
331 * Uploads a file to the FTP server
333 * RETURNS
334 * TRUE on success
335 * FALSE on failure
338 static BOOL FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
339 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
341 HANDLE hFile;
342 BOOL bSuccess = FALSE;
343 LPWININETAPPINFOW hIC = NULL;
344 INT nResCode;
346 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
348 /* Clear any error information */
349 INTERNET_SetLastError(0);
351 /* Open file to be uploaded */
352 if (INVALID_HANDLE_VALUE ==
353 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
354 /* Let CreateFile set the appropriate error */
355 return FALSE;
357 hIC = lpwfs->lpAppInfo;
359 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
361 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
363 INT nDataSocket;
365 /* Get data socket to server */
366 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
368 FTP_SendData(lpwfs, nDataSocket, hFile);
369 closesocket(nDataSocket);
370 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
371 if (nResCode)
373 if (nResCode == 226)
374 bSuccess = TRUE;
375 else
376 FTP_SetResponseError(nResCode);
381 if (lpwfs->lstnSocket != -1)
382 closesocket(lpwfs->lstnSocket);
384 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
386 INTERNET_ASYNC_RESULT iar;
388 iar.dwResult = (DWORD)bSuccess;
389 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
390 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
391 &iar, sizeof(INTERNET_ASYNC_RESULT));
394 CloseHandle(hFile);
396 return bSuccess;
400 /***********************************************************************
401 * FtpSetCurrentDirectoryA (WININET.@)
403 * Change the working directory on the FTP server
405 * RETURNS
406 * TRUE on success
407 * FALSE on failure
410 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
412 LPWSTR lpwzDirectory;
413 BOOL ret;
415 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
416 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
417 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
418 return ret;
422 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
424 struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
425 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
427 TRACE("%p\n", lpwfs);
429 FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
430 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
433 /***********************************************************************
434 * FtpSetCurrentDirectoryW (WININET.@)
436 * Change the working directory on the FTP server
438 * RETURNS
439 * TRUE on success
440 * FALSE on failure
443 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
445 LPWININETFTPSESSIONW lpwfs = NULL;
446 LPWININETAPPINFOW hIC = NULL;
447 BOOL r = FALSE;
449 if (!lpszDirectory)
451 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
452 goto lend;
455 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
456 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
458 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
459 goto lend;
462 if (lpwfs->download_in_progress != NULL)
464 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
465 goto lend;
468 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
470 hIC = lpwfs->lpAppInfo;
471 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
473 WORKREQUEST workRequest;
474 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
476 workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
477 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
478 req = &workRequest.u.FtpSetCurrentDirectoryW;
479 req->lpszDirectory = WININET_strdupW(lpszDirectory);
481 r = INTERNET_AsyncCall(&workRequest);
483 else
485 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
488 lend:
489 if( lpwfs )
490 WININET_Release( &lpwfs->hdr );
492 return r;
496 /***********************************************************************
497 * FTP_FtpSetCurrentDirectoryW (Internal)
499 * Change the working directory on the FTP server
501 * RETURNS
502 * TRUE on success
503 * FALSE on failure
506 static BOOL FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
508 INT nResCode;
509 LPWININETAPPINFOW hIC = NULL;
510 DWORD bSuccess = FALSE;
512 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
514 /* Clear any error information */
515 INTERNET_SetLastError(0);
517 hIC = lpwfs->lpAppInfo;
518 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
519 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
520 goto lend;
522 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
524 if (nResCode)
526 if (nResCode == 250)
527 bSuccess = TRUE;
528 else
529 FTP_SetResponseError(nResCode);
532 lend:
533 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
535 INTERNET_ASYNC_RESULT iar;
537 iar.dwResult = bSuccess;
538 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
539 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
540 &iar, sizeof(INTERNET_ASYNC_RESULT));
542 return bSuccess;
546 /***********************************************************************
547 * FtpCreateDirectoryA (WININET.@)
549 * Create new directory on the FTP server
551 * RETURNS
552 * TRUE on success
553 * FALSE on failure
556 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
558 LPWSTR lpwzDirectory;
559 BOOL ret;
561 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
562 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
563 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
564 return ret;
568 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
570 struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
571 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
573 TRACE(" %p\n", lpwfs);
575 FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
576 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
579 /***********************************************************************
580 * FtpCreateDirectoryW (WININET.@)
582 * Create new directory on the FTP server
584 * RETURNS
585 * TRUE on success
586 * FALSE on failure
589 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
591 LPWININETFTPSESSIONW lpwfs;
592 LPWININETAPPINFOW hIC = NULL;
593 BOOL r = FALSE;
595 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
596 if (!lpwfs)
598 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
599 return FALSE;
602 if (WH_HFTPSESSION != lpwfs->hdr.htype)
604 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
605 goto lend;
608 if (lpwfs->download_in_progress != NULL)
610 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
611 goto lend;
614 if (!lpszDirectory)
616 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
617 goto lend;
620 hIC = lpwfs->lpAppInfo;
621 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
623 WORKREQUEST workRequest;
624 struct WORKREQ_FTPCREATEDIRECTORYW *req;
626 workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
627 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
628 req = &workRequest.u.FtpCreateDirectoryW;
629 req->lpszDirectory = WININET_strdupW(lpszDirectory);
631 r = INTERNET_AsyncCall(&workRequest);
633 else
635 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
637 lend:
638 WININET_Release( &lpwfs->hdr );
640 return r;
644 /***********************************************************************
645 * FTP_FtpCreateDirectoryW (Internal)
647 * Create new directory on the FTP server
649 * RETURNS
650 * TRUE on success
651 * FALSE on failure
654 static BOOL FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
656 INT nResCode;
657 BOOL bSuccess = FALSE;
658 LPWININETAPPINFOW hIC = NULL;
660 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
662 /* Clear any error information */
663 INTERNET_SetLastError(0);
665 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
666 goto lend;
668 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
669 if (nResCode)
671 if (nResCode == 257)
672 bSuccess = TRUE;
673 else
674 FTP_SetResponseError(nResCode);
677 lend:
678 hIC = lpwfs->lpAppInfo;
679 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
681 INTERNET_ASYNC_RESULT iar;
683 iar.dwResult = (DWORD)bSuccess;
684 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
685 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
686 &iar, sizeof(INTERNET_ASYNC_RESULT));
689 return bSuccess;
692 /***********************************************************************
693 * FtpFindFirstFileA (WININET.@)
695 * Search the specified directory
697 * RETURNS
698 * HINTERNET on success
699 * NULL on failure
702 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
703 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
705 LPWSTR lpwzSearchFile;
706 WIN32_FIND_DATAW wfd;
707 LPWIN32_FIND_DATAW lpFindFileDataW;
708 HINTERNET ret;
710 lpwzSearchFile = lpszSearchFile?WININET_strdup_AtoW(lpszSearchFile):NULL;
711 lpFindFileDataW = lpFindFileData?&wfd:NULL;
712 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
713 HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
715 if(lpFindFileData) {
716 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
718 return ret;
722 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
724 struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
725 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
727 TRACE("%p\n", lpwfs);
729 FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
730 req->lpFindFileData, req->dwFlags, req->dwContext);
731 HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
734 /***********************************************************************
735 * FtpFindFirstFileW (WININET.@)
737 * Search the specified directory
739 * RETURNS
740 * HINTERNET on success
741 * NULL on failure
744 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
745 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
747 LPWININETFTPSESSIONW lpwfs;
748 LPWININETAPPINFOW hIC = NULL;
749 HINTERNET r = NULL;
751 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
752 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
754 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
755 goto lend;
758 if (lpwfs->download_in_progress != NULL)
760 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
761 goto lend;
764 hIC = lpwfs->lpAppInfo;
765 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
767 WORKREQUEST workRequest;
768 struct WORKREQ_FTPFINDFIRSTFILEW *req;
770 workRequest.asyncproc = AsyncFtpFindFirstFileProc;
771 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
772 req = &workRequest.u.FtpFindFirstFileW;
773 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : WININET_strdupW(lpszSearchFile);
774 req->lpFindFileData = lpFindFileData;
775 req->dwFlags = dwFlags;
776 req->dwContext= dwContext;
778 INTERNET_AsyncCall(&workRequest);
779 r = NULL;
781 else
783 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
784 dwFlags, dwContext);
786 lend:
787 if( lpwfs )
788 WININET_Release( &lpwfs->hdr );
790 return r;
794 /***********************************************************************
795 * FTP_FtpFindFirstFileW (Internal)
797 * Search the specified directory
799 * RETURNS
800 * HINTERNET on success
801 * NULL on failure
804 static HINTERNET FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
805 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
807 INT nResCode;
808 LPWININETAPPINFOW hIC = NULL;
809 HINTERNET hFindNext = NULL;
811 TRACE("\n");
813 /* Clear any error information */
814 INTERNET_SetLastError(0);
816 if (!FTP_InitListenSocket(lpwfs))
817 goto lend;
819 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
820 goto lend;
822 if (!FTP_SendPortOrPasv(lpwfs))
823 goto lend;
825 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
826 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
827 goto lend;
829 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
830 if (nResCode)
832 if (nResCode == 125 || nResCode == 150)
834 INT nDataSocket;
836 /* Get data socket to server */
837 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
839 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
840 closesocket(nDataSocket);
841 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
842 if (nResCode != 226 && nResCode != 250)
843 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
846 else
847 FTP_SetResponseError(nResCode);
850 lend:
851 if (lpwfs->lstnSocket != -1)
852 closesocket(lpwfs->lstnSocket);
854 hIC = lpwfs->lpAppInfo;
855 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
857 INTERNET_ASYNC_RESULT iar;
859 if (hFindNext)
861 iar.dwResult = (DWORD_PTR)hFindNext;
862 iar.dwError = ERROR_SUCCESS;
863 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
864 &iar, sizeof(INTERNET_ASYNC_RESULT));
867 iar.dwResult = (DWORD_PTR)hFindNext;
868 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
869 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
870 &iar, sizeof(INTERNET_ASYNC_RESULT));
873 return hFindNext;
877 /***********************************************************************
878 * FtpGetCurrentDirectoryA (WININET.@)
880 * Retrieves the current directory
882 * RETURNS
883 * TRUE on success
884 * FALSE on failure
887 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
888 LPDWORD lpdwCurrentDirectory)
890 WCHAR *dir = NULL;
891 DWORD len;
892 BOOL ret;
894 if(lpdwCurrentDirectory) {
895 len = *lpdwCurrentDirectory;
896 if(lpszCurrentDirectory)
898 dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
899 if (NULL == dir)
901 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
902 return FALSE;
906 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
908 if (ret && lpszCurrentDirectory)
909 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
911 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
912 HeapFree(GetProcessHeap(), 0, dir);
913 return ret;
917 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
919 struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
920 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
922 TRACE("%p\n", lpwfs);
924 FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
927 /***********************************************************************
928 * FtpGetCurrentDirectoryW (WININET.@)
930 * Retrieves the current directory
932 * RETURNS
933 * TRUE on success
934 * FALSE on failure
937 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
938 LPDWORD lpdwCurrentDirectory)
940 LPWININETFTPSESSIONW lpwfs;
941 LPWININETAPPINFOW hIC = NULL;
942 BOOL r = FALSE;
944 TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
946 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
947 if (NULL == lpwfs)
949 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
950 goto lend;
953 if (WH_HFTPSESSION != lpwfs->hdr.htype)
955 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
956 goto lend;
959 if (!lpdwCurrentDirectory)
961 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
962 goto lend;
965 if (lpszCurrentDirectory == NULL)
967 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
968 goto lend;
971 if (lpwfs->download_in_progress != NULL)
973 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
974 goto lend;
977 hIC = lpwfs->lpAppInfo;
978 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
980 WORKREQUEST workRequest;
981 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
983 workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
984 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
985 req = &workRequest.u.FtpGetCurrentDirectoryW;
986 req->lpszDirectory = lpszCurrentDirectory;
987 req->lpdwDirectory = lpdwCurrentDirectory;
989 r = INTERNET_AsyncCall(&workRequest);
991 else
993 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
994 lpdwCurrentDirectory);
997 lend:
998 if( lpwfs )
999 WININET_Release( &lpwfs->hdr );
1001 return r;
1005 /***********************************************************************
1006 * FTP_FtpGetCurrentDirectoryW (Internal)
1008 * Retrieves the current directory
1010 * RETURNS
1011 * TRUE on success
1012 * FALSE on failure
1015 static BOOL FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
1016 LPDWORD lpdwCurrentDirectory)
1018 INT nResCode;
1019 LPWININETAPPINFOW hIC = NULL;
1020 DWORD bSuccess = FALSE;
1022 /* Clear any error information */
1023 INTERNET_SetLastError(0);
1025 hIC = lpwfs->lpAppInfo;
1026 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1027 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1028 goto lend;
1030 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1031 if (nResCode)
1033 if (nResCode == 257) /* Extract directory name */
1035 DWORD firstpos, lastpos, len;
1036 LPWSTR lpszResponseBuffer = WININET_strdup_AtoW(INTERNET_GetResponseBuffer());
1038 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1040 if ('"' == lpszResponseBuffer[lastpos])
1042 if (!firstpos)
1043 firstpos = lastpos;
1044 else
1045 break;
1048 len = lastpos - firstpos;
1049 if (*lpdwCurrentDirectory >= len)
1051 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1052 lpszCurrentDirectory[len - 1] = 0;
1053 *lpdwCurrentDirectory = len;
1054 bSuccess = TRUE;
1056 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1058 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
1060 else
1061 FTP_SetResponseError(nResCode);
1064 lend:
1065 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1067 INTERNET_ASYNC_RESULT iar;
1069 iar.dwResult = bSuccess;
1070 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1071 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1072 &iar, sizeof(INTERNET_ASYNC_RESULT));
1075 return bSuccess;
1078 /***********************************************************************
1079 * FtpOpenFileA (WININET.@)
1081 * Open a remote file for writing or reading
1083 * RETURNS
1084 * HINTERNET handle on success
1085 * NULL on failure
1088 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1089 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1090 DWORD_PTR dwContext)
1092 LPWSTR lpwzFileName;
1093 HINTERNET ret;
1095 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1096 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1097 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1098 return ret;
1102 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1104 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1105 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1107 TRACE("%p\n", lpwfs);
1109 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1110 req->dwAccess, req->dwFlags, req->dwContext);
1111 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1114 /***********************************************************************
1115 * FtpOpenFileW (WININET.@)
1117 * Open a remote file for writing or reading
1119 * RETURNS
1120 * HINTERNET handle on success
1121 * NULL on failure
1124 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1125 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1126 DWORD_PTR dwContext)
1128 LPWININETFTPSESSIONW lpwfs;
1129 LPWININETAPPINFOW hIC = NULL;
1130 HINTERNET r = NULL;
1132 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1133 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1135 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1136 if (!lpwfs)
1138 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1139 return FALSE;
1142 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1144 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1145 goto lend;
1148 if ((!lpszFileName) ||
1149 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1150 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1152 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1153 goto lend;
1156 if (lpwfs->download_in_progress != NULL)
1158 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1159 goto lend;
1162 hIC = lpwfs->lpAppInfo;
1163 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1165 WORKREQUEST workRequest;
1166 struct WORKREQ_FTPOPENFILEW *req;
1168 workRequest.asyncproc = AsyncFtpOpenFileProc;
1169 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1170 req = &workRequest.u.FtpOpenFileW;
1171 req->lpszFilename = WININET_strdupW(lpszFileName);
1172 req->dwAccess = fdwAccess;
1173 req->dwFlags = dwFlags;
1174 req->dwContext = dwContext;
1176 INTERNET_AsyncCall(&workRequest);
1177 r = NULL;
1179 else
1181 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1184 lend:
1185 WININET_Release( &lpwfs->hdr );
1187 return r;
1191 /***********************************************************************
1192 * FTPFILE_Destroy(internal)
1194 * Closes the file transfer handle. This also 'cleans' the data queue of
1195 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1198 static void FTPFILE_Destroy(WININETHANDLEHEADER *hdr)
1200 LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
1201 LPWININETFTPSESSIONW lpwfs = lpwh->lpFtpSession;
1202 INT nResCode;
1204 TRACE("\n");
1206 WININET_Release(&lpwh->lpFtpSession->hdr);
1208 if (!lpwh->session_deleted)
1209 lpwfs->download_in_progress = NULL;
1211 if (lpwh->nDataSocket != -1)
1212 closesocket(lpwh->nDataSocket);
1214 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1215 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1217 HeapFree(GetProcessHeap(), 0, lpwh);
1220 static DWORD FTPFILE_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1222 switch(option) {
1223 case INTERNET_OPTION_HANDLE_TYPE:
1224 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1226 if (*size < sizeof(ULONG))
1227 return ERROR_INSUFFICIENT_BUFFER;
1229 *size = sizeof(DWORD);
1230 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1231 return ERROR_SUCCESS;
1234 return INET_QueryOption(option, buffer, size, unicode);
1237 static DWORD FTPFILE_ReadFile(WININETHANDLEHEADER *hdr, void *buffer, DWORD size, DWORD *read)
1239 WININETFTPFILE *file = (WININETFTPFILE*)hdr;
1240 int res;
1242 if (file->nDataSocket == -1)
1243 return ERROR_INTERNET_DISCONNECTED;
1245 /* FIXME: FTP should use NETCON_ stuff */
1246 res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1247 *read = res>0 ? res : 0;
1249 return res>=0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME*/
1252 static BOOL FTPFILE_WriteFile(WININETHANDLEHEADER *hdr, const void *buffer, DWORD size, DWORD *written)
1254 LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
1255 int res;
1257 res = send(lpwh->nDataSocket, buffer, size, 0);
1259 *written = res>0 ? res : 0;
1260 return res >= 0;
1263 static const HANDLEHEADERVtbl FTPFILEVtbl = {
1264 FTPFILE_Destroy,
1265 NULL,
1266 FTPFILE_QueryOption,
1267 NULL,
1268 FTPFILE_ReadFile,
1269 NULL,
1270 NULL,
1271 FTPFILE_WriteFile,
1272 NULL,
1273 NULL
1276 /***********************************************************************
1277 * FTP_FtpOpenFileW (Internal)
1279 * Open a remote file for writing or reading
1281 * RETURNS
1282 * HINTERNET handle on success
1283 * NULL on failure
1286 HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
1287 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1288 DWORD_PTR dwContext)
1290 INT nDataSocket;
1291 BOOL bSuccess = FALSE;
1292 LPWININETFTPFILE lpwh = NULL;
1293 LPWININETAPPINFOW hIC = NULL;
1294 HINTERNET handle = NULL;
1296 TRACE("\n");
1298 /* Clear any error information */
1299 INTERNET_SetLastError(0);
1301 if (GENERIC_READ == fdwAccess)
1303 /* Set up socket to retrieve data */
1304 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1306 else if (GENERIC_WRITE == fdwAccess)
1308 /* Set up socket to send data */
1309 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1312 /* Get data socket to server */
1313 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1315 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPFILE));
1316 lpwh->hdr.htype = WH_HFILE;
1317 lpwh->hdr.vtbl = &FTPFILEVtbl;
1318 lpwh->hdr.dwFlags = dwFlags;
1319 lpwh->hdr.dwContext = dwContext;
1320 lpwh->hdr.refs = 1;
1321 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1322 lpwh->nDataSocket = nDataSocket;
1323 lpwh->session_deleted = FALSE;
1325 WININET_AddRef( &lpwfs->hdr );
1326 lpwh->lpFtpSession = lpwfs;
1327 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1329 handle = WININET_AllocHandle( &lpwh->hdr );
1330 if( !handle )
1331 goto lend;
1333 /* Indicate that a download is currently in progress */
1334 lpwfs->download_in_progress = lpwh;
1337 if (lpwfs->lstnSocket != -1)
1338 closesocket(lpwfs->lstnSocket);
1340 hIC = lpwfs->lpAppInfo;
1341 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1343 INTERNET_ASYNC_RESULT iar;
1345 if (lpwh)
1347 iar.dwResult = (DWORD_PTR)handle;
1348 iar.dwError = ERROR_SUCCESS;
1349 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1350 &iar, sizeof(INTERNET_ASYNC_RESULT));
1353 iar.dwResult = bSuccess;
1354 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1355 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1356 &iar, sizeof(INTERNET_ASYNC_RESULT));
1359 lend:
1360 if( lpwh )
1361 WININET_Release( &lpwh->hdr );
1363 return handle;
1367 /***********************************************************************
1368 * FtpGetFileA (WININET.@)
1370 * Retrieve file from the FTP server
1372 * RETURNS
1373 * TRUE on success
1374 * FALSE on failure
1377 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1378 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1379 DWORD_PTR dwContext)
1381 LPWSTR lpwzRemoteFile;
1382 LPWSTR lpwzNewFile;
1383 BOOL ret;
1385 lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
1386 lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
1387 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1388 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1389 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1390 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1391 return ret;
1395 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1397 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1398 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1400 TRACE("%p\n", lpwfs);
1402 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1403 req->lpszNewFile, req->fFailIfExists,
1404 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1405 HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1406 HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1410 /***********************************************************************
1411 * FtpGetFileW (WININET.@)
1413 * Retrieve file from the FTP server
1415 * RETURNS
1416 * TRUE on success
1417 * FALSE on failure
1420 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1421 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1422 DWORD_PTR dwContext)
1424 LPWININETFTPSESSIONW lpwfs;
1425 LPWININETAPPINFOW hIC = NULL;
1426 BOOL r = FALSE;
1428 if (!lpszRemoteFile || !lpszNewFile)
1430 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1431 return FALSE;
1434 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
1435 if (!lpwfs)
1437 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1438 return FALSE;
1441 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1443 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1444 goto lend;
1447 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1449 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1450 goto lend;
1453 if (lpwfs->download_in_progress != NULL)
1455 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1456 goto lend;
1459 hIC = lpwfs->lpAppInfo;
1460 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1462 WORKREQUEST workRequest;
1463 struct WORKREQ_FTPGETFILEW *req;
1465 workRequest.asyncproc = AsyncFtpGetFileProc;
1466 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1467 req = &workRequest.u.FtpGetFileW;
1468 req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
1469 req->lpszNewFile = WININET_strdupW(lpszNewFile);
1470 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1471 req->fFailIfExists = fFailIfExists;
1472 req->dwFlags = dwInternetFlags;
1473 req->dwContext = dwContext;
1475 r = INTERNET_AsyncCall(&workRequest);
1477 else
1479 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1480 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1483 lend:
1484 WININET_Release( &lpwfs->hdr );
1486 return r;
1490 /***********************************************************************
1491 * FTP_FtpGetFileW (Internal)
1493 * Retrieve file from the FTP server
1495 * RETURNS
1496 * TRUE on success
1497 * FALSE on failure
1500 static BOOL FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1501 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1502 DWORD_PTR dwContext)
1504 BOOL bSuccess = FALSE;
1505 HANDLE hFile;
1506 LPWININETAPPINFOW hIC = NULL;
1508 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1510 /* Clear any error information */
1511 INTERNET_SetLastError(0);
1513 /* Ensure we can write to lpszNewfile by opening it */
1514 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1515 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1516 if (INVALID_HANDLE_VALUE == hFile)
1517 return FALSE;
1519 /* Set up socket to retrieve data */
1520 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1522 INT nDataSocket;
1524 /* Get data socket to server */
1525 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1527 INT nResCode;
1529 /* Receive data */
1530 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1531 closesocket(nDataSocket);
1533 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1534 if (nResCode)
1536 if (nResCode == 226)
1537 bSuccess = TRUE;
1538 else
1539 FTP_SetResponseError(nResCode);
1544 if (lpwfs->lstnSocket != -1)
1545 closesocket(lpwfs->lstnSocket);
1547 CloseHandle(hFile);
1549 hIC = lpwfs->lpAppInfo;
1550 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1552 INTERNET_ASYNC_RESULT iar;
1554 iar.dwResult = (DWORD)bSuccess;
1555 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1556 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1557 &iar, sizeof(INTERNET_ASYNC_RESULT));
1560 if (!bSuccess) DeleteFileW(lpszNewFile);
1561 return bSuccess;
1564 /***********************************************************************
1565 * FtpGetFileSize (WININET.@)
1567 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1569 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1571 if (lpdwFileSizeHigh)
1572 *lpdwFileSizeHigh = 0;
1574 return 0;
1577 /***********************************************************************
1578 * FtpDeleteFileA (WININET.@)
1580 * Delete a file on the ftp server
1582 * RETURNS
1583 * TRUE on success
1584 * FALSE on failure
1587 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1589 LPWSTR lpwzFileName;
1590 BOOL ret;
1592 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1593 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1594 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1595 return ret;
1598 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1600 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1601 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1603 TRACE("%p\n", lpwfs);
1605 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1606 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1609 /***********************************************************************
1610 * FtpDeleteFileW (WININET.@)
1612 * Delete a file on the ftp server
1614 * RETURNS
1615 * TRUE on success
1616 * FALSE on failure
1619 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1621 LPWININETFTPSESSIONW lpwfs;
1622 LPWININETAPPINFOW hIC = NULL;
1623 BOOL r = FALSE;
1625 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1626 if (!lpwfs)
1628 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1629 return FALSE;
1632 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1634 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1635 goto lend;
1638 if (lpwfs->download_in_progress != NULL)
1640 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1641 goto lend;
1644 if (!lpszFileName)
1646 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1647 goto lend;
1650 hIC = lpwfs->lpAppInfo;
1651 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1653 WORKREQUEST workRequest;
1654 struct WORKREQ_FTPDELETEFILEW *req;
1656 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1657 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1658 req = &workRequest.u.FtpDeleteFileW;
1659 req->lpszFilename = WININET_strdupW(lpszFileName);
1661 r = INTERNET_AsyncCall(&workRequest);
1663 else
1665 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1668 lend:
1669 WININET_Release( &lpwfs->hdr );
1671 return r;
1674 /***********************************************************************
1675 * FTP_FtpDeleteFileW (Internal)
1677 * Delete a file on the ftp server
1679 * RETURNS
1680 * TRUE on success
1681 * FALSE on failure
1684 BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName)
1686 INT nResCode;
1687 BOOL bSuccess = FALSE;
1688 LPWININETAPPINFOW hIC = NULL;
1690 TRACE("%p\n", lpwfs);
1692 /* Clear any error information */
1693 INTERNET_SetLastError(0);
1695 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1696 goto lend;
1698 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1699 if (nResCode)
1701 if (nResCode == 250)
1702 bSuccess = TRUE;
1703 else
1704 FTP_SetResponseError(nResCode);
1706 lend:
1707 hIC = lpwfs->lpAppInfo;
1708 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1710 INTERNET_ASYNC_RESULT iar;
1712 iar.dwResult = (DWORD)bSuccess;
1713 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1714 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1715 &iar, sizeof(INTERNET_ASYNC_RESULT));
1718 return bSuccess;
1722 /***********************************************************************
1723 * FtpRemoveDirectoryA (WININET.@)
1725 * Remove a directory on the ftp server
1727 * RETURNS
1728 * TRUE on success
1729 * FALSE on failure
1732 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1734 LPWSTR lpwzDirectory;
1735 BOOL ret;
1737 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
1738 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1739 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1740 return ret;
1743 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1745 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1746 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1748 TRACE("%p\n", lpwfs);
1750 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1751 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1754 /***********************************************************************
1755 * FtpRemoveDirectoryW (WININET.@)
1757 * Remove a directory on the ftp server
1759 * RETURNS
1760 * TRUE on success
1761 * FALSE on failure
1764 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1766 LPWININETFTPSESSIONW lpwfs;
1767 LPWININETAPPINFOW hIC = NULL;
1768 BOOL r = FALSE;
1770 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1771 if (!lpwfs)
1773 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1774 return FALSE;
1777 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1779 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1780 goto lend;
1783 if (lpwfs->download_in_progress != NULL)
1785 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1786 goto lend;
1789 if (!lpszDirectory)
1791 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1792 goto lend;
1795 hIC = lpwfs->lpAppInfo;
1796 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1798 WORKREQUEST workRequest;
1799 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1801 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1802 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1803 req = &workRequest.u.FtpRemoveDirectoryW;
1804 req->lpszDirectory = WININET_strdupW(lpszDirectory);
1806 r = INTERNET_AsyncCall(&workRequest);
1808 else
1810 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1813 lend:
1814 WININET_Release( &lpwfs->hdr );
1816 return r;
1819 /***********************************************************************
1820 * FTP_FtpRemoveDirectoryW (Internal)
1822 * Remove a directory on the ftp server
1824 * RETURNS
1825 * TRUE on success
1826 * FALSE on failure
1829 BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
1831 INT nResCode;
1832 BOOL bSuccess = FALSE;
1833 LPWININETAPPINFOW hIC = NULL;
1835 TRACE("\n");
1837 /* Clear any error information */
1838 INTERNET_SetLastError(0);
1840 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1841 goto lend;
1843 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1844 if (nResCode)
1846 if (nResCode == 250)
1847 bSuccess = TRUE;
1848 else
1849 FTP_SetResponseError(nResCode);
1852 lend:
1853 hIC = lpwfs->lpAppInfo;
1854 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1856 INTERNET_ASYNC_RESULT iar;
1858 iar.dwResult = (DWORD)bSuccess;
1859 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1860 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1861 &iar, sizeof(INTERNET_ASYNC_RESULT));
1864 return bSuccess;
1868 /***********************************************************************
1869 * FtpRenameFileA (WININET.@)
1871 * Rename a file on the ftp server
1873 * RETURNS
1874 * TRUE on success
1875 * FALSE on failure
1878 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1880 LPWSTR lpwzSrc;
1881 LPWSTR lpwzDest;
1882 BOOL ret;
1884 lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL;
1885 lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL;
1886 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1887 HeapFree(GetProcessHeap(), 0, lpwzSrc);
1888 HeapFree(GetProcessHeap(), 0, lpwzDest);
1889 return ret;
1892 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
1894 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
1895 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1897 TRACE("%p\n", lpwfs);
1899 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
1900 HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
1901 HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
1904 /***********************************************************************
1905 * FtpRenameFileW (WININET.@)
1907 * Rename a file on the ftp server
1909 * RETURNS
1910 * TRUE on success
1911 * FALSE on failure
1914 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
1916 LPWININETFTPSESSIONW lpwfs;
1917 LPWININETAPPINFOW hIC = NULL;
1918 BOOL r = FALSE;
1920 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1921 if (!lpwfs)
1923 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1924 return FALSE;
1927 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1929 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1930 goto lend;
1933 if (lpwfs->download_in_progress != NULL)
1935 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1936 goto lend;
1939 if (!lpszSrc || !lpszDest)
1941 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1942 goto lend;
1945 hIC = lpwfs->lpAppInfo;
1946 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1948 WORKREQUEST workRequest;
1949 struct WORKREQ_FTPRENAMEFILEW *req;
1951 workRequest.asyncproc = AsyncFtpRenameFileProc;
1952 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1953 req = &workRequest.u.FtpRenameFileW;
1954 req->lpszSrcFile = WININET_strdupW(lpszSrc);
1955 req->lpszDestFile = WININET_strdupW(lpszDest);
1957 r = INTERNET_AsyncCall(&workRequest);
1959 else
1961 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
1964 lend:
1965 WININET_Release( &lpwfs->hdr );
1967 return r;
1970 /***********************************************************************
1971 * FTP_FtpRenameFileW (Internal)
1973 * Rename a file on the ftp server
1975 * RETURNS
1976 * TRUE on success
1977 * FALSE on failure
1980 BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs,
1981 LPCWSTR lpszSrc, LPCWSTR lpszDest)
1983 INT nResCode;
1984 BOOL bSuccess = FALSE;
1985 LPWININETAPPINFOW hIC = NULL;
1987 TRACE("\n");
1989 /* Clear any error information */
1990 INTERNET_SetLastError(0);
1992 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
1993 goto lend;
1995 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1996 if (nResCode == 350)
1998 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
1999 goto lend;
2001 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2004 if (nResCode == 250)
2005 bSuccess = TRUE;
2006 else
2007 FTP_SetResponseError(nResCode);
2009 lend:
2010 hIC = lpwfs->lpAppInfo;
2011 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2013 INTERNET_ASYNC_RESULT iar;
2015 iar.dwResult = (DWORD)bSuccess;
2016 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2017 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2018 &iar, sizeof(INTERNET_ASYNC_RESULT));
2021 return bSuccess;
2024 /***********************************************************************
2025 * FtpCommandA (WININET.@)
2027 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2028 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2030 BOOL r;
2031 WCHAR *cmdW;
2033 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2034 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2036 if (fExpectResponse)
2038 FIXME("data connection not supported\n");
2039 return FALSE;
2042 if (!lpszCommand || !lpszCommand[0])
2044 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2045 return FALSE;
2048 if (!(cmdW = WININET_strdup_AtoW(lpszCommand)))
2050 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2051 return FALSE;
2054 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2056 HeapFree(GetProcessHeap(), 0, cmdW);
2057 return r;
2060 /***********************************************************************
2061 * FtpCommandW (WININET.@)
2063 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2064 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2066 BOOL r = FALSE;
2067 LPWININETFTPSESSIONW lpwfs;
2068 LPSTR cmd = NULL;
2069 DWORD len, nBytesSent= 0;
2070 INT nResCode, nRC = 0;
2072 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2073 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2075 if (!lpszCommand || !lpszCommand[0])
2077 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2078 return FALSE;
2081 if (fExpectResponse)
2083 FIXME("data connection not supported\n");
2084 return FALSE;
2087 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
2088 if (!lpwfs)
2090 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2091 return FALSE;
2094 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2096 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2097 goto lend;
2100 if (lpwfs->download_in_progress != NULL)
2102 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2103 goto lend;
2106 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2107 if ((cmd = HeapAlloc(GetProcessHeap(), 0, len )))
2108 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2109 else
2111 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2112 goto lend;
2115 strcat(cmd, szCRLF);
2116 len--;
2118 TRACE("Sending (%s) len(%d)\n", cmd, len);
2119 while ((nBytesSent < len) && (nRC != -1))
2121 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2122 if (nRC != -1)
2124 nBytesSent += nRC;
2125 TRACE("Sent %d bytes\n", nRC);
2129 if (nBytesSent)
2131 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2132 if (nResCode > 0 && nResCode < 400)
2133 r = TRUE;
2134 else
2135 FTP_SetResponseError(nResCode);
2138 lend:
2139 WININET_Release( &lpwfs->hdr );
2140 HeapFree(GetProcessHeap(), 0, cmd);
2141 return r;
2145 /***********************************************************************
2146 * FTPSESSION_Destroy (internal)
2148 * Deallocate session handle
2150 static void FTPSESSION_Destroy(WININETHANDLEHEADER *hdr)
2152 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2154 TRACE("\n");
2156 WININET_Release(&lpwfs->lpAppInfo->hdr);
2158 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2159 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2160 HeapFree(GetProcessHeap(), 0, lpwfs);
2163 static void FTPSESSION_CloseConnection(WININETHANDLEHEADER *hdr)
2165 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2167 TRACE("\n");
2169 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2170 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2172 if (lpwfs->download_in_progress != NULL)
2173 lpwfs->download_in_progress->session_deleted = TRUE;
2175 if (lpwfs->sndSocket != -1)
2176 closesocket(lpwfs->sndSocket);
2178 if (lpwfs->lstnSocket != -1)
2179 closesocket(lpwfs->lstnSocket);
2181 if (lpwfs->pasvSocket != -1)
2182 closesocket(lpwfs->pasvSocket);
2184 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2185 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2188 static DWORD FTPSESSION_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2190 switch(option) {
2191 case INTERNET_OPTION_HANDLE_TYPE:
2192 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2194 if (*size < sizeof(ULONG))
2195 return ERROR_INSUFFICIENT_BUFFER;
2197 *size = sizeof(DWORD);
2198 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2199 return ERROR_SUCCESS;
2202 return INET_QueryOption(option, buffer, size, unicode);
2205 static const HANDLEHEADERVtbl FTPSESSIONVtbl = {
2206 FTPSESSION_Destroy,
2207 FTPSESSION_CloseConnection,
2208 FTPSESSION_QueryOption,
2209 NULL,
2210 NULL,
2211 NULL,
2212 NULL,
2213 NULL,
2214 NULL
2218 /***********************************************************************
2219 * FTP_Connect (internal)
2221 * Connect to a ftp server
2223 * RETURNS
2224 * HINTERNET a session handle on success
2225 * NULL on failure
2227 * NOTES:
2229 * Windows uses 'anonymous' as the username, when given a NULL username
2230 * and a NULL password. The password is first looked up in:
2232 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2234 * If this entry is not present it uses the current username as the password.
2238 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
2239 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2240 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2241 DWORD dwInternalFlags)
2243 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2244 'M','i','c','r','o','s','o','f','t','\\',
2245 'W','i','n','d','o','w','s','\\',
2246 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2247 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2248 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2249 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2250 static const WCHAR szEmpty[] = {'\0'};
2251 struct sockaddr_in socketAddr;
2252 INT nsocket = -1;
2253 UINT sock_namelen;
2254 BOOL bSuccess = FALSE;
2255 LPWININETFTPSESSIONW lpwfs = NULL;
2256 HINTERNET handle = NULL;
2258 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2259 hIC, debugstr_w(lpszServerName),
2260 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2262 assert( hIC->hdr.htype == WH_HINIT );
2264 if ((!lpszUserName || !strlenW(lpszUserName)) && lpszPassword)
2266 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2267 goto lerror;
2270 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
2271 if (NULL == lpwfs)
2273 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2274 goto lerror;
2277 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2278 nServerPort = INTERNET_DEFAULT_FTP_PORT;
2280 lpwfs->hdr.htype = WH_HFTPSESSION;
2281 lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
2282 lpwfs->hdr.dwFlags = dwFlags;
2283 lpwfs->hdr.dwContext = dwContext;
2284 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2285 lpwfs->hdr.refs = 1;
2286 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2287 lpwfs->download_in_progress = NULL;
2288 lpwfs->sndSocket = -1;
2289 lpwfs->lstnSocket = -1;
2290 lpwfs->pasvSocket = -1;
2292 WININET_AddRef( &hIC->hdr );
2293 lpwfs->lpAppInfo = hIC;
2294 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2296 handle = WININET_AllocHandle( &lpwfs->hdr );
2297 if( !handle )
2299 ERR("Failed to alloc handle\n");
2300 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2301 goto lerror;
2304 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2305 if(strchrW(hIC->lpszProxy, ' '))
2306 FIXME("Several proxies not implemented.\n");
2307 if(hIC->lpszProxyBypass)
2308 FIXME("Proxy bypass is ignored.\n");
2310 if (!lpszUserName || !strlenW(lpszUserName)) {
2311 HKEY key;
2312 WCHAR szPassword[MAX_PATH];
2313 DWORD len = sizeof(szPassword);
2315 lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
2317 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2318 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2319 /* Nothing in the registry, get the username and use that as the password */
2320 if (!GetUserNameW(szPassword, &len)) {
2321 /* Should never get here, but use an empty password as failsafe */
2322 strcpyW(szPassword, szEmpty);
2325 RegCloseKey(key);
2327 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2328 lpwfs->lpszPassword = WININET_strdupW(szPassword);
2330 else {
2331 lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
2333 if (lpszPassword)
2334 lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
2335 else
2336 lpwfs->lpszPassword = WININET_strdupW(szEmpty);
2339 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2340 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2342 INTERNET_ASYNC_RESULT iar;
2344 iar.dwResult = (DWORD_PTR)handle;
2345 iar.dwError = ERROR_SUCCESS;
2347 SendAsyncCallback(&hIC->hdr, dwContext,
2348 INTERNET_STATUS_HANDLE_CREATED, &iar,
2349 sizeof(INTERNET_ASYNC_RESULT));
2352 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2353 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2355 if (!GetAddress(lpszServerName, nServerPort, &socketAddr))
2357 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2358 goto lerror;
2361 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2362 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2364 nsocket = socket(AF_INET,SOCK_STREAM,0);
2365 if (nsocket == -1)
2367 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2368 goto lerror;
2371 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2372 &socketAddr, sizeof(struct sockaddr_in));
2374 if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
2376 ERR("Unable to connect (%s)\n", strerror(errno));
2377 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2379 else
2381 TRACE("Connected to server\n");
2382 lpwfs->sndSocket = nsocket;
2383 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2384 &socketAddr, sizeof(struct sockaddr_in));
2386 sock_namelen = sizeof(lpwfs->socketAddress);
2387 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2389 if (FTP_ConnectToHost(lpwfs))
2391 TRACE("Successfully logged into server\n");
2392 bSuccess = TRUE;
2396 lerror:
2397 if (lpwfs) WININET_Release( &lpwfs->hdr );
2399 if (!bSuccess && handle)
2401 WININET_FreeHandle( handle );
2402 handle = NULL;
2405 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2407 INTERNET_ASYNC_RESULT iar;
2409 iar.dwResult = bSuccess ? (DWORD_PTR)lpwfs : 0;
2410 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2411 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2412 &iar, sizeof(INTERNET_ASYNC_RESULT));
2415 return handle;
2419 /***********************************************************************
2420 * FTP_ConnectToHost (internal)
2422 * Connect to a ftp server
2424 * RETURNS
2425 * TRUE on success
2426 * NULL on failure
2429 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
2431 INT nResCode;
2432 BOOL bSuccess = FALSE;
2434 TRACE("\n");
2435 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2437 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2438 goto lend;
2440 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2441 if (nResCode)
2443 /* Login successful... */
2444 if (nResCode == 230)
2445 bSuccess = TRUE;
2446 /* User name okay, need password... */
2447 else if (nResCode == 331)
2448 bSuccess = FTP_SendPassword(lpwfs);
2449 /* Need account for login... */
2450 else if (nResCode == 332)
2451 bSuccess = FTP_SendAccount(lpwfs);
2452 else
2453 FTP_SetResponseError(nResCode);
2456 TRACE("Returning %d\n", bSuccess);
2457 lend:
2458 return bSuccess;
2462 /***********************************************************************
2463 * FTP_SendCommandA (internal)
2465 * Send command to server
2467 * RETURNS
2468 * TRUE on success
2469 * NULL on failure
2472 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2473 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
2475 DWORD len;
2476 CHAR *buf;
2477 DWORD nBytesSent = 0;
2478 int nRC = 0;
2479 DWORD dwParamLen;
2481 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2483 if (lpfnStatusCB)
2485 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2488 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2489 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2490 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2492 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2493 return FALSE;
2495 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2496 dwParamLen ? lpszParam : "", szCRLF);
2498 TRACE("Sending (%s) len(%d)\n", buf, len);
2499 while((nBytesSent < len) && (nRC != -1))
2501 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2502 nBytesSent += nRC;
2505 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2507 if (lpfnStatusCB)
2509 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2510 &nBytesSent, sizeof(DWORD));
2513 TRACE("Sent %d bytes\n", nBytesSent);
2514 return (nRC != -1);
2517 /***********************************************************************
2518 * FTP_SendCommand (internal)
2520 * Send command to server
2522 * RETURNS
2523 * TRUE on success
2524 * NULL on failure
2527 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2528 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
2530 BOOL ret;
2531 LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
2532 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2533 HeapFree(GetProcessHeap(), 0, lpszParamA);
2534 return ret;
2537 /***********************************************************************
2538 * FTP_ReceiveResponse (internal)
2540 * Receive response from server
2542 * RETURNS
2543 * Reply code on success
2544 * 0 on failure
2547 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD_PTR dwContext)
2549 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2550 DWORD nRecv;
2551 INT rc = 0;
2552 char firstprefix[5];
2553 BOOL multiline = FALSE;
2555 TRACE("socket(%d)\n", lpwfs->sndSocket);
2557 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2559 while(1)
2561 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2562 goto lerror;
2564 if (nRecv >= 3)
2566 if(!multiline)
2568 if(lpszResponse[3] != '-')
2569 break;
2570 else
2571 { /* Start of multiline response. Loop until we get "nnn " */
2572 multiline = TRUE;
2573 memcpy(firstprefix, lpszResponse, 3);
2574 firstprefix[3] = ' ';
2575 firstprefix[4] = '\0';
2578 else
2580 if(!memcmp(firstprefix, lpszResponse, 4))
2581 break;
2586 if (nRecv >= 3)
2588 rc = atoi(lpszResponse);
2590 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2591 &nRecv, sizeof(DWORD));
2594 lerror:
2595 TRACE("return %d\n", rc);
2596 return rc;
2600 /***********************************************************************
2601 * FTP_SendPassword (internal)
2603 * Send password to ftp server
2605 * RETURNS
2606 * TRUE on success
2607 * NULL on failure
2610 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2612 INT nResCode;
2613 BOOL bSuccess = FALSE;
2615 TRACE("\n");
2616 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2617 goto lend;
2619 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2620 if (nResCode)
2622 TRACE("Received reply code %d\n", nResCode);
2623 /* Login successful... */
2624 if (nResCode == 230)
2625 bSuccess = TRUE;
2626 /* Command not implemented, superfluous at the server site... */
2627 /* Need account for login... */
2628 else if (nResCode == 332)
2629 bSuccess = FTP_SendAccount(lpwfs);
2630 else
2631 FTP_SetResponseError(nResCode);
2634 lend:
2635 TRACE("Returning %d\n", bSuccess);
2636 return bSuccess;
2640 /***********************************************************************
2641 * FTP_SendAccount (internal)
2645 * RETURNS
2646 * TRUE on success
2647 * FALSE on failure
2650 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2652 INT nResCode;
2653 BOOL bSuccess = FALSE;
2655 TRACE("\n");
2656 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2657 goto lend;
2659 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2660 if (nResCode)
2661 bSuccess = TRUE;
2662 else
2663 FTP_SetResponseError(nResCode);
2665 lend:
2666 return bSuccess;
2670 /***********************************************************************
2671 * FTP_SendStore (internal)
2673 * Send request to upload file to ftp server
2675 * RETURNS
2676 * TRUE on success
2677 * FALSE on failure
2680 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2682 INT nResCode;
2683 BOOL bSuccess = FALSE;
2685 TRACE("\n");
2686 if (!FTP_InitListenSocket(lpwfs))
2687 goto lend;
2689 if (!FTP_SendType(lpwfs, dwType))
2690 goto lend;
2692 if (!FTP_SendPortOrPasv(lpwfs))
2693 goto lend;
2695 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2696 goto lend;
2697 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2698 if (nResCode)
2700 if (nResCode == 150 || nResCode == 125)
2701 bSuccess = TRUE;
2702 else
2703 FTP_SetResponseError(nResCode);
2706 lend:
2707 if (!bSuccess && lpwfs->lstnSocket != -1)
2709 closesocket(lpwfs->lstnSocket);
2710 lpwfs->lstnSocket = -1;
2713 return bSuccess;
2717 /***********************************************************************
2718 * FTP_InitListenSocket (internal)
2720 * Create a socket to listen for server response
2722 * RETURNS
2723 * TRUE on success
2724 * FALSE on failure
2727 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2729 BOOL bSuccess = FALSE;
2730 socklen_t namelen = sizeof(struct sockaddr_in);
2732 TRACE("\n");
2734 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2735 if (lpwfs->lstnSocket == -1)
2737 TRACE("Unable to create listening socket\n");
2738 goto lend;
2741 /* We obtain our ip addr from the name of the command channel socket */
2742 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2744 /* and get the system to assign us a port */
2745 lpwfs->lstnSocketAddress.sin_port = htons(0);
2747 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2749 TRACE("Unable to bind socket\n");
2750 goto lend;
2753 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2755 TRACE("listen failed\n");
2756 goto lend;
2759 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2760 bSuccess = TRUE;
2762 lend:
2763 if (!bSuccess && lpwfs->lstnSocket != -1)
2765 closesocket(lpwfs->lstnSocket);
2766 lpwfs->lstnSocket = -1;
2769 return bSuccess;
2773 /***********************************************************************
2774 * FTP_SendType (internal)
2776 * Tell server type of data being transferred
2778 * RETURNS
2779 * TRUE on success
2780 * FALSE on failure
2782 * W98SE doesn't cache the type that's currently set
2783 * (i.e. it sends it always),
2784 * so we probably don't want to do that either.
2786 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2788 INT nResCode;
2789 WCHAR type[] = { 'I','\0' };
2790 BOOL bSuccess = FALSE;
2792 TRACE("\n");
2793 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2794 type[0] = 'A';
2796 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2797 goto lend;
2799 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2800 if (nResCode)
2802 if (nResCode == 2)
2803 bSuccess = TRUE;
2804 else
2805 FTP_SetResponseError(nResCode);
2808 lend:
2809 return bSuccess;
2813 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2814 /***********************************************************************
2815 * FTP_GetFileSize (internal)
2817 * Retrieves from the server the size of the given file
2819 * RETURNS
2820 * TRUE on success
2821 * FALSE on failure
2824 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2826 INT nResCode;
2827 BOOL bSuccess = FALSE;
2829 TRACE("\n");
2831 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2832 goto lend;
2834 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2835 if (nResCode)
2837 if (nResCode == 213) {
2838 /* Now parses the output to get the actual file size */
2839 int i;
2840 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2842 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2843 if (lpszResponseBuffer[i] == '\0') return FALSE;
2844 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2846 bSuccess = TRUE;
2847 } else {
2848 FTP_SetResponseError(nResCode);
2852 lend:
2853 return bSuccess;
2855 #endif
2858 /***********************************************************************
2859 * FTP_SendPort (internal)
2861 * Tell server which port to use
2863 * RETURNS
2864 * TRUE on success
2865 * FALSE on failure
2868 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2870 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2871 INT nResCode;
2872 WCHAR szIPAddress[64];
2873 BOOL bSuccess = FALSE;
2874 TRACE("\n");
2876 sprintfW(szIPAddress, szIPFormat,
2877 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2878 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2879 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2880 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2881 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2882 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2884 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2885 goto lend;
2887 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2888 if (nResCode)
2890 if (nResCode == 200)
2891 bSuccess = TRUE;
2892 else
2893 FTP_SetResponseError(nResCode);
2896 lend:
2897 return bSuccess;
2901 /***********************************************************************
2902 * FTP_DoPassive (internal)
2904 * Tell server that we want to do passive transfers
2905 * and connect data socket
2907 * RETURNS
2908 * TRUE on success
2909 * FALSE on failure
2912 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2914 INT nResCode;
2915 BOOL bSuccess = FALSE;
2917 TRACE("\n");
2918 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2919 goto lend;
2921 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2922 if (nResCode)
2924 if (nResCode == 227)
2926 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2927 LPSTR p;
2928 int f[6];
2929 int i;
2930 char *pAddr, *pPort;
2931 INT nsocket = -1;
2932 struct sockaddr_in dataSocketAddress;
2934 p = lpszResponseBuffer+4; /* skip status code */
2935 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
2937 if (*p == '\0')
2939 ERR("no address found in response, aborting\n");
2940 goto lend;
2943 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
2944 &f[4], &f[5]) != 6)
2946 ERR("unknown response address format '%s', aborting\n", p);
2947 goto lend;
2949 for (i=0; i < 6; i++)
2950 f[i] = f[i] & 0xff;
2952 dataSocketAddress = lpwfs->socketAddress;
2953 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2954 pPort = (char *)&(dataSocketAddress.sin_port);
2955 pAddr[0] = f[0];
2956 pAddr[1] = f[1];
2957 pAddr[2] = f[2];
2958 pAddr[3] = f[3];
2959 pPort[0] = f[4];
2960 pPort[1] = f[5];
2962 nsocket = socket(AF_INET,SOCK_STREAM,0);
2963 if (nsocket == -1)
2964 goto lend;
2966 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2968 ERR("can't connect passive FTP data port.\n");
2969 closesocket(nsocket);
2970 goto lend;
2972 lpwfs->pasvSocket = nsocket;
2973 bSuccess = TRUE;
2975 else
2976 FTP_SetResponseError(nResCode);
2979 lend:
2980 return bSuccess;
2984 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2986 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2988 if (!FTP_DoPassive(lpwfs))
2989 return FALSE;
2991 else
2993 if (!FTP_SendPort(lpwfs))
2994 return FALSE;
2996 return TRUE;
3000 /***********************************************************************
3001 * FTP_GetDataSocket (internal)
3003 * Either accepts an incoming data socket connection from the server
3004 * or just returns the already opened socket after a PASV command
3005 * in case of passive FTP.
3008 * RETURNS
3009 * TRUE on success
3010 * FALSE on failure
3013 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
3015 struct sockaddr_in saddr;
3016 socklen_t addrlen = sizeof(struct sockaddr);
3018 TRACE("\n");
3019 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3021 *nDataSocket = lpwfs->pasvSocket;
3023 else
3025 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3026 closesocket(lpwfs->lstnSocket);
3027 lpwfs->lstnSocket = -1;
3029 return *nDataSocket != -1;
3033 /***********************************************************************
3034 * FTP_SendData (internal)
3036 * Send data to the server
3038 * RETURNS
3039 * TRUE on success
3040 * FALSE on failure
3043 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
3045 BY_HANDLE_FILE_INFORMATION fi;
3046 DWORD nBytesRead = 0;
3047 DWORD nBytesSent = 0;
3048 DWORD nTotalSent = 0;
3049 DWORD nBytesToSend, nLen;
3050 int nRC = 1;
3051 time_t s_long_time, e_long_time;
3052 LONG nSeconds;
3053 CHAR *lpszBuffer;
3055 TRACE("\n");
3056 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3058 /* Get the size of the file. */
3059 GetFileInformationByHandle(hFile, &fi);
3060 time(&s_long_time);
3064 nBytesToSend = nBytesRead - nBytesSent;
3066 if (nBytesToSend <= 0)
3068 /* Read data from file. */
3069 nBytesSent = 0;
3070 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3071 ERR("Failed reading from file\n");
3073 if (nBytesRead > 0)
3074 nBytesToSend = nBytesRead;
3075 else
3076 break;
3079 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3080 DATA_PACKET_SIZE : nBytesToSend;
3081 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3083 if (nRC != -1)
3085 nBytesSent += nRC;
3086 nTotalSent += nRC;
3089 /* Do some computation to display the status. */
3090 time(&e_long_time);
3091 nSeconds = e_long_time - s_long_time;
3092 if( nSeconds / 60 > 0 )
3094 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3095 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3096 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3098 else
3100 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3101 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3102 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3104 } while (nRC != -1);
3106 TRACE("file transfer complete!\n");
3108 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3110 return nTotalSent;
3114 /***********************************************************************
3115 * FTP_SendRetrieve (internal)
3117 * Send request to retrieve a file
3119 * RETURNS
3120 * Number of bytes to be received on success
3121 * 0 on failure
3124 static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3126 INT nResCode;
3127 BOOL ret;
3129 TRACE("\n");
3130 if (!(ret = FTP_InitListenSocket(lpwfs)))
3131 goto lend;
3133 if (!(ret = FTP_SendType(lpwfs, dwType)))
3134 goto lend;
3136 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3137 goto lend;
3139 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3140 goto lend;
3142 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3143 if ((nResCode != 125) && (nResCode != 150)) {
3144 /* That means that we got an error getting the file. */
3145 FTP_SetResponseError(nResCode);
3146 ret = FALSE;
3149 lend:
3150 if (!ret && lpwfs->lstnSocket != -1)
3152 closesocket(lpwfs->lstnSocket);
3153 lpwfs->lstnSocket = -1;
3156 return ret;
3160 /***********************************************************************
3161 * FTP_RetrieveData (internal)
3163 * Retrieve data from server
3165 * RETURNS
3166 * TRUE on success
3167 * FALSE on failure
3170 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
3172 DWORD nBytesWritten;
3173 INT nRC = 0;
3174 CHAR *lpszBuffer;
3176 TRACE("\n");
3178 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3179 if (NULL == lpszBuffer)
3181 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3182 return FALSE;
3185 while (nRC != -1)
3187 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3188 if (nRC != -1)
3190 /* other side closed socket. */
3191 if (nRC == 0)
3192 goto recv_end;
3193 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3197 TRACE("Data transfer complete\n");
3199 recv_end:
3200 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3202 return (nRC != -1);
3205 /***********************************************************************
3206 * FTPFINDNEXT_Destroy (internal)
3208 * Deallocate session handle
3210 static void FTPFINDNEXT_Destroy(WININETHANDLEHEADER *hdr)
3212 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3213 DWORD i;
3215 TRACE("\n");
3217 WININET_Release(&lpwfn->lpFtpSession->hdr);
3219 for (i = 0; i < lpwfn->size; i++)
3221 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3224 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3225 HeapFree(GetProcessHeap(), 0, lpwfn);
3228 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3230 WIN32_FIND_DATAW *find_data = data;
3231 DWORD res = ERROR_SUCCESS;
3233 TRACE("index(%d) size(%d)\n", find->index, find->size);
3235 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3237 if (find->index < find->size) {
3238 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3239 find->index++;
3241 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3242 }else {
3243 res = ERROR_NO_MORE_FILES;
3246 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3248 INTERNET_ASYNC_RESULT iar;
3250 iar.dwResult = (res == ERROR_SUCCESS);
3251 iar.dwError = res;
3253 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3254 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3255 sizeof(INTERNET_ASYNC_RESULT));
3258 return res;
3261 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3263 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3265 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3268 static DWORD FTPFINDNEXT_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3270 switch(option) {
3271 case INTERNET_OPTION_HANDLE_TYPE:
3272 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3274 if (*size < sizeof(ULONG))
3275 return ERROR_INSUFFICIENT_BUFFER;
3277 *size = sizeof(DWORD);
3278 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3279 return ERROR_SUCCESS;
3282 return INET_QueryOption(option, buffer, size, unicode);
3285 static DWORD FTPFINDNEXT_FindNextFileW(WININETHANDLEHEADER *hdr, void *data)
3287 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3289 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3291 WORKREQUEST workRequest;
3292 struct WORKREQ_FTPFINDNEXTW *req;
3294 workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3295 workRequest.hdr = WININET_AddRef( &find->hdr );
3296 req = &workRequest.u.FtpFindNextW;
3297 req->lpFindFileData = data;
3299 INTERNET_AsyncCall(&workRequest);
3301 return ERROR_SUCCESS;
3304 return FTPFINDNEXT_FindNextFileProc(find, data);
3307 static const HANDLEHEADERVtbl FTPFINDNEXTVtbl = {
3308 FTPFINDNEXT_Destroy,
3309 NULL,
3310 FTPFINDNEXT_QueryOption,
3311 NULL,
3312 NULL,
3313 NULL,
3314 NULL,
3315 NULL,
3316 NULL,
3317 FTPFINDNEXT_FindNextFileW
3320 /***********************************************************************
3321 * FTP_ReceiveFileList (internal)
3323 * Read file list from server
3325 * RETURNS
3326 * Handle to file list on success
3327 * NULL on failure
3330 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3331 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3333 DWORD dwSize = 0;
3334 LPFILEPROPERTIESW lpafp = NULL;
3335 LPWININETFTPFINDNEXTW lpwfn = NULL;
3336 HINTERNET handle = 0;
3338 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3340 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3342 if(lpFindFileData)
3343 FTP_ConvertFileProp(lpafp, lpFindFileData);
3345 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3346 if (lpwfn)
3348 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3349 lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3350 lpwfn->hdr.dwContext = dwContext;
3351 lpwfn->hdr.refs = 1;
3352 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3353 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3354 lpwfn->size = dwSize;
3355 lpwfn->lpafp = lpafp;
3357 WININET_AddRef( &lpwfs->hdr );
3358 lpwfn->lpFtpSession = lpwfs;
3359 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3361 handle = WININET_AllocHandle( &lpwfn->hdr );
3365 if( lpwfn )
3366 WININET_Release( &lpwfn->hdr );
3368 TRACE("Matched %d files\n", dwSize);
3369 return handle;
3373 /***********************************************************************
3374 * FTP_ConvertFileProp (internal)
3376 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3378 * RETURNS
3379 * TRUE on success
3380 * FALSE on failure
3383 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3385 BOOL bSuccess = FALSE;
3387 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3389 if (lpafp)
3391 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3392 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3393 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3395 /* Not all fields are filled in */
3396 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3397 lpFindFileData->nFileSizeLow = lpafp->nSize;
3399 if (lpafp->bIsDirectory)
3400 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3402 if (lpafp->lpszName)
3403 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3405 bSuccess = TRUE;
3408 return bSuccess;
3411 /***********************************************************************
3412 * FTP_ParseNextFile (internal)
3414 * Parse the next line in file listing
3416 * RETURNS
3417 * TRUE on success
3418 * FALSE on failure
3420 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3422 static const char szSpace[] = " \t";
3423 DWORD nBufLen;
3424 char *pszLine;
3425 char *pszToken;
3426 char *pszTmp;
3427 BOOL found = FALSE;
3428 int i;
3430 lpfp->lpszName = NULL;
3431 do {
3432 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3433 return FALSE;
3435 pszToken = strtok(pszLine, szSpace);
3436 /* ls format
3437 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3439 * For instance:
3440 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3442 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3443 if(!FTP_ParsePermission(pszToken, lpfp))
3444 lpfp->bIsDirectory = FALSE;
3445 for(i=0; i<=3; i++) {
3446 if(!(pszToken = strtok(NULL, szSpace)))
3447 break;
3449 if(!pszToken) continue;
3450 if(lpfp->bIsDirectory) {
3451 TRACE("Is directory\n");
3452 lpfp->nSize = 0;
3454 else {
3455 TRACE("Size: %s\n", pszToken);
3456 lpfp->nSize = atol(pszToken);
3459 lpfp->tmLastModified.wSecond = 0;
3460 lpfp->tmLastModified.wMinute = 0;
3461 lpfp->tmLastModified.wHour = 0;
3462 lpfp->tmLastModified.wDay = 0;
3463 lpfp->tmLastModified.wMonth = 0;
3464 lpfp->tmLastModified.wYear = 0;
3466 /* Determine month */
3467 pszToken = strtok(NULL, szSpace);
3468 if(!pszToken) continue;
3469 if(strlen(pszToken) >= 3) {
3470 pszToken[3] = 0;
3471 if((pszTmp = StrStrIA(szMonths, pszToken)))
3472 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3474 /* Determine day */
3475 pszToken = strtok(NULL, szSpace);
3476 if(!pszToken) continue;
3477 lpfp->tmLastModified.wDay = atoi(pszToken);
3478 /* Determine time or year */
3479 pszToken = strtok(NULL, szSpace);
3480 if(!pszToken) continue;
3481 if((pszTmp = strchr(pszToken, ':'))) {
3482 SYSTEMTIME curr_time;
3483 *pszTmp = 0;
3484 pszTmp++;
3485 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3486 lpfp->tmLastModified.wHour = atoi(pszToken);
3487 GetLocalTime( &curr_time );
3488 lpfp->tmLastModified.wYear = curr_time.wYear;
3490 else {
3491 lpfp->tmLastModified.wYear = atoi(pszToken);
3492 lpfp->tmLastModified.wHour = 12;
3494 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3495 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3496 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3498 pszToken = strtok(NULL, szSpace);
3499 if(!pszToken) continue;
3500 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3501 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3503 /* NT way of parsing ... :
3505 07-13-03 08:55PM <DIR> sakpatch
3506 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3508 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3509 int mon, mday, year, hour, min;
3510 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3512 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3513 lpfp->tmLastModified.wDay = mday;
3514 lpfp->tmLastModified.wMonth = mon;
3515 lpfp->tmLastModified.wYear = year;
3517 /* Hacky and bad Y2K protection :-) */
3518 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3520 pszToken = strtok(NULL, szSpace);
3521 if(!pszToken) continue;
3522 sscanf(pszToken, "%d:%d", &hour, &min);
3523 lpfp->tmLastModified.wHour = hour;
3524 lpfp->tmLastModified.wMinute = min;
3525 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3526 lpfp->tmLastModified.wHour += 12;
3528 lpfp->tmLastModified.wSecond = 0;
3530 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3531 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3532 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3534 pszToken = strtok(NULL, szSpace);
3535 if(!pszToken) continue;
3536 if(!strcasecmp(pszToken, "<DIR>")) {
3537 lpfp->bIsDirectory = TRUE;
3538 lpfp->nSize = 0;
3539 TRACE("Is directory\n");
3541 else {
3542 lpfp->bIsDirectory = FALSE;
3543 lpfp->nSize = atol(pszToken);
3544 TRACE("Size: %d\n", lpfp->nSize);
3547 pszToken = strtok(NULL, szSpace);
3548 if(!pszToken) continue;
3549 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3550 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3552 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3553 else if(pszToken[0] == '+') {
3554 FIXME("EPLF Format not implemented\n");
3557 if(lpfp->lpszName) {
3558 if((lpszSearchFile == NULL) ||
3559 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3560 found = TRUE;
3561 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3563 else {
3564 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3565 lpfp->lpszName = NULL;
3568 } while(!found);
3569 return TRUE;
3572 /***********************************************************************
3573 * FTP_ParseDirectory (internal)
3575 * Parse string of directory information
3577 * RETURNS
3578 * TRUE on success
3579 * FALSE on failure
3581 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3582 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3584 BOOL bSuccess = TRUE;
3585 INT sizeFilePropArray = 500;/*20; */
3586 INT indexFilePropArray = -1;
3588 TRACE("\n");
3590 /* Allocate initial file properties array */
3591 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3592 if (!*lpafp)
3593 return FALSE;
3595 do {
3596 if (indexFilePropArray+1 >= sizeFilePropArray)
3598 LPFILEPROPERTIESW tmpafp;
3600 sizeFilePropArray *= 2;
3601 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3602 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3603 if (NULL == tmpafp)
3605 bSuccess = FALSE;
3606 break;
3609 *lpafp = tmpafp;
3611 indexFilePropArray++;
3612 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3614 if (bSuccess && indexFilePropArray)
3616 if (indexFilePropArray < sizeFilePropArray - 1)
3618 LPFILEPROPERTIESW tmpafp;
3620 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3621 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3622 if (NULL != tmpafp)
3623 *lpafp = tmpafp;
3625 *dwfp = indexFilePropArray;
3627 else
3629 HeapFree(GetProcessHeap(), 0, *lpafp);
3630 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3631 bSuccess = FALSE;
3634 return bSuccess;
3638 /***********************************************************************
3639 * FTP_ParsePermission (internal)
3641 * Parse permission string of directory information
3643 * RETURNS
3644 * TRUE on success
3645 * FALSE on failure
3648 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3650 BOOL bSuccess = TRUE;
3651 unsigned short nPermission = 0;
3652 INT nPos = 1;
3653 INT nLast = 9;
3655 TRACE("\n");
3656 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3658 bSuccess = FALSE;
3659 return bSuccess;
3662 lpfp->bIsDirectory = (*lpszPermission == 'd');
3665 switch (nPos)
3667 case 1:
3668 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3669 break;
3670 case 2:
3671 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3672 break;
3673 case 3:
3674 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3675 break;
3676 case 4:
3677 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3678 break;
3679 case 5:
3680 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3681 break;
3682 case 6:
3683 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3684 break;
3685 case 7:
3686 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3687 break;
3688 case 8:
3689 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3690 break;
3691 case 9:
3692 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3693 break;
3695 nPos++;
3696 }while (nPos <= nLast);
3698 lpfp->permissions = nPermission;
3699 return bSuccess;
3703 /***********************************************************************
3704 * FTP_SetResponseError (internal)
3706 * Set the appropriate error code for a given response from the server
3708 * RETURNS
3711 static DWORD FTP_SetResponseError(DWORD dwResponse)
3713 DWORD dwCode = 0;
3715 switch(dwResponse)
3717 case 425: /* Cannot open data connection. */
3718 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3719 break;
3721 case 426: /* Connection closed, transer aborted. */
3722 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3723 break;
3725 case 530: /* Not logged in. Login incorrect. */
3726 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3727 break;
3729 case 421: /* Service not available - Server may be shutting down. */
3730 case 450: /* File action not taken. File may be busy. */
3731 case 451: /* Action aborted. Server error. */
3732 case 452: /* Action not taken. Insufficient storage space on server. */
3733 case 500: /* Syntax error. Command unrecognized. */
3734 case 501: /* Syntax error. Error in parameters or arguments. */
3735 case 502: /* Command not implemented. */
3736 case 503: /* Bad sequence of commands. */
3737 case 504: /* Command not implemented for that parameter. */
3738 case 532: /* Need account for storing files */
3739 case 550: /* File action not taken. File not found or no access. */
3740 case 551: /* Requested action aborted. Page type unknown */
3741 case 552: /* Action aborted. Exceeded storage allocation */
3742 case 553: /* Action not taken. File name not allowed. */
3744 default:
3745 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3746 break;
3749 INTERNET_SetLastError(dwCode);
3750 return dwCode;