kernel32: Change all functions to use CDECL.
[wine/wine64.git] / dlls / wininet / ftp.c
blobd5bf13b3c596924d0bbbd7b5273d148b4225be0b
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)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)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 FTPFILE_WriteFile,
1271 NULL,
1272 NULL
1275 /***********************************************************************
1276 * FTP_FtpOpenFileW (Internal)
1278 * Open a remote file for writing or reading
1280 * RETURNS
1281 * HINTERNET handle on success
1282 * NULL on failure
1285 HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
1286 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1287 DWORD_PTR dwContext)
1289 INT nDataSocket;
1290 BOOL bSuccess = FALSE;
1291 LPWININETFTPFILE lpwh = NULL;
1292 LPWININETAPPINFOW hIC = NULL;
1293 HINTERNET handle = NULL;
1295 TRACE("\n");
1297 /* Clear any error information */
1298 INTERNET_SetLastError(0);
1300 if (GENERIC_READ == fdwAccess)
1302 /* Set up socket to retrieve data */
1303 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1305 else if (GENERIC_WRITE == fdwAccess)
1307 /* Set up socket to send data */
1308 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1311 /* Get data socket to server */
1312 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1314 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPFILE));
1315 lpwh->hdr.htype = WH_HFILE;
1316 lpwh->hdr.vtbl = &FTPFILEVtbl;
1317 lpwh->hdr.dwFlags = dwFlags;
1318 lpwh->hdr.dwContext = dwContext;
1319 lpwh->hdr.refs = 1;
1320 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1321 lpwh->nDataSocket = nDataSocket;
1322 lpwh->session_deleted = FALSE;
1324 WININET_AddRef( &lpwfs->hdr );
1325 lpwh->lpFtpSession = lpwfs;
1326 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1328 handle = WININET_AllocHandle( &lpwh->hdr );
1329 if( !handle )
1330 goto lend;
1332 /* Indicate that a download is currently in progress */
1333 lpwfs->download_in_progress = lpwh;
1336 if (lpwfs->lstnSocket != -1)
1337 closesocket(lpwfs->lstnSocket);
1339 hIC = lpwfs->lpAppInfo;
1340 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1342 INTERNET_ASYNC_RESULT iar;
1344 if (lpwh)
1346 iar.dwResult = (DWORD)handle;
1347 iar.dwError = ERROR_SUCCESS;
1348 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1349 &iar, sizeof(INTERNET_ASYNC_RESULT));
1352 iar.dwResult = (DWORD)bSuccess;
1353 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1354 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1355 &iar, sizeof(INTERNET_ASYNC_RESULT));
1358 lend:
1359 if( lpwh )
1360 WININET_Release( &lpwh->hdr );
1362 return handle;
1366 /***********************************************************************
1367 * FtpGetFileA (WININET.@)
1369 * Retrieve file from the FTP server
1371 * RETURNS
1372 * TRUE on success
1373 * FALSE on failure
1376 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1377 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1378 DWORD_PTR dwContext)
1380 LPWSTR lpwzRemoteFile;
1381 LPWSTR lpwzNewFile;
1382 BOOL ret;
1384 lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
1385 lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
1386 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1387 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1388 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1389 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1390 return ret;
1394 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1396 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1397 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1399 TRACE("%p\n", lpwfs);
1401 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1402 req->lpszNewFile, req->fFailIfExists,
1403 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1404 HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1405 HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1409 /***********************************************************************
1410 * FtpGetFileW (WININET.@)
1412 * Retrieve file from the FTP server
1414 * RETURNS
1415 * TRUE on success
1416 * FALSE on failure
1419 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1420 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1421 DWORD_PTR dwContext)
1423 LPWININETFTPSESSIONW lpwfs;
1424 LPWININETAPPINFOW hIC = NULL;
1425 BOOL r = FALSE;
1427 if (!lpszRemoteFile || !lpszNewFile)
1429 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1430 return FALSE;
1433 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
1434 if (!lpwfs)
1436 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1437 return FALSE;
1440 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1442 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1443 goto lend;
1446 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1448 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1449 goto lend;
1452 if (lpwfs->download_in_progress != NULL)
1454 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1455 goto lend;
1458 hIC = lpwfs->lpAppInfo;
1459 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1461 WORKREQUEST workRequest;
1462 struct WORKREQ_FTPGETFILEW *req;
1464 workRequest.asyncproc = AsyncFtpGetFileProc;
1465 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1466 req = &workRequest.u.FtpGetFileW;
1467 req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
1468 req->lpszNewFile = WININET_strdupW(lpszNewFile);
1469 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1470 req->fFailIfExists = fFailIfExists;
1471 req->dwFlags = dwInternetFlags;
1472 req->dwContext = dwContext;
1474 r = INTERNET_AsyncCall(&workRequest);
1476 else
1478 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1479 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1482 lend:
1483 WININET_Release( &lpwfs->hdr );
1485 return r;
1489 /***********************************************************************
1490 * FTP_FtpGetFileW (Internal)
1492 * Retrieve file from the FTP server
1494 * RETURNS
1495 * TRUE on success
1496 * FALSE on failure
1499 static BOOL FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1500 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1501 DWORD_PTR dwContext)
1503 BOOL bSuccess = FALSE;
1504 HANDLE hFile;
1505 LPWININETAPPINFOW hIC = NULL;
1507 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1509 /* Clear any error information */
1510 INTERNET_SetLastError(0);
1512 /* Ensure we can write to lpszNewfile by opening it */
1513 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1514 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1515 if (INVALID_HANDLE_VALUE == hFile)
1516 return FALSE;
1518 /* Set up socket to retrieve data */
1519 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1521 INT nDataSocket;
1523 /* Get data socket to server */
1524 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1526 INT nResCode;
1528 /* Receive data */
1529 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1530 closesocket(nDataSocket);
1532 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1533 if (nResCode)
1535 if (nResCode == 226)
1536 bSuccess = TRUE;
1537 else
1538 FTP_SetResponseError(nResCode);
1543 if (lpwfs->lstnSocket != -1)
1544 closesocket(lpwfs->lstnSocket);
1546 CloseHandle(hFile);
1548 hIC = lpwfs->lpAppInfo;
1549 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1551 INTERNET_ASYNC_RESULT iar;
1553 iar.dwResult = (DWORD)bSuccess;
1554 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1555 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1556 &iar, sizeof(INTERNET_ASYNC_RESULT));
1559 if (!bSuccess) DeleteFileW(lpszNewFile);
1560 return bSuccess;
1563 /***********************************************************************
1564 * FtpGetFileSize (WININET.@)
1566 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1568 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1570 if (lpdwFileSizeHigh)
1571 *lpdwFileSizeHigh = 0;
1573 return 0;
1576 /***********************************************************************
1577 * FtpDeleteFileA (WININET.@)
1579 * Delete a file on the ftp server
1581 * RETURNS
1582 * TRUE on success
1583 * FALSE on failure
1586 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1588 LPWSTR lpwzFileName;
1589 BOOL ret;
1591 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1592 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1593 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1594 return ret;
1597 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1599 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1600 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1602 TRACE("%p\n", lpwfs);
1604 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1605 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1608 /***********************************************************************
1609 * FtpDeleteFileW (WININET.@)
1611 * Delete a file on the ftp server
1613 * RETURNS
1614 * TRUE on success
1615 * FALSE on failure
1618 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1620 LPWININETFTPSESSIONW lpwfs;
1621 LPWININETAPPINFOW hIC = NULL;
1622 BOOL r = FALSE;
1624 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1625 if (!lpwfs)
1627 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1628 return FALSE;
1631 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1633 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1634 goto lend;
1637 if (lpwfs->download_in_progress != NULL)
1639 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1640 goto lend;
1643 if (!lpszFileName)
1645 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1646 goto lend;
1649 hIC = lpwfs->lpAppInfo;
1650 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1652 WORKREQUEST workRequest;
1653 struct WORKREQ_FTPDELETEFILEW *req;
1655 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1656 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1657 req = &workRequest.u.FtpDeleteFileW;
1658 req->lpszFilename = WININET_strdupW(lpszFileName);
1660 r = INTERNET_AsyncCall(&workRequest);
1662 else
1664 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1667 lend:
1668 WININET_Release( &lpwfs->hdr );
1670 return r;
1673 /***********************************************************************
1674 * FTP_FtpDeleteFileW (Internal)
1676 * Delete a file on the ftp server
1678 * RETURNS
1679 * TRUE on success
1680 * FALSE on failure
1683 BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName)
1685 INT nResCode;
1686 BOOL bSuccess = FALSE;
1687 LPWININETAPPINFOW hIC = NULL;
1689 TRACE("%p\n", lpwfs);
1691 /* Clear any error information */
1692 INTERNET_SetLastError(0);
1694 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1695 goto lend;
1697 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1698 if (nResCode)
1700 if (nResCode == 250)
1701 bSuccess = TRUE;
1702 else
1703 FTP_SetResponseError(nResCode);
1705 lend:
1706 hIC = lpwfs->lpAppInfo;
1707 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1709 INTERNET_ASYNC_RESULT iar;
1711 iar.dwResult = (DWORD)bSuccess;
1712 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1713 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1714 &iar, sizeof(INTERNET_ASYNC_RESULT));
1717 return bSuccess;
1721 /***********************************************************************
1722 * FtpRemoveDirectoryA (WININET.@)
1724 * Remove a directory on the ftp server
1726 * RETURNS
1727 * TRUE on success
1728 * FALSE on failure
1731 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1733 LPWSTR lpwzDirectory;
1734 BOOL ret;
1736 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
1737 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1738 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1739 return ret;
1742 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1744 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1745 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1747 TRACE("%p\n", lpwfs);
1749 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1750 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1753 /***********************************************************************
1754 * FtpRemoveDirectoryW (WININET.@)
1756 * Remove a directory on the ftp server
1758 * RETURNS
1759 * TRUE on success
1760 * FALSE on failure
1763 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1765 LPWININETFTPSESSIONW lpwfs;
1766 LPWININETAPPINFOW hIC = NULL;
1767 BOOL r = FALSE;
1769 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1770 if (!lpwfs)
1772 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1773 return FALSE;
1776 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1778 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1779 goto lend;
1782 if (lpwfs->download_in_progress != NULL)
1784 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1785 goto lend;
1788 if (!lpszDirectory)
1790 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1791 goto lend;
1794 hIC = lpwfs->lpAppInfo;
1795 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1797 WORKREQUEST workRequest;
1798 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1800 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1801 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1802 req = &workRequest.u.FtpRemoveDirectoryW;
1803 req->lpszDirectory = WININET_strdupW(lpszDirectory);
1805 r = INTERNET_AsyncCall(&workRequest);
1807 else
1809 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1812 lend:
1813 WININET_Release( &lpwfs->hdr );
1815 return r;
1818 /***********************************************************************
1819 * FTP_FtpRemoveDirectoryW (Internal)
1821 * Remove a directory on the ftp server
1823 * RETURNS
1824 * TRUE on success
1825 * FALSE on failure
1828 BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
1830 INT nResCode;
1831 BOOL bSuccess = FALSE;
1832 LPWININETAPPINFOW hIC = NULL;
1834 TRACE("\n");
1836 /* Clear any error information */
1837 INTERNET_SetLastError(0);
1839 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1840 goto lend;
1842 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1843 if (nResCode)
1845 if (nResCode == 250)
1846 bSuccess = TRUE;
1847 else
1848 FTP_SetResponseError(nResCode);
1851 lend:
1852 hIC = lpwfs->lpAppInfo;
1853 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1855 INTERNET_ASYNC_RESULT iar;
1857 iar.dwResult = (DWORD)bSuccess;
1858 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1859 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1860 &iar, sizeof(INTERNET_ASYNC_RESULT));
1863 return bSuccess;
1867 /***********************************************************************
1868 * FtpRenameFileA (WININET.@)
1870 * Rename a file on the ftp server
1872 * RETURNS
1873 * TRUE on success
1874 * FALSE on failure
1877 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1879 LPWSTR lpwzSrc;
1880 LPWSTR lpwzDest;
1881 BOOL ret;
1883 lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL;
1884 lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL;
1885 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1886 HeapFree(GetProcessHeap(), 0, lpwzSrc);
1887 HeapFree(GetProcessHeap(), 0, lpwzDest);
1888 return ret;
1891 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
1893 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
1894 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1896 TRACE("%p\n", lpwfs);
1898 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
1899 HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
1900 HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
1903 /***********************************************************************
1904 * FtpRenameFileW (WININET.@)
1906 * Rename a file on the ftp server
1908 * RETURNS
1909 * TRUE on success
1910 * FALSE on failure
1913 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
1915 LPWININETFTPSESSIONW lpwfs;
1916 LPWININETAPPINFOW hIC = NULL;
1917 BOOL r = FALSE;
1919 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1920 if (!lpwfs)
1922 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1923 return FALSE;
1926 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1928 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1929 goto lend;
1932 if (lpwfs->download_in_progress != NULL)
1934 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1935 goto lend;
1938 if (!lpszSrc || !lpszDest)
1940 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1941 goto lend;
1944 hIC = lpwfs->lpAppInfo;
1945 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1947 WORKREQUEST workRequest;
1948 struct WORKREQ_FTPRENAMEFILEW *req;
1950 workRequest.asyncproc = AsyncFtpRenameFileProc;
1951 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1952 req = &workRequest.u.FtpRenameFileW;
1953 req->lpszSrcFile = WININET_strdupW(lpszSrc);
1954 req->lpszDestFile = WININET_strdupW(lpszDest);
1956 r = INTERNET_AsyncCall(&workRequest);
1958 else
1960 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
1963 lend:
1964 WININET_Release( &lpwfs->hdr );
1966 return r;
1969 /***********************************************************************
1970 * FTP_FtpRenameFileW (Internal)
1972 * Rename a file on the ftp server
1974 * RETURNS
1975 * TRUE on success
1976 * FALSE on failure
1979 BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs,
1980 LPCWSTR lpszSrc, LPCWSTR lpszDest)
1982 INT nResCode;
1983 BOOL bSuccess = FALSE;
1984 LPWININETAPPINFOW hIC = NULL;
1986 TRACE("\n");
1988 /* Clear any error information */
1989 INTERNET_SetLastError(0);
1991 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
1992 goto lend;
1994 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1995 if (nResCode == 350)
1997 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
1998 goto lend;
2000 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2003 if (nResCode == 250)
2004 bSuccess = TRUE;
2005 else
2006 FTP_SetResponseError(nResCode);
2008 lend:
2009 hIC = lpwfs->lpAppInfo;
2010 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2012 INTERNET_ASYNC_RESULT iar;
2014 iar.dwResult = (DWORD)bSuccess;
2015 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2016 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2017 &iar, sizeof(INTERNET_ASYNC_RESULT));
2020 return bSuccess;
2023 /***********************************************************************
2024 * FtpCommandA (WININET.@)
2026 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2027 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2029 BOOL r;
2030 WCHAR *cmdW;
2032 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2033 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2035 if (fExpectResponse)
2037 FIXME("data connection not supported\n");
2038 return FALSE;
2041 if (!lpszCommand || !lpszCommand[0])
2043 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2044 return FALSE;
2047 if (!(cmdW = WININET_strdup_AtoW(lpszCommand)))
2049 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2050 return FALSE;
2053 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2055 HeapFree(GetProcessHeap(), 0, cmdW);
2056 return r;
2059 /***********************************************************************
2060 * FtpCommandW (WININET.@)
2062 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2063 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2065 BOOL r = FALSE;
2066 LPWININETFTPSESSIONW lpwfs;
2067 LPSTR cmd = NULL;
2068 DWORD len, nBytesSent= 0;
2069 INT nResCode, nRC = 0;
2071 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2072 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2074 if (!lpszCommand || !lpszCommand[0])
2076 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2077 return FALSE;
2080 if (fExpectResponse)
2082 FIXME("data connection not supported\n");
2083 return FALSE;
2086 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
2087 if (!lpwfs)
2089 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2090 return FALSE;
2093 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2095 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2096 goto lend;
2099 if (lpwfs->download_in_progress != NULL)
2101 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2102 goto lend;
2105 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2106 if ((cmd = HeapAlloc(GetProcessHeap(), 0, len )))
2107 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2108 else
2110 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2111 goto lend;
2114 strcat(cmd, szCRLF);
2115 len--;
2117 TRACE("Sending (%s) len(%d)\n", cmd, len);
2118 while ((nBytesSent < len) && (nRC != -1))
2120 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2121 if (nRC != -1)
2123 nBytesSent += nRC;
2124 TRACE("Sent %d bytes\n", nRC);
2128 if (nBytesSent)
2130 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2131 if (nResCode > 0 && nResCode < 400)
2132 r = TRUE;
2133 else
2134 FTP_SetResponseError(nResCode);
2137 lend:
2138 WININET_Release( &lpwfs->hdr );
2139 HeapFree(GetProcessHeap(), 0, cmd);
2140 return r;
2144 /***********************************************************************
2145 * FTPSESSION_Destroy (internal)
2147 * Deallocate session handle
2149 static void FTPSESSION_Destroy(WININETHANDLEHEADER *hdr)
2151 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2153 TRACE("\n");
2155 WININET_Release(&lpwfs->lpAppInfo->hdr);
2157 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2158 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2159 HeapFree(GetProcessHeap(), 0, lpwfs);
2162 static void FTPSESSION_CloseConnection(WININETHANDLEHEADER *hdr)
2164 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2166 TRACE("\n");
2168 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2169 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2171 if (lpwfs->download_in_progress != NULL)
2172 lpwfs->download_in_progress->session_deleted = TRUE;
2174 if (lpwfs->sndSocket != -1)
2175 closesocket(lpwfs->sndSocket);
2177 if (lpwfs->lstnSocket != -1)
2178 closesocket(lpwfs->lstnSocket);
2180 if (lpwfs->pasvSocket != -1)
2181 closesocket(lpwfs->pasvSocket);
2183 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2184 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2187 static DWORD FTPSESSION_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2189 switch(option) {
2190 case INTERNET_OPTION_HANDLE_TYPE:
2191 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2193 if (*size < sizeof(ULONG))
2194 return ERROR_INSUFFICIENT_BUFFER;
2196 *size = sizeof(DWORD);
2197 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2198 return ERROR_SUCCESS;
2201 return INET_QueryOption(option, buffer, size, unicode);
2204 static const HANDLEHEADERVtbl FTPSESSIONVtbl = {
2205 FTPSESSION_Destroy,
2206 FTPSESSION_CloseConnection,
2207 FTPSESSION_QueryOption,
2208 NULL,
2209 NULL,
2210 NULL,
2211 NULL,
2212 NULL,
2213 NULL
2217 /***********************************************************************
2218 * FTP_Connect (internal)
2220 * Connect to a ftp server
2222 * RETURNS
2223 * HINTERNET a session handle on success
2224 * NULL on failure
2226 * NOTES:
2228 * Windows uses 'anonymous' as the username, when given a NULL username
2229 * and a NULL password. The password is first looked up in:
2231 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2233 * If this entry is not present it uses the current username as the password.
2237 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
2238 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2239 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2240 DWORD dwInternalFlags)
2242 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2243 'M','i','c','r','o','s','o','f','t','\\',
2244 'W','i','n','d','o','w','s','\\',
2245 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2246 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2247 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2248 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2249 static const WCHAR szEmpty[] = {'\0'};
2250 struct sockaddr_in socketAddr;
2251 INT nsocket = -1;
2252 UINT sock_namelen;
2253 BOOL bSuccess = FALSE;
2254 LPWININETFTPSESSIONW lpwfs = NULL;
2255 HINTERNET handle = NULL;
2257 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2258 hIC, debugstr_w(lpszServerName),
2259 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2261 assert( hIC->hdr.htype == WH_HINIT );
2263 if ((!lpszUserName || !strlenW(lpszUserName)) && lpszPassword)
2265 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2266 goto lerror;
2269 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
2270 if (NULL == lpwfs)
2272 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2273 goto lerror;
2276 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2277 nServerPort = INTERNET_DEFAULT_FTP_PORT;
2279 lpwfs->hdr.htype = WH_HFTPSESSION;
2280 lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
2281 lpwfs->hdr.dwFlags = dwFlags;
2282 lpwfs->hdr.dwContext = dwContext;
2283 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2284 lpwfs->hdr.refs = 1;
2285 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2286 lpwfs->download_in_progress = NULL;
2287 lpwfs->sndSocket = -1;
2288 lpwfs->lstnSocket = -1;
2289 lpwfs->pasvSocket = -1;
2291 WININET_AddRef( &hIC->hdr );
2292 lpwfs->lpAppInfo = hIC;
2293 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2295 handle = WININET_AllocHandle( &lpwfs->hdr );
2296 if( !handle )
2298 ERR("Failed to alloc handle\n");
2299 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2300 goto lerror;
2303 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2304 if(strchrW(hIC->lpszProxy, ' '))
2305 FIXME("Several proxies not implemented.\n");
2306 if(hIC->lpszProxyBypass)
2307 FIXME("Proxy bypass is ignored.\n");
2309 if (!lpszUserName || !strlenW(lpszUserName)) {
2310 HKEY key;
2311 WCHAR szPassword[MAX_PATH];
2312 DWORD len = sizeof(szPassword);
2314 lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
2316 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2317 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2318 /* Nothing in the registry, get the username and use that as the password */
2319 if (!GetUserNameW(szPassword, &len)) {
2320 /* Should never get here, but use an empty password as failsafe */
2321 strcpyW(szPassword, szEmpty);
2324 RegCloseKey(key);
2326 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2327 lpwfs->lpszPassword = WININET_strdupW(szPassword);
2329 else {
2330 lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
2332 if (lpszPassword)
2333 lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
2334 else
2335 lpwfs->lpszPassword = WININET_strdupW(szEmpty);
2338 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2339 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2341 INTERNET_ASYNC_RESULT iar;
2343 iar.dwResult = (DWORD)handle;
2344 iar.dwError = ERROR_SUCCESS;
2346 SendAsyncCallback(&hIC->hdr, dwContext,
2347 INTERNET_STATUS_HANDLE_CREATED, &iar,
2348 sizeof(INTERNET_ASYNC_RESULT));
2351 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2352 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2354 if (!GetAddress(lpszServerName, nServerPort, &socketAddr))
2356 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2357 goto lerror;
2360 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2361 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2363 nsocket = socket(AF_INET,SOCK_STREAM,0);
2364 if (nsocket == -1)
2366 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2367 goto lerror;
2370 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2371 &socketAddr, sizeof(struct sockaddr_in));
2373 if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
2375 ERR("Unable to connect (%s)\n", strerror(errno));
2376 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2378 else
2380 TRACE("Connected to server\n");
2381 lpwfs->sndSocket = nsocket;
2382 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2383 &socketAddr, sizeof(struct sockaddr_in));
2385 sock_namelen = sizeof(lpwfs->socketAddress);
2386 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2388 if (FTP_ConnectToHost(lpwfs))
2390 TRACE("Successfully logged into server\n");
2391 bSuccess = TRUE;
2395 lerror:
2396 if (lpwfs) WININET_Release( &lpwfs->hdr );
2398 if (!bSuccess && handle)
2400 WININET_FreeHandle( handle );
2401 handle = NULL;
2404 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2406 INTERNET_ASYNC_RESULT iar;
2408 iar.dwResult = bSuccess ? (DWORD_PTR)lpwfs : 0;
2409 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2410 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2411 &iar, sizeof(INTERNET_ASYNC_RESULT));
2414 return handle;
2418 /***********************************************************************
2419 * FTP_ConnectToHost (internal)
2421 * Connect to a ftp server
2423 * RETURNS
2424 * TRUE on success
2425 * NULL on failure
2428 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
2430 INT nResCode;
2431 BOOL bSuccess = FALSE;
2433 TRACE("\n");
2434 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2436 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2437 goto lend;
2439 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2440 if (nResCode)
2442 /* Login successful... */
2443 if (nResCode == 230)
2444 bSuccess = TRUE;
2445 /* User name okay, need password... */
2446 else if (nResCode == 331)
2447 bSuccess = FTP_SendPassword(lpwfs);
2448 /* Need account for login... */
2449 else if (nResCode == 332)
2450 bSuccess = FTP_SendAccount(lpwfs);
2451 else
2452 FTP_SetResponseError(nResCode);
2455 TRACE("Returning %d\n", bSuccess);
2456 lend:
2457 return bSuccess;
2461 /***********************************************************************
2462 * FTP_SendCommandA (internal)
2464 * Send command to server
2466 * RETURNS
2467 * TRUE on success
2468 * NULL on failure
2471 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2472 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
2474 DWORD len;
2475 CHAR *buf;
2476 DWORD nBytesSent = 0;
2477 int nRC = 0;
2478 DWORD dwParamLen;
2480 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2482 if (lpfnStatusCB)
2484 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2487 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2488 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2489 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2491 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2492 return FALSE;
2494 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2495 dwParamLen ? lpszParam : "", szCRLF);
2497 TRACE("Sending (%s) len(%d)\n", buf, len);
2498 while((nBytesSent < len) && (nRC != -1))
2500 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2501 nBytesSent += nRC;
2504 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2506 if (lpfnStatusCB)
2508 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2509 &nBytesSent, sizeof(DWORD));
2512 TRACE("Sent %d bytes\n", nBytesSent);
2513 return (nRC != -1);
2516 /***********************************************************************
2517 * FTP_SendCommand (internal)
2519 * Send command to server
2521 * RETURNS
2522 * TRUE on success
2523 * NULL on failure
2526 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2527 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
2529 BOOL ret;
2530 LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
2531 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2532 HeapFree(GetProcessHeap(), 0, lpszParamA);
2533 return ret;
2536 /***********************************************************************
2537 * FTP_ReceiveResponse (internal)
2539 * Receive response from server
2541 * RETURNS
2542 * Reply code on success
2543 * 0 on failure
2546 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD_PTR dwContext)
2548 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2549 DWORD nRecv;
2550 INT rc = 0;
2551 char firstprefix[5];
2552 BOOL multiline = FALSE;
2554 TRACE("socket(%d)\n", lpwfs->sndSocket);
2556 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2558 while(1)
2560 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2561 goto lerror;
2563 if (nRecv >= 3)
2565 if(!multiline)
2567 if(lpszResponse[3] != '-')
2568 break;
2569 else
2570 { /* Start of multiline response. Loop until we get "nnn " */
2571 multiline = TRUE;
2572 memcpy(firstprefix, lpszResponse, 3);
2573 firstprefix[3] = ' ';
2574 firstprefix[4] = '\0';
2577 else
2579 if(!memcmp(firstprefix, lpszResponse, 4))
2580 break;
2585 if (nRecv >= 3)
2587 rc = atoi(lpszResponse);
2589 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2590 &nRecv, sizeof(DWORD));
2593 lerror:
2594 TRACE("return %d\n", rc);
2595 return rc;
2599 /***********************************************************************
2600 * FTP_SendPassword (internal)
2602 * Send password to ftp server
2604 * RETURNS
2605 * TRUE on success
2606 * NULL on failure
2609 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2611 INT nResCode;
2612 BOOL bSuccess = FALSE;
2614 TRACE("\n");
2615 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2616 goto lend;
2618 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2619 if (nResCode)
2621 TRACE("Received reply code %d\n", nResCode);
2622 /* Login successful... */
2623 if (nResCode == 230)
2624 bSuccess = TRUE;
2625 /* Command not implemented, superfluous at the server site... */
2626 /* Need account for login... */
2627 else if (nResCode == 332)
2628 bSuccess = FTP_SendAccount(lpwfs);
2629 else
2630 FTP_SetResponseError(nResCode);
2633 lend:
2634 TRACE("Returning %d\n", bSuccess);
2635 return bSuccess;
2639 /***********************************************************************
2640 * FTP_SendAccount (internal)
2644 * RETURNS
2645 * TRUE on success
2646 * FALSE on failure
2649 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2651 INT nResCode;
2652 BOOL bSuccess = FALSE;
2654 TRACE("\n");
2655 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2656 goto lend;
2658 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2659 if (nResCode)
2660 bSuccess = TRUE;
2661 else
2662 FTP_SetResponseError(nResCode);
2664 lend:
2665 return bSuccess;
2669 /***********************************************************************
2670 * FTP_SendStore (internal)
2672 * Send request to upload file to ftp server
2674 * RETURNS
2675 * TRUE on success
2676 * FALSE on failure
2679 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2681 INT nResCode;
2682 BOOL bSuccess = FALSE;
2684 TRACE("\n");
2685 if (!FTP_InitListenSocket(lpwfs))
2686 goto lend;
2688 if (!FTP_SendType(lpwfs, dwType))
2689 goto lend;
2691 if (!FTP_SendPortOrPasv(lpwfs))
2692 goto lend;
2694 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2695 goto lend;
2696 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2697 if (nResCode)
2699 if (nResCode == 150 || nResCode == 125)
2700 bSuccess = TRUE;
2701 else
2702 FTP_SetResponseError(nResCode);
2705 lend:
2706 if (!bSuccess && lpwfs->lstnSocket != -1)
2708 closesocket(lpwfs->lstnSocket);
2709 lpwfs->lstnSocket = -1;
2712 return bSuccess;
2716 /***********************************************************************
2717 * FTP_InitListenSocket (internal)
2719 * Create a socket to listen for server response
2721 * RETURNS
2722 * TRUE on success
2723 * FALSE on failure
2726 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2728 BOOL bSuccess = FALSE;
2729 socklen_t namelen = sizeof(struct sockaddr_in);
2731 TRACE("\n");
2733 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2734 if (lpwfs->lstnSocket == -1)
2736 TRACE("Unable to create listening socket\n");
2737 goto lend;
2740 /* We obtain our ip addr from the name of the command channel socket */
2741 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2743 /* and get the system to assign us a port */
2744 lpwfs->lstnSocketAddress.sin_port = htons(0);
2746 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2748 TRACE("Unable to bind socket\n");
2749 goto lend;
2752 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2754 TRACE("listen failed\n");
2755 goto lend;
2758 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2759 bSuccess = TRUE;
2761 lend:
2762 if (!bSuccess && lpwfs->lstnSocket != -1)
2764 closesocket(lpwfs->lstnSocket);
2765 lpwfs->lstnSocket = -1;
2768 return bSuccess;
2772 /***********************************************************************
2773 * FTP_SendType (internal)
2775 * Tell server type of data being transferred
2777 * RETURNS
2778 * TRUE on success
2779 * FALSE on failure
2781 * W98SE doesn't cache the type that's currently set
2782 * (i.e. it sends it always),
2783 * so we probably don't want to do that either.
2785 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2787 INT nResCode;
2788 WCHAR type[] = { 'I','\0' };
2789 BOOL bSuccess = FALSE;
2791 TRACE("\n");
2792 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2793 type[0] = 'A';
2795 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2796 goto lend;
2798 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2799 if (nResCode)
2801 if (nResCode == 2)
2802 bSuccess = TRUE;
2803 else
2804 FTP_SetResponseError(nResCode);
2807 lend:
2808 return bSuccess;
2812 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2813 /***********************************************************************
2814 * FTP_GetFileSize (internal)
2816 * Retrieves from the server the size of the given file
2818 * RETURNS
2819 * TRUE on success
2820 * FALSE on failure
2823 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2825 INT nResCode;
2826 BOOL bSuccess = FALSE;
2828 TRACE("\n");
2830 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2831 goto lend;
2833 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2834 if (nResCode)
2836 if (nResCode == 213) {
2837 /* Now parses the output to get the actual file size */
2838 int i;
2839 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2841 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2842 if (lpszResponseBuffer[i] == '\0') return FALSE;
2843 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2845 bSuccess = TRUE;
2846 } else {
2847 FTP_SetResponseError(nResCode);
2851 lend:
2852 return bSuccess;
2854 #endif
2857 /***********************************************************************
2858 * FTP_SendPort (internal)
2860 * Tell server which port to use
2862 * RETURNS
2863 * TRUE on success
2864 * FALSE on failure
2867 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2869 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2870 INT nResCode;
2871 WCHAR szIPAddress[64];
2872 BOOL bSuccess = FALSE;
2873 TRACE("\n");
2875 sprintfW(szIPAddress, szIPFormat,
2876 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2877 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2878 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2879 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2880 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2881 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2883 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2884 goto lend;
2886 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2887 if (nResCode)
2889 if (nResCode == 200)
2890 bSuccess = TRUE;
2891 else
2892 FTP_SetResponseError(nResCode);
2895 lend:
2896 return bSuccess;
2900 /***********************************************************************
2901 * FTP_DoPassive (internal)
2903 * Tell server that we want to do passive transfers
2904 * and connect data socket
2906 * RETURNS
2907 * TRUE on success
2908 * FALSE on failure
2911 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2913 INT nResCode;
2914 BOOL bSuccess = FALSE;
2916 TRACE("\n");
2917 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2918 goto lend;
2920 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2921 if (nResCode)
2923 if (nResCode == 227)
2925 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2926 LPSTR p;
2927 int f[6];
2928 int i;
2929 char *pAddr, *pPort;
2930 INT nsocket = -1;
2931 struct sockaddr_in dataSocketAddress;
2933 p = lpszResponseBuffer+4; /* skip status code */
2934 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
2936 if (*p == '\0')
2938 ERR("no address found in response, aborting\n");
2939 goto lend;
2942 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
2943 &f[4], &f[5]) != 6)
2945 ERR("unknown response address format '%s', aborting\n", p);
2946 goto lend;
2948 for (i=0; i < 6; i++)
2949 f[i] = f[i] & 0xff;
2951 dataSocketAddress = lpwfs->socketAddress;
2952 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2953 pPort = (char *)&(dataSocketAddress.sin_port);
2954 pAddr[0] = f[0];
2955 pAddr[1] = f[1];
2956 pAddr[2] = f[2];
2957 pAddr[3] = f[3];
2958 pPort[0] = f[4];
2959 pPort[1] = f[5];
2961 nsocket = socket(AF_INET,SOCK_STREAM,0);
2962 if (nsocket == -1)
2963 goto lend;
2965 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2967 ERR("can't connect passive FTP data port.\n");
2968 closesocket(nsocket);
2969 goto lend;
2971 lpwfs->pasvSocket = nsocket;
2972 bSuccess = TRUE;
2974 else
2975 FTP_SetResponseError(nResCode);
2978 lend:
2979 return bSuccess;
2983 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2985 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2987 if (!FTP_DoPassive(lpwfs))
2988 return FALSE;
2990 else
2992 if (!FTP_SendPort(lpwfs))
2993 return FALSE;
2995 return TRUE;
2999 /***********************************************************************
3000 * FTP_GetDataSocket (internal)
3002 * Either accepts an incoming data socket connection from the server
3003 * or just returns the already opened socket after a PASV command
3004 * in case of passive FTP.
3007 * RETURNS
3008 * TRUE on success
3009 * FALSE on failure
3012 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
3014 struct sockaddr_in saddr;
3015 socklen_t addrlen = sizeof(struct sockaddr);
3017 TRACE("\n");
3018 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3020 *nDataSocket = lpwfs->pasvSocket;
3022 else
3024 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3025 closesocket(lpwfs->lstnSocket);
3026 lpwfs->lstnSocket = -1;
3028 return *nDataSocket != -1;
3032 /***********************************************************************
3033 * FTP_SendData (internal)
3035 * Send data to the server
3037 * RETURNS
3038 * TRUE on success
3039 * FALSE on failure
3042 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
3044 BY_HANDLE_FILE_INFORMATION fi;
3045 DWORD nBytesRead = 0;
3046 DWORD nBytesSent = 0;
3047 DWORD nTotalSent = 0;
3048 DWORD nBytesToSend, nLen;
3049 int nRC = 1;
3050 time_t s_long_time, e_long_time;
3051 LONG nSeconds;
3052 CHAR *lpszBuffer;
3054 TRACE("\n");
3055 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3057 /* Get the size of the file. */
3058 GetFileInformationByHandle(hFile, &fi);
3059 time(&s_long_time);
3063 nBytesToSend = nBytesRead - nBytesSent;
3065 if (nBytesToSend <= 0)
3067 /* Read data from file. */
3068 nBytesSent = 0;
3069 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3070 ERR("Failed reading from file\n");
3072 if (nBytesRead > 0)
3073 nBytesToSend = nBytesRead;
3074 else
3075 break;
3078 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3079 DATA_PACKET_SIZE : nBytesToSend;
3080 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3082 if (nRC != -1)
3084 nBytesSent += nRC;
3085 nTotalSent += nRC;
3088 /* Do some computation to display the status. */
3089 time(&e_long_time);
3090 nSeconds = e_long_time - s_long_time;
3091 if( nSeconds / 60 > 0 )
3093 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3094 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3095 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3097 else
3099 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3100 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3101 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3103 } while (nRC != -1);
3105 TRACE("file transfer complete!\n");
3107 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3109 return nTotalSent;
3113 /***********************************************************************
3114 * FTP_SendRetrieve (internal)
3116 * Send request to retrieve a file
3118 * RETURNS
3119 * Number of bytes to be received on success
3120 * 0 on failure
3123 static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3125 INT nResCode;
3126 BOOL ret;
3128 TRACE("\n");
3129 if (!(ret = FTP_InitListenSocket(lpwfs)))
3130 goto lend;
3132 if (!(ret = FTP_SendType(lpwfs, dwType)))
3133 goto lend;
3135 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3136 goto lend;
3138 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3139 goto lend;
3141 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3142 if ((nResCode != 125) && (nResCode != 150)) {
3143 /* That means that we got an error getting the file. */
3144 FTP_SetResponseError(nResCode);
3145 ret = FALSE;
3148 lend:
3149 if (!ret && lpwfs->lstnSocket != -1)
3151 closesocket(lpwfs->lstnSocket);
3152 lpwfs->lstnSocket = -1;
3155 return ret;
3159 /***********************************************************************
3160 * FTP_RetrieveData (internal)
3162 * Retrieve data from server
3164 * RETURNS
3165 * TRUE on success
3166 * FALSE on failure
3169 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
3171 DWORD nBytesWritten;
3172 INT nRC = 0;
3173 CHAR *lpszBuffer;
3175 TRACE("\n");
3177 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3178 if (NULL == lpszBuffer)
3180 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3181 return FALSE;
3184 while (nRC != -1)
3186 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3187 if (nRC != -1)
3189 /* other side closed socket. */
3190 if (nRC == 0)
3191 goto recv_end;
3192 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3196 TRACE("Data transfer complete\n");
3198 recv_end:
3199 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3201 return (nRC != -1);
3204 /***********************************************************************
3205 * FTPFINDNEXT_Destroy (internal)
3207 * Deallocate session handle
3209 static void FTPFINDNEXT_Destroy(WININETHANDLEHEADER *hdr)
3211 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3212 DWORD i;
3214 TRACE("\n");
3216 WININET_Release(&lpwfn->lpFtpSession->hdr);
3218 for (i = 0; i < lpwfn->size; i++)
3220 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3223 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3224 HeapFree(GetProcessHeap(), 0, lpwfn);
3227 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3229 WIN32_FIND_DATAW *find_data = data;
3230 DWORD res = ERROR_SUCCESS;
3232 TRACE("index(%d) size(%d)\n", find->index, find->size);
3234 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3236 if (find->index < find->size) {
3237 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3238 find->index++;
3240 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3241 }else {
3242 res = ERROR_NO_MORE_FILES;
3245 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3247 INTERNET_ASYNC_RESULT iar;
3249 iar.dwResult = (res == ERROR_SUCCESS);
3250 iar.dwError = res;
3252 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3253 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3254 sizeof(INTERNET_ASYNC_RESULT));
3257 return res;
3260 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3262 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3264 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3267 static DWORD FTPFINDNEXT_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3269 switch(option) {
3270 case INTERNET_OPTION_HANDLE_TYPE:
3271 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3273 if (*size < sizeof(ULONG))
3274 return ERROR_INSUFFICIENT_BUFFER;
3276 *size = sizeof(DWORD);
3277 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3278 return ERROR_SUCCESS;
3281 return INET_QueryOption(option, buffer, size, unicode);
3284 static DWORD FTPFINDNEXT_FindNextFileW(WININETHANDLEHEADER *hdr, void *data)
3286 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3288 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3290 WORKREQUEST workRequest;
3291 struct WORKREQ_FTPFINDNEXTW *req;
3293 workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3294 workRequest.hdr = WININET_AddRef( &find->hdr );
3295 req = &workRequest.u.FtpFindNextW;
3296 req->lpFindFileData = data;
3298 INTERNET_AsyncCall(&workRequest);
3300 return ERROR_SUCCESS;
3303 return FTPFINDNEXT_FindNextFileProc(find, data);
3306 static const HANDLEHEADERVtbl FTPFINDNEXTVtbl = {
3307 FTPFINDNEXT_Destroy,
3308 NULL,
3309 FTPFINDNEXT_QueryOption,
3310 NULL,
3311 NULL,
3312 NULL,
3313 NULL,
3314 NULL,
3315 FTPFINDNEXT_FindNextFileW
3318 /***********************************************************************
3319 * FTP_ReceiveFileList (internal)
3321 * Read file list from server
3323 * RETURNS
3324 * Handle to file list on success
3325 * NULL on failure
3328 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3329 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3331 DWORD dwSize = 0;
3332 LPFILEPROPERTIESW lpafp = NULL;
3333 LPWININETFTPFINDNEXTW lpwfn = NULL;
3334 HINTERNET handle = 0;
3336 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3338 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3340 if(lpFindFileData)
3341 FTP_ConvertFileProp(lpafp, lpFindFileData);
3343 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3344 if (lpwfn)
3346 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3347 lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3348 lpwfn->hdr.dwContext = dwContext;
3349 lpwfn->hdr.refs = 1;
3350 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3351 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3352 lpwfn->size = dwSize;
3353 lpwfn->lpafp = lpafp;
3355 WININET_AddRef( &lpwfs->hdr );
3356 lpwfn->lpFtpSession = lpwfs;
3357 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3359 handle = WININET_AllocHandle( &lpwfn->hdr );
3363 if( lpwfn )
3364 WININET_Release( &lpwfn->hdr );
3366 TRACE("Matched %d files\n", dwSize);
3367 return handle;
3371 /***********************************************************************
3372 * FTP_ConvertFileProp (internal)
3374 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3376 * RETURNS
3377 * TRUE on success
3378 * FALSE on failure
3381 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3383 BOOL bSuccess = FALSE;
3385 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3387 if (lpafp)
3389 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3390 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3391 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3393 /* Not all fields are filled in */
3394 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3395 lpFindFileData->nFileSizeLow = lpafp->nSize;
3397 if (lpafp->bIsDirectory)
3398 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3400 if (lpafp->lpszName)
3401 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3403 bSuccess = TRUE;
3406 return bSuccess;
3409 /***********************************************************************
3410 * FTP_ParseNextFile (internal)
3412 * Parse the next line in file listing
3414 * RETURNS
3415 * TRUE on success
3416 * FALSE on failure
3418 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3420 static const char szSpace[] = " \t";
3421 DWORD nBufLen;
3422 char *pszLine;
3423 char *pszToken;
3424 char *pszTmp;
3425 BOOL found = FALSE;
3426 int i;
3428 lpfp->lpszName = NULL;
3429 do {
3430 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3431 return FALSE;
3433 pszToken = strtok(pszLine, szSpace);
3434 /* ls format
3435 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3437 * For instance:
3438 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3440 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3441 if(!FTP_ParsePermission(pszToken, lpfp))
3442 lpfp->bIsDirectory = FALSE;
3443 for(i=0; i<=3; i++) {
3444 if(!(pszToken = strtok(NULL, szSpace)))
3445 break;
3447 if(!pszToken) continue;
3448 if(lpfp->bIsDirectory) {
3449 TRACE("Is directory\n");
3450 lpfp->nSize = 0;
3452 else {
3453 TRACE("Size: %s\n", pszToken);
3454 lpfp->nSize = atol(pszToken);
3457 lpfp->tmLastModified.wSecond = 0;
3458 lpfp->tmLastModified.wMinute = 0;
3459 lpfp->tmLastModified.wHour = 0;
3460 lpfp->tmLastModified.wDay = 0;
3461 lpfp->tmLastModified.wMonth = 0;
3462 lpfp->tmLastModified.wYear = 0;
3464 /* Determine month */
3465 pszToken = strtok(NULL, szSpace);
3466 if(!pszToken) continue;
3467 if(strlen(pszToken) >= 3) {
3468 pszToken[3] = 0;
3469 if((pszTmp = StrStrIA(szMonths, pszToken)))
3470 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3472 /* Determine day */
3473 pszToken = strtok(NULL, szSpace);
3474 if(!pszToken) continue;
3475 lpfp->tmLastModified.wDay = atoi(pszToken);
3476 /* Determine time or year */
3477 pszToken = strtok(NULL, szSpace);
3478 if(!pszToken) continue;
3479 if((pszTmp = strchr(pszToken, ':'))) {
3480 SYSTEMTIME curr_time;
3481 *pszTmp = 0;
3482 pszTmp++;
3483 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3484 lpfp->tmLastModified.wHour = atoi(pszToken);
3485 GetLocalTime( &curr_time );
3486 lpfp->tmLastModified.wYear = curr_time.wYear;
3488 else {
3489 lpfp->tmLastModified.wYear = atoi(pszToken);
3490 lpfp->tmLastModified.wHour = 12;
3492 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3493 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3494 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3496 pszToken = strtok(NULL, szSpace);
3497 if(!pszToken) continue;
3498 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3499 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3501 /* NT way of parsing ... :
3503 07-13-03 08:55PM <DIR> sakpatch
3504 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3506 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3507 int mon, mday, year, hour, min;
3508 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3510 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3511 lpfp->tmLastModified.wDay = mday;
3512 lpfp->tmLastModified.wMonth = mon;
3513 lpfp->tmLastModified.wYear = year;
3515 /* Hacky and bad Y2K protection :-) */
3516 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3518 pszToken = strtok(NULL, szSpace);
3519 if(!pszToken) continue;
3520 sscanf(pszToken, "%d:%d", &hour, &min);
3521 lpfp->tmLastModified.wHour = hour;
3522 lpfp->tmLastModified.wMinute = min;
3523 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3524 lpfp->tmLastModified.wHour += 12;
3526 lpfp->tmLastModified.wSecond = 0;
3528 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3529 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3530 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3532 pszToken = strtok(NULL, szSpace);
3533 if(!pszToken) continue;
3534 if(!strcasecmp(pszToken, "<DIR>")) {
3535 lpfp->bIsDirectory = TRUE;
3536 lpfp->nSize = 0;
3537 TRACE("Is directory\n");
3539 else {
3540 lpfp->bIsDirectory = FALSE;
3541 lpfp->nSize = atol(pszToken);
3542 TRACE("Size: %d\n", lpfp->nSize);
3545 pszToken = strtok(NULL, szSpace);
3546 if(!pszToken) continue;
3547 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3548 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3550 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3551 else if(pszToken[0] == '+') {
3552 FIXME("EPLF Format not implemented\n");
3555 if(lpfp->lpszName) {
3556 if((lpszSearchFile == NULL) ||
3557 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3558 found = TRUE;
3559 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3561 else {
3562 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3563 lpfp->lpszName = NULL;
3566 } while(!found);
3567 return TRUE;
3570 /***********************************************************************
3571 * FTP_ParseDirectory (internal)
3573 * Parse string of directory information
3575 * RETURNS
3576 * TRUE on success
3577 * FALSE on failure
3579 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3580 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3582 BOOL bSuccess = TRUE;
3583 INT sizeFilePropArray = 500;/*20; */
3584 INT indexFilePropArray = -1;
3586 TRACE("\n");
3588 /* Allocate initial file properties array */
3589 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3590 if (!*lpafp)
3591 return FALSE;
3593 do {
3594 if (indexFilePropArray+1 >= sizeFilePropArray)
3596 LPFILEPROPERTIESW tmpafp;
3598 sizeFilePropArray *= 2;
3599 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3600 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3601 if (NULL == tmpafp)
3603 bSuccess = FALSE;
3604 break;
3607 *lpafp = tmpafp;
3609 indexFilePropArray++;
3610 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3612 if (bSuccess && indexFilePropArray)
3614 if (indexFilePropArray < sizeFilePropArray - 1)
3616 LPFILEPROPERTIESW tmpafp;
3618 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3619 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3620 if (NULL != tmpafp)
3621 *lpafp = tmpafp;
3623 *dwfp = indexFilePropArray;
3625 else
3627 HeapFree(GetProcessHeap(), 0, *lpafp);
3628 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3629 bSuccess = FALSE;
3632 return bSuccess;
3636 /***********************************************************************
3637 * FTP_ParsePermission (internal)
3639 * Parse permission string of directory information
3641 * RETURNS
3642 * TRUE on success
3643 * FALSE on failure
3646 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3648 BOOL bSuccess = TRUE;
3649 unsigned short nPermission = 0;
3650 INT nPos = 1;
3651 INT nLast = 9;
3653 TRACE("\n");
3654 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3656 bSuccess = FALSE;
3657 return bSuccess;
3660 lpfp->bIsDirectory = (*lpszPermission == 'd');
3663 switch (nPos)
3665 case 1:
3666 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3667 break;
3668 case 2:
3669 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3670 break;
3671 case 3:
3672 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3673 break;
3674 case 4:
3675 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3676 break;
3677 case 5:
3678 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3679 break;
3680 case 6:
3681 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3682 break;
3683 case 7:
3684 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3685 break;
3686 case 8:
3687 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3688 break;
3689 case 9:
3690 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3691 break;
3693 nPos++;
3694 }while (nPos <= nLast);
3696 lpfp->permissions = nPermission;
3697 return bSuccess;
3701 /***********************************************************************
3702 * FTP_SetResponseError (internal)
3704 * Set the appropriate error code for a given response from the server
3706 * RETURNS
3709 static DWORD FTP_SetResponseError(DWORD dwResponse)
3711 DWORD dwCode = 0;
3713 switch(dwResponse)
3715 case 425: /* Cannot open data connection. */
3716 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3717 break;
3719 case 426: /* Connection closed, transer aborted. */
3720 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3721 break;
3723 case 530: /* Not logged in. Login incorrect. */
3724 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3725 break;
3727 case 421: /* Service not available - Server may be shutting down. */
3728 case 450: /* File action not taken. File may be busy. */
3729 case 451: /* Action aborted. Server error. */
3730 case 452: /* Action not taken. Insufficient storage space on server. */
3731 case 500: /* Syntax error. Command unrecognized. */
3732 case 501: /* Syntax error. Error in parameters or arguments. */
3733 case 502: /* Command not implemented. */
3734 case 503: /* Bad sequence of commands. */
3735 case 504: /* Command not implemented for that parameter. */
3736 case 532: /* Need account for storing files */
3737 case 550: /* File action not taken. File not found or no access. */
3738 case 551: /* Requested action aborted. Page type unknown */
3739 case 552: /* Action aborted. Exceeded storage allocation */
3740 case 553: /* Action not taken. File name not allowed. */
3742 default:
3743 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3744 break;
3747 INTERNET_SetLastError(dwCode);
3748 return dwCode;