push c4c845830c6aff14e1b16bbb8b4a57a7e0d6213f
[wine/hacks.git] / dlls / wininet / ftp.c
blobdce18bb6804ed1cbaa7b30128ec789d30fcb60be
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 #ifdef HAVE_SYS_IOCTL_H
50 # include <sys/ioctl.h>
51 #endif
52 #include <time.h>
53 #include <assert.h>
55 #include "windef.h"
56 #include "winbase.h"
57 #include "wingdi.h"
58 #include "winuser.h"
59 #include "wininet.h"
60 #include "winnls.h"
61 #include "winerror.h"
62 #include "winreg.h"
63 #include "winternl.h"
64 #include "shlwapi.h"
66 #include "wine/debug.h"
67 #include "internet.h"
69 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
71 typedef struct _WININETFTPSESSIONW WININETFTPSESSIONW;
73 typedef struct
75 object_header_t hdr;
76 WININETFTPSESSIONW *lpFtpSession;
77 BOOL session_deleted;
78 int nDataSocket;
79 } WININETFTPFILE, *LPWININETFTPFILE;
81 typedef struct _WININETFTPSESSIONW
83 object_header_t hdr;
84 WININETAPPINFOW *lpAppInfo;
85 int sndSocket;
86 int lstnSocket;
87 int pasvSocket; /* data socket connected by us in case of passive FTP */
88 LPWININETFTPFILE download_in_progress;
89 struct sockaddr_in socketAddress;
90 struct sockaddr_in lstnSocketAddress;
91 LPWSTR lpszPassword;
92 LPWSTR lpszUserName;
93 } *LPWININETFTPSESSIONW;
95 typedef struct
97 BOOL bIsDirectory;
98 LPWSTR lpszName;
99 DWORD nSize;
100 SYSTEMTIME tmLastModified;
101 unsigned short permissions;
102 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
104 typedef struct
106 object_header_t hdr;
107 WININETFTPSESSIONW *lpFtpSession;
108 DWORD index;
109 DWORD size;
110 LPFILEPROPERTIESW lpafp;
111 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
113 #define DATA_PACKET_SIZE 0x2000
114 #define szCRLF "\r\n"
115 #define MAX_BACKLOG 5
117 /* Testing shows that Windows only accepts dwFlags where the last
118 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
120 #define FTP_CONDITION_MASK 0x0007
122 typedef enum {
123 /* FTP commands with arguments. */
124 FTP_CMD_ACCT,
125 FTP_CMD_CWD,
126 FTP_CMD_DELE,
127 FTP_CMD_MKD,
128 FTP_CMD_PASS,
129 FTP_CMD_PORT,
130 FTP_CMD_RETR,
131 FTP_CMD_RMD,
132 FTP_CMD_RNFR,
133 FTP_CMD_RNTO,
134 FTP_CMD_STOR,
135 FTP_CMD_TYPE,
136 FTP_CMD_USER,
137 FTP_CMD_SIZE,
139 /* FTP commands without arguments. */
140 FTP_CMD_ABOR,
141 FTP_CMD_LIST,
142 FTP_CMD_NLST,
143 FTP_CMD_PASV,
144 FTP_CMD_PWD,
145 FTP_CMD_QUIT,
146 } FTP_COMMAND;
148 static const CHAR *const szFtpCommands[] = {
149 "ACCT",
150 "CWD",
151 "DELE",
152 "MKD",
153 "PASS",
154 "PORT",
155 "RETR",
156 "RMD",
157 "RNFR",
158 "RNTO",
159 "STOR",
160 "TYPE",
161 "USER",
162 "SIZE",
163 "ABOR",
164 "LIST",
165 "NLST",
166 "PASV",
167 "PWD",
168 "QUIT",
171 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
172 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
174 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
175 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext);
176 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
177 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket);
178 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile);
179 static INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD_PTR dwContext);
180 static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
181 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile);
182 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs);
183 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs);
184 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs);
185 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs);
186 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType);
187 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs);
188 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs);
189 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs);
190 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
191 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
192 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
193 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
194 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
195 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
196 static DWORD FTP_SetResponseError(DWORD dwResponse);
197 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
198 static BOOL FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
199 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
200 static BOOL FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
201 static BOOL FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
202 static HINTERNET FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
203 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
204 static BOOL FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
205 LPDWORD lpdwCurrentDirectory);
206 static BOOL FTP_FtpRenameFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest);
207 static BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
208 static BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName);
209 static BOOL FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
210 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
211 DWORD_PTR dwContext);
214 /***********************************************************************
215 * FtpPutFileA (WININET.@)
217 * Uploads a file to the FTP server
219 * RETURNS
220 * TRUE on success
221 * FALSE on failure
224 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
225 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
227 LPWSTR lpwzLocalFile;
228 LPWSTR lpwzNewRemoteFile;
229 BOOL ret;
231 lpwzLocalFile = lpszLocalFile?WININET_strdup_AtoW(lpszLocalFile):NULL;
232 lpwzNewRemoteFile = lpszNewRemoteFile?WININET_strdup_AtoW(lpszNewRemoteFile):NULL;
233 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
234 dwFlags, dwContext);
235 HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
236 HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
237 return ret;
240 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
242 struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
243 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
245 TRACE("%p\n", lpwfs);
247 FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
248 req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
250 HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
251 HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
254 /***********************************************************************
255 * FtpPutFileW (WININET.@)
257 * Uploads a file to the FTP server
259 * RETURNS
260 * TRUE on success
261 * FALSE on failure
264 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
265 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
267 LPWININETFTPSESSIONW lpwfs;
268 LPWININETAPPINFOW hIC = NULL;
269 BOOL r = FALSE;
271 if (!lpszLocalFile || !lpszNewRemoteFile)
273 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
274 return FALSE;
277 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
278 if (!lpwfs)
280 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
281 return FALSE;
284 if (WH_HFTPSESSION != lpwfs->hdr.htype)
286 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
287 goto lend;
290 if (lpwfs->download_in_progress != NULL)
292 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
293 goto lend;
296 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
298 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
299 goto lend;
302 hIC = lpwfs->lpAppInfo;
303 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
305 WORKREQUEST workRequest;
306 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
308 workRequest.asyncproc = AsyncFtpPutFileProc;
309 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
310 req->lpszLocalFile = WININET_strdupW(lpszLocalFile);
311 req->lpszNewRemoteFile = WININET_strdupW(lpszNewRemoteFile);
312 req->dwFlags = dwFlags;
313 req->dwContext = dwContext;
315 r = INTERNET_AsyncCall(&workRequest);
317 else
319 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
320 lpszNewRemoteFile, dwFlags, dwContext);
323 lend:
324 WININET_Release( &lpwfs->hdr );
326 return r;
329 /***********************************************************************
330 * FTP_FtpPutFileW (Internal)
332 * Uploads a file to the FTP server
334 * RETURNS
335 * TRUE on success
336 * FALSE on failure
339 static BOOL FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
340 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
342 HANDLE hFile;
343 BOOL bSuccess = FALSE;
344 LPWININETAPPINFOW hIC = NULL;
345 INT nResCode;
347 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
349 /* Clear any error information */
350 INTERNET_SetLastError(0);
352 /* Open file to be uploaded */
353 if (INVALID_HANDLE_VALUE ==
354 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
355 /* Let CreateFile set the appropriate error */
356 return FALSE;
358 hIC = lpwfs->lpAppInfo;
360 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
362 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
364 INT nDataSocket;
366 /* Get data socket to server */
367 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
369 FTP_SendData(lpwfs, nDataSocket, hFile);
370 closesocket(nDataSocket);
371 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
372 if (nResCode)
374 if (nResCode == 226)
375 bSuccess = TRUE;
376 else
377 FTP_SetResponseError(nResCode);
382 if (lpwfs->lstnSocket != -1)
383 closesocket(lpwfs->lstnSocket);
385 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
387 INTERNET_ASYNC_RESULT iar;
389 iar.dwResult = (DWORD)bSuccess;
390 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
391 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
392 &iar, sizeof(INTERNET_ASYNC_RESULT));
395 CloseHandle(hFile);
397 return bSuccess;
401 /***********************************************************************
402 * FtpSetCurrentDirectoryA (WININET.@)
404 * Change the working directory on the FTP server
406 * RETURNS
407 * TRUE on success
408 * FALSE on failure
411 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
413 LPWSTR lpwzDirectory;
414 BOOL ret;
416 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
417 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
418 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
419 return ret;
423 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
425 struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
426 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
428 TRACE("%p\n", lpwfs);
430 FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
431 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
434 /***********************************************************************
435 * FtpSetCurrentDirectoryW (WININET.@)
437 * Change the working directory on the FTP server
439 * RETURNS
440 * TRUE on success
441 * FALSE on failure
444 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
446 LPWININETFTPSESSIONW lpwfs = NULL;
447 LPWININETAPPINFOW hIC = NULL;
448 BOOL r = FALSE;
450 if (!lpszDirectory)
452 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
453 goto lend;
456 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
457 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
459 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
460 goto lend;
463 if (lpwfs->download_in_progress != NULL)
465 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
466 goto lend;
469 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
471 hIC = lpwfs->lpAppInfo;
472 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
474 WORKREQUEST workRequest;
475 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
477 workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
478 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
479 req = &workRequest.u.FtpSetCurrentDirectoryW;
480 req->lpszDirectory = WININET_strdupW(lpszDirectory);
482 r = INTERNET_AsyncCall(&workRequest);
484 else
486 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
489 lend:
490 if( lpwfs )
491 WININET_Release( &lpwfs->hdr );
493 return r;
497 /***********************************************************************
498 * FTP_FtpSetCurrentDirectoryW (Internal)
500 * Change the working directory on the FTP server
502 * RETURNS
503 * TRUE on success
504 * FALSE on failure
507 static BOOL FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
509 INT nResCode;
510 LPWININETAPPINFOW hIC = NULL;
511 DWORD bSuccess = FALSE;
513 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
515 /* Clear any error information */
516 INTERNET_SetLastError(0);
518 hIC = lpwfs->lpAppInfo;
519 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
520 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
521 goto lend;
523 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
525 if (nResCode)
527 if (nResCode == 250)
528 bSuccess = TRUE;
529 else
530 FTP_SetResponseError(nResCode);
533 lend:
534 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
536 INTERNET_ASYNC_RESULT iar;
538 iar.dwResult = bSuccess;
539 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
540 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
541 &iar, sizeof(INTERNET_ASYNC_RESULT));
543 return bSuccess;
547 /***********************************************************************
548 * FtpCreateDirectoryA (WININET.@)
550 * Create new directory on the FTP server
552 * RETURNS
553 * TRUE on success
554 * FALSE on failure
557 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
559 LPWSTR lpwzDirectory;
560 BOOL ret;
562 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
563 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
564 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
565 return ret;
569 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
571 struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
572 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
574 TRACE(" %p\n", lpwfs);
576 FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
577 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
580 /***********************************************************************
581 * FtpCreateDirectoryW (WININET.@)
583 * Create new directory on the FTP server
585 * RETURNS
586 * TRUE on success
587 * FALSE on failure
590 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
592 LPWININETFTPSESSIONW lpwfs;
593 LPWININETAPPINFOW hIC = NULL;
594 BOOL r = FALSE;
596 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
597 if (!lpwfs)
599 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
600 return FALSE;
603 if (WH_HFTPSESSION != lpwfs->hdr.htype)
605 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
606 goto lend;
609 if (lpwfs->download_in_progress != NULL)
611 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
612 goto lend;
615 if (!lpszDirectory)
617 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
618 goto lend;
621 hIC = lpwfs->lpAppInfo;
622 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
624 WORKREQUEST workRequest;
625 struct WORKREQ_FTPCREATEDIRECTORYW *req;
627 workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
628 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
629 req = &workRequest.u.FtpCreateDirectoryW;
630 req->lpszDirectory = WININET_strdupW(lpszDirectory);
632 r = INTERNET_AsyncCall(&workRequest);
634 else
636 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
638 lend:
639 WININET_Release( &lpwfs->hdr );
641 return r;
645 /***********************************************************************
646 * FTP_FtpCreateDirectoryW (Internal)
648 * Create new directory on the FTP server
650 * RETURNS
651 * TRUE on success
652 * FALSE on failure
655 static BOOL FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
657 INT nResCode;
658 BOOL bSuccess = FALSE;
659 LPWININETAPPINFOW hIC = NULL;
661 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
663 /* Clear any error information */
664 INTERNET_SetLastError(0);
666 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
667 goto lend;
669 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
670 if (nResCode)
672 if (nResCode == 257)
673 bSuccess = TRUE;
674 else
675 FTP_SetResponseError(nResCode);
678 lend:
679 hIC = lpwfs->lpAppInfo;
680 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
682 INTERNET_ASYNC_RESULT iar;
684 iar.dwResult = (DWORD)bSuccess;
685 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
686 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
687 &iar, sizeof(INTERNET_ASYNC_RESULT));
690 return bSuccess;
693 /***********************************************************************
694 * FtpFindFirstFileA (WININET.@)
696 * Search the specified directory
698 * RETURNS
699 * HINTERNET on success
700 * NULL on failure
703 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
704 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
706 LPWSTR lpwzSearchFile;
707 WIN32_FIND_DATAW wfd;
708 LPWIN32_FIND_DATAW lpFindFileDataW;
709 HINTERNET ret;
711 lpwzSearchFile = lpszSearchFile?WININET_strdup_AtoW(lpszSearchFile):NULL;
712 lpFindFileDataW = lpFindFileData?&wfd:NULL;
713 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
714 HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
716 if(lpFindFileData) {
717 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
719 return ret;
723 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
725 struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
726 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
728 TRACE("%p\n", lpwfs);
730 FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
731 req->lpFindFileData, req->dwFlags, req->dwContext);
732 HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
735 /***********************************************************************
736 * FtpFindFirstFileW (WININET.@)
738 * Search the specified directory
740 * RETURNS
741 * HINTERNET on success
742 * NULL on failure
745 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
746 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
748 LPWININETFTPSESSIONW lpwfs;
749 LPWININETAPPINFOW hIC = NULL;
750 HINTERNET r = NULL;
752 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
753 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
755 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
756 goto lend;
759 if (lpwfs->download_in_progress != NULL)
761 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
762 goto lend;
765 hIC = lpwfs->lpAppInfo;
766 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
768 WORKREQUEST workRequest;
769 struct WORKREQ_FTPFINDFIRSTFILEW *req;
771 workRequest.asyncproc = AsyncFtpFindFirstFileProc;
772 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
773 req = &workRequest.u.FtpFindFirstFileW;
774 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : WININET_strdupW(lpszSearchFile);
775 req->lpFindFileData = lpFindFileData;
776 req->dwFlags = dwFlags;
777 req->dwContext= dwContext;
779 INTERNET_AsyncCall(&workRequest);
780 r = NULL;
782 else
784 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
785 dwFlags, dwContext);
787 lend:
788 if( lpwfs )
789 WININET_Release( &lpwfs->hdr );
791 return r;
795 /***********************************************************************
796 * FTP_FtpFindFirstFileW (Internal)
798 * Search the specified directory
800 * RETURNS
801 * HINTERNET on success
802 * NULL on failure
805 static HINTERNET FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
806 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
808 INT nResCode;
809 LPWININETAPPINFOW hIC = NULL;
810 HINTERNET hFindNext = NULL;
812 TRACE("\n");
814 /* Clear any error information */
815 INTERNET_SetLastError(0);
817 if (!FTP_InitListenSocket(lpwfs))
818 goto lend;
820 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
821 goto lend;
823 if (!FTP_SendPortOrPasv(lpwfs))
824 goto lend;
826 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
827 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
828 goto lend;
830 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
831 if (nResCode)
833 if (nResCode == 125 || nResCode == 150)
835 INT nDataSocket;
837 /* Get data socket to server */
838 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
840 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
841 closesocket(nDataSocket);
842 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
843 if (nResCode != 226 && nResCode != 250)
844 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
847 else
848 FTP_SetResponseError(nResCode);
851 lend:
852 if (lpwfs->lstnSocket != -1)
853 closesocket(lpwfs->lstnSocket);
855 hIC = lpwfs->lpAppInfo;
856 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
858 INTERNET_ASYNC_RESULT iar;
860 if (hFindNext)
862 iar.dwResult = (DWORD_PTR)hFindNext;
863 iar.dwError = ERROR_SUCCESS;
864 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
865 &iar, sizeof(INTERNET_ASYNC_RESULT));
868 iar.dwResult = (DWORD_PTR)hFindNext;
869 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
870 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
871 &iar, sizeof(INTERNET_ASYNC_RESULT));
874 return hFindNext;
878 /***********************************************************************
879 * FtpGetCurrentDirectoryA (WININET.@)
881 * Retrieves the current directory
883 * RETURNS
884 * TRUE on success
885 * FALSE on failure
888 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
889 LPDWORD lpdwCurrentDirectory)
891 WCHAR *dir = NULL;
892 DWORD len;
893 BOOL ret;
895 if(lpdwCurrentDirectory) {
896 len = *lpdwCurrentDirectory;
897 if(lpszCurrentDirectory)
899 dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
900 if (NULL == dir)
902 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
903 return FALSE;
907 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
909 if (ret && lpszCurrentDirectory)
910 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
912 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
913 HeapFree(GetProcessHeap(), 0, dir);
914 return ret;
918 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
920 struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
921 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
923 TRACE("%p\n", lpwfs);
925 FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
928 /***********************************************************************
929 * FtpGetCurrentDirectoryW (WININET.@)
931 * Retrieves the current directory
933 * RETURNS
934 * TRUE on success
935 * FALSE on failure
938 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
939 LPDWORD lpdwCurrentDirectory)
941 LPWININETFTPSESSIONW lpwfs;
942 LPWININETAPPINFOW hIC = NULL;
943 BOOL r = FALSE;
945 TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
947 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
948 if (NULL == lpwfs)
950 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
951 goto lend;
954 if (WH_HFTPSESSION != lpwfs->hdr.htype)
956 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
957 goto lend;
960 if (!lpdwCurrentDirectory)
962 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
963 goto lend;
966 if (lpszCurrentDirectory == NULL)
968 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
969 goto lend;
972 if (lpwfs->download_in_progress != NULL)
974 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
975 goto lend;
978 hIC = lpwfs->lpAppInfo;
979 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
981 WORKREQUEST workRequest;
982 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
984 workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
985 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
986 req = &workRequest.u.FtpGetCurrentDirectoryW;
987 req->lpszDirectory = lpszCurrentDirectory;
988 req->lpdwDirectory = lpdwCurrentDirectory;
990 r = INTERNET_AsyncCall(&workRequest);
992 else
994 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
995 lpdwCurrentDirectory);
998 lend:
999 if( lpwfs )
1000 WININET_Release( &lpwfs->hdr );
1002 return r;
1006 /***********************************************************************
1007 * FTP_FtpGetCurrentDirectoryW (Internal)
1009 * Retrieves the current directory
1011 * RETURNS
1012 * TRUE on success
1013 * FALSE on failure
1016 static BOOL FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
1017 LPDWORD lpdwCurrentDirectory)
1019 INT nResCode;
1020 LPWININETAPPINFOW hIC = NULL;
1021 DWORD bSuccess = FALSE;
1023 /* Clear any error information */
1024 INTERNET_SetLastError(0);
1026 hIC = lpwfs->lpAppInfo;
1027 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1028 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1029 goto lend;
1031 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1032 if (nResCode)
1034 if (nResCode == 257) /* Extract directory name */
1036 DWORD firstpos, lastpos, len;
1037 LPWSTR lpszResponseBuffer = WININET_strdup_AtoW(INTERNET_GetResponseBuffer());
1039 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1041 if ('"' == lpszResponseBuffer[lastpos])
1043 if (!firstpos)
1044 firstpos = lastpos;
1045 else
1046 break;
1049 len = lastpos - firstpos;
1050 if (*lpdwCurrentDirectory >= len)
1052 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1053 lpszCurrentDirectory[len - 1] = 0;
1054 *lpdwCurrentDirectory = len;
1055 bSuccess = TRUE;
1057 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1059 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
1061 else
1062 FTP_SetResponseError(nResCode);
1065 lend:
1066 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1068 INTERNET_ASYNC_RESULT iar;
1070 iar.dwResult = bSuccess;
1071 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1072 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1073 &iar, sizeof(INTERNET_ASYNC_RESULT));
1076 return bSuccess;
1080 /***********************************************************************
1081 * FTPFILE_Destroy(internal)
1083 * Closes the file transfer handle. This also 'cleans' the data queue of
1084 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1087 static void FTPFILE_Destroy(object_header_t *hdr)
1089 LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
1090 LPWININETFTPSESSIONW lpwfs = lpwh->lpFtpSession;
1091 INT nResCode;
1093 TRACE("\n");
1095 if (!lpwh->session_deleted)
1096 lpwfs->download_in_progress = NULL;
1098 if (lpwh->nDataSocket != -1)
1099 closesocket(lpwh->nDataSocket);
1101 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1102 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1104 WININET_Release(&lpwh->lpFtpSession->hdr);
1106 HeapFree(GetProcessHeap(), 0, lpwh);
1109 static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1111 switch(option) {
1112 case INTERNET_OPTION_HANDLE_TYPE:
1113 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1115 if (*size < sizeof(ULONG))
1116 return ERROR_INSUFFICIENT_BUFFER;
1118 *size = sizeof(DWORD);
1119 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1120 return ERROR_SUCCESS;
1123 return INET_QueryOption(option, buffer, size, unicode);
1126 static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
1128 WININETFTPFILE *file = (WININETFTPFILE*)hdr;
1129 int res;
1131 if (file->nDataSocket == -1)
1132 return ERROR_INTERNET_DISCONNECTED;
1134 /* FIXME: FTP should use NETCON_ stuff */
1135 res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1136 *read = res>0 ? res : 0;
1138 return res>=0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME*/
1141 static DWORD FTPFILE_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
1142 DWORD flags, DWORD_PTR context)
1144 return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1147 static DWORD FTPFILE_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
1148 DWORD flags, DWORD_PTR context)
1150 return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1153 static BOOL FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1155 LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
1156 int res;
1158 res = send(lpwh->nDataSocket, buffer, size, 0);
1160 *written = res>0 ? res : 0;
1161 return res >= 0;
1164 static void FTP_ReceiveRequestData(WININETFTPFILE *file, BOOL first_notif)
1166 INTERNET_ASYNC_RESULT iar;
1167 BYTE buffer[4096];
1168 int available;
1170 TRACE("%p\n", file);
1172 available = recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1174 if(available != -1) {
1175 iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1176 iar.dwError = first_notif ? 0 : available;
1177 }else {
1178 iar.dwResult = 0;
1179 iar.dwError = INTERNET_GetLastError();
1182 INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1183 sizeof(INTERNET_ASYNC_RESULT));
1186 static void FTPFILE_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
1188 WININETFTPFILE *file = (WININETFTPFILE*)workRequest->hdr;
1190 FTP_ReceiveRequestData(file, FALSE);
1193 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1195 LPWININETFTPFILE file = (LPWININETFTPFILE) hdr;
1196 int retval, unread = 0;
1198 TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
1200 #ifdef FIONREAD
1201 retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
1202 if (!retval)
1203 TRACE("%d bytes of queued, but unread data\n", unread);
1204 #else
1205 FIXME("FIONREAD not available\n");
1206 #endif
1208 *available = unread;
1210 if(!unread) {
1211 BYTE byte;
1213 *available = 0;
1215 retval = recv(file->nDataSocket, &byte, 1, MSG_PEEK);
1216 if(retval > 0) {
1217 WORKREQUEST workRequest;
1219 *available = 0;
1220 workRequest.asyncproc = FTPFILE_AsyncQueryDataAvailableProc;
1221 workRequest.hdr = WININET_AddRef( &file->hdr );
1223 INTERNET_AsyncCall(&workRequest);
1225 return ERROR_IO_PENDING;
1229 return ERROR_SUCCESS;
1233 static const object_vtbl_t FTPFILEVtbl = {
1234 FTPFILE_Destroy,
1235 NULL,
1236 FTPFILE_QueryOption,
1237 NULL,
1238 FTPFILE_ReadFile,
1239 FTPFILE_ReadFileExA,
1240 FTPFILE_ReadFileExW,
1241 FTPFILE_WriteFile,
1242 FTPFILE_QueryDataAvailable,
1243 NULL
1246 /***********************************************************************
1247 * FTP_FtpOpenFileW (Internal)
1249 * Open a remote file for writing or reading
1251 * RETURNS
1252 * HINTERNET handle on success
1253 * NULL on failure
1256 HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
1257 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1258 DWORD_PTR dwContext)
1260 INT nDataSocket;
1261 BOOL bSuccess = FALSE;
1262 LPWININETFTPFILE lpwh = NULL;
1263 LPWININETAPPINFOW hIC = NULL;
1264 HINTERNET handle = NULL;
1266 TRACE("\n");
1268 /* Clear any error information */
1269 INTERNET_SetLastError(0);
1271 if (GENERIC_READ == fdwAccess)
1273 /* Set up socket to retrieve data */
1274 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1276 else if (GENERIC_WRITE == fdwAccess)
1278 /* Set up socket to send data */
1279 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1282 /* Get data socket to server */
1283 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1285 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPFILE));
1286 lpwh->hdr.htype = WH_HFILE;
1287 lpwh->hdr.vtbl = &FTPFILEVtbl;
1288 lpwh->hdr.dwFlags = dwFlags;
1289 lpwh->hdr.dwContext = dwContext;
1290 lpwh->hdr.refs = 1;
1291 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1292 lpwh->nDataSocket = nDataSocket;
1293 lpwh->session_deleted = FALSE;
1295 WININET_AddRef( &lpwfs->hdr );
1296 lpwh->lpFtpSession = lpwfs;
1297 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1299 handle = WININET_AllocHandle( &lpwh->hdr );
1300 if( !handle )
1301 goto lend;
1303 /* Indicate that a download is currently in progress */
1304 lpwfs->download_in_progress = lpwh;
1307 if (lpwfs->lstnSocket != -1)
1308 closesocket(lpwfs->lstnSocket);
1310 hIC = lpwfs->lpAppInfo;
1311 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1313 INTERNET_ASYNC_RESULT iar;
1315 if (lpwh)
1317 iar.dwResult = (DWORD_PTR)handle;
1318 iar.dwError = ERROR_SUCCESS;
1319 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1320 &iar, sizeof(INTERNET_ASYNC_RESULT));
1323 if(bSuccess) {
1324 FTP_ReceiveRequestData(lpwh, TRUE);
1325 }else {
1326 iar.dwResult = 0;
1327 iar.dwError = INTERNET_GetLastError();
1328 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1329 &iar, sizeof(INTERNET_ASYNC_RESULT));
1333 lend:
1334 if( lpwh )
1335 WININET_Release( &lpwh->hdr );
1337 return handle;
1341 /***********************************************************************
1342 * FtpOpenFileA (WININET.@)
1344 * Open a remote file for writing or reading
1346 * RETURNS
1347 * HINTERNET handle on success
1348 * NULL on failure
1351 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1352 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1353 DWORD_PTR dwContext)
1355 LPWSTR lpwzFileName;
1356 HINTERNET ret;
1358 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1359 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1360 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1361 return ret;
1365 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1367 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1368 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1370 TRACE("%p\n", lpwfs);
1372 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1373 req->dwAccess, req->dwFlags, req->dwContext);
1374 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1377 /***********************************************************************
1378 * FtpOpenFileW (WININET.@)
1380 * Open a remote file for writing or reading
1382 * RETURNS
1383 * HINTERNET handle on success
1384 * NULL on failure
1387 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1388 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1389 DWORD_PTR dwContext)
1391 LPWININETFTPSESSIONW lpwfs;
1392 LPWININETAPPINFOW hIC = NULL;
1393 HINTERNET r = NULL;
1395 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1396 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1398 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1399 if (!lpwfs)
1401 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1402 return FALSE;
1405 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1407 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1408 goto lend;
1411 if ((!lpszFileName) ||
1412 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1413 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1415 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1416 goto lend;
1419 if (lpwfs->download_in_progress != NULL)
1421 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1422 goto lend;
1425 hIC = lpwfs->lpAppInfo;
1426 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1428 WORKREQUEST workRequest;
1429 struct WORKREQ_FTPOPENFILEW *req;
1431 workRequest.asyncproc = AsyncFtpOpenFileProc;
1432 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1433 req = &workRequest.u.FtpOpenFileW;
1434 req->lpszFilename = WININET_strdupW(lpszFileName);
1435 req->dwAccess = fdwAccess;
1436 req->dwFlags = dwFlags;
1437 req->dwContext = dwContext;
1439 INTERNET_AsyncCall(&workRequest);
1440 r = NULL;
1442 else
1444 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1447 lend:
1448 WININET_Release( &lpwfs->hdr );
1450 return r;
1454 /***********************************************************************
1455 * FtpGetFileA (WININET.@)
1457 * Retrieve file from the FTP server
1459 * RETURNS
1460 * TRUE on success
1461 * FALSE on failure
1464 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1465 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1466 DWORD_PTR dwContext)
1468 LPWSTR lpwzRemoteFile;
1469 LPWSTR lpwzNewFile;
1470 BOOL ret;
1472 lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
1473 lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
1474 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1475 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1476 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1477 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1478 return ret;
1482 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1484 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1485 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1487 TRACE("%p\n", lpwfs);
1489 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1490 req->lpszNewFile, req->fFailIfExists,
1491 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1492 HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1493 HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1497 /***********************************************************************
1498 * FtpGetFileW (WININET.@)
1500 * Retrieve file from the FTP server
1502 * RETURNS
1503 * TRUE on success
1504 * FALSE on failure
1507 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1508 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1509 DWORD_PTR dwContext)
1511 LPWININETFTPSESSIONW lpwfs;
1512 LPWININETAPPINFOW hIC = NULL;
1513 BOOL r = FALSE;
1515 if (!lpszRemoteFile || !lpszNewFile)
1517 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1518 return FALSE;
1521 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
1522 if (!lpwfs)
1524 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1525 return FALSE;
1528 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1530 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1531 goto lend;
1534 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1536 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1537 goto lend;
1540 if (lpwfs->download_in_progress != NULL)
1542 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1543 goto lend;
1546 hIC = lpwfs->lpAppInfo;
1547 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1549 WORKREQUEST workRequest;
1550 struct WORKREQ_FTPGETFILEW *req;
1552 workRequest.asyncproc = AsyncFtpGetFileProc;
1553 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1554 req = &workRequest.u.FtpGetFileW;
1555 req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
1556 req->lpszNewFile = WININET_strdupW(lpszNewFile);
1557 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1558 req->fFailIfExists = fFailIfExists;
1559 req->dwFlags = dwInternetFlags;
1560 req->dwContext = dwContext;
1562 r = INTERNET_AsyncCall(&workRequest);
1564 else
1566 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1567 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1570 lend:
1571 WININET_Release( &lpwfs->hdr );
1573 return r;
1577 /***********************************************************************
1578 * FTP_FtpGetFileW (Internal)
1580 * Retrieve file from the FTP server
1582 * RETURNS
1583 * TRUE on success
1584 * FALSE on failure
1587 static BOOL FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1588 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1589 DWORD_PTR dwContext)
1591 BOOL bSuccess = FALSE;
1592 HANDLE hFile;
1593 LPWININETAPPINFOW hIC = NULL;
1595 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1597 /* Clear any error information */
1598 INTERNET_SetLastError(0);
1600 /* Ensure we can write to lpszNewfile by opening it */
1601 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1602 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1603 if (INVALID_HANDLE_VALUE == hFile)
1604 return FALSE;
1606 /* Set up socket to retrieve data */
1607 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1609 INT nDataSocket;
1611 /* Get data socket to server */
1612 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1614 INT nResCode;
1616 /* Receive data */
1617 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1618 closesocket(nDataSocket);
1620 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1621 if (nResCode)
1623 if (nResCode == 226)
1624 bSuccess = TRUE;
1625 else
1626 FTP_SetResponseError(nResCode);
1631 if (lpwfs->lstnSocket != -1)
1632 closesocket(lpwfs->lstnSocket);
1634 CloseHandle(hFile);
1636 hIC = lpwfs->lpAppInfo;
1637 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1639 INTERNET_ASYNC_RESULT iar;
1641 iar.dwResult = (DWORD)bSuccess;
1642 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1643 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1644 &iar, sizeof(INTERNET_ASYNC_RESULT));
1647 if (!bSuccess) DeleteFileW(lpszNewFile);
1648 return bSuccess;
1651 /***********************************************************************
1652 * FtpGetFileSize (WININET.@)
1654 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1656 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1658 if (lpdwFileSizeHigh)
1659 *lpdwFileSizeHigh = 0;
1661 return 0;
1664 /***********************************************************************
1665 * FtpDeleteFileA (WININET.@)
1667 * Delete a file on the ftp server
1669 * RETURNS
1670 * TRUE on success
1671 * FALSE on failure
1674 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1676 LPWSTR lpwzFileName;
1677 BOOL ret;
1679 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1680 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1681 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1682 return ret;
1685 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1687 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1688 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1690 TRACE("%p\n", lpwfs);
1692 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1693 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1696 /***********************************************************************
1697 * FtpDeleteFileW (WININET.@)
1699 * Delete a file on the ftp server
1701 * RETURNS
1702 * TRUE on success
1703 * FALSE on failure
1706 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1708 LPWININETFTPSESSIONW lpwfs;
1709 LPWININETAPPINFOW hIC = NULL;
1710 BOOL r = FALSE;
1712 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1713 if (!lpwfs)
1715 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1716 return FALSE;
1719 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1721 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1722 goto lend;
1725 if (lpwfs->download_in_progress != NULL)
1727 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1728 goto lend;
1731 if (!lpszFileName)
1733 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1734 goto lend;
1737 hIC = lpwfs->lpAppInfo;
1738 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1740 WORKREQUEST workRequest;
1741 struct WORKREQ_FTPDELETEFILEW *req;
1743 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1744 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1745 req = &workRequest.u.FtpDeleteFileW;
1746 req->lpszFilename = WININET_strdupW(lpszFileName);
1748 r = INTERNET_AsyncCall(&workRequest);
1750 else
1752 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1755 lend:
1756 WININET_Release( &lpwfs->hdr );
1758 return r;
1761 /***********************************************************************
1762 * FTP_FtpDeleteFileW (Internal)
1764 * Delete a file on the ftp server
1766 * RETURNS
1767 * TRUE on success
1768 * FALSE on failure
1771 BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName)
1773 INT nResCode;
1774 BOOL bSuccess = FALSE;
1775 LPWININETAPPINFOW hIC = NULL;
1777 TRACE("%p\n", lpwfs);
1779 /* Clear any error information */
1780 INTERNET_SetLastError(0);
1782 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1783 goto lend;
1785 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1786 if (nResCode)
1788 if (nResCode == 250)
1789 bSuccess = TRUE;
1790 else
1791 FTP_SetResponseError(nResCode);
1793 lend:
1794 hIC = lpwfs->lpAppInfo;
1795 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1797 INTERNET_ASYNC_RESULT iar;
1799 iar.dwResult = (DWORD)bSuccess;
1800 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1801 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1802 &iar, sizeof(INTERNET_ASYNC_RESULT));
1805 return bSuccess;
1809 /***********************************************************************
1810 * FtpRemoveDirectoryA (WININET.@)
1812 * Remove a directory on the ftp server
1814 * RETURNS
1815 * TRUE on success
1816 * FALSE on failure
1819 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1821 LPWSTR lpwzDirectory;
1822 BOOL ret;
1824 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
1825 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1826 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1827 return ret;
1830 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1832 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1833 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1835 TRACE("%p\n", lpwfs);
1837 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1838 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1841 /***********************************************************************
1842 * FtpRemoveDirectoryW (WININET.@)
1844 * Remove a directory on the ftp server
1846 * RETURNS
1847 * TRUE on success
1848 * FALSE on failure
1851 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1853 LPWININETFTPSESSIONW lpwfs;
1854 LPWININETAPPINFOW hIC = NULL;
1855 BOOL r = FALSE;
1857 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1858 if (!lpwfs)
1860 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1861 return FALSE;
1864 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1866 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1867 goto lend;
1870 if (lpwfs->download_in_progress != NULL)
1872 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1873 goto lend;
1876 if (!lpszDirectory)
1878 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1879 goto lend;
1882 hIC = lpwfs->lpAppInfo;
1883 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1885 WORKREQUEST workRequest;
1886 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1888 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1889 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1890 req = &workRequest.u.FtpRemoveDirectoryW;
1891 req->lpszDirectory = WININET_strdupW(lpszDirectory);
1893 r = INTERNET_AsyncCall(&workRequest);
1895 else
1897 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1900 lend:
1901 WININET_Release( &lpwfs->hdr );
1903 return r;
1906 /***********************************************************************
1907 * FTP_FtpRemoveDirectoryW (Internal)
1909 * Remove a directory on the ftp server
1911 * RETURNS
1912 * TRUE on success
1913 * FALSE on failure
1916 BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
1918 INT nResCode;
1919 BOOL bSuccess = FALSE;
1920 LPWININETAPPINFOW hIC = NULL;
1922 TRACE("\n");
1924 /* Clear any error information */
1925 INTERNET_SetLastError(0);
1927 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1928 goto lend;
1930 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1931 if (nResCode)
1933 if (nResCode == 250)
1934 bSuccess = TRUE;
1935 else
1936 FTP_SetResponseError(nResCode);
1939 lend:
1940 hIC = lpwfs->lpAppInfo;
1941 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1943 INTERNET_ASYNC_RESULT iar;
1945 iar.dwResult = (DWORD)bSuccess;
1946 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1947 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1948 &iar, sizeof(INTERNET_ASYNC_RESULT));
1951 return bSuccess;
1955 /***********************************************************************
1956 * FtpRenameFileA (WININET.@)
1958 * Rename a file on the ftp server
1960 * RETURNS
1961 * TRUE on success
1962 * FALSE on failure
1965 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1967 LPWSTR lpwzSrc;
1968 LPWSTR lpwzDest;
1969 BOOL ret;
1971 lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL;
1972 lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL;
1973 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1974 HeapFree(GetProcessHeap(), 0, lpwzSrc);
1975 HeapFree(GetProcessHeap(), 0, lpwzDest);
1976 return ret;
1979 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
1981 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
1982 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1984 TRACE("%p\n", lpwfs);
1986 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
1987 HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
1988 HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
1991 /***********************************************************************
1992 * FtpRenameFileW (WININET.@)
1994 * Rename a file on the ftp server
1996 * RETURNS
1997 * TRUE on success
1998 * FALSE on failure
2001 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2003 LPWININETFTPSESSIONW lpwfs;
2004 LPWININETAPPINFOW hIC = NULL;
2005 BOOL r = FALSE;
2007 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
2008 if (!lpwfs)
2010 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2011 return FALSE;
2014 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2016 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2017 goto lend;
2020 if (lpwfs->download_in_progress != NULL)
2022 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2023 goto lend;
2026 if (!lpszSrc || !lpszDest)
2028 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2029 goto lend;
2032 hIC = lpwfs->lpAppInfo;
2033 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2035 WORKREQUEST workRequest;
2036 struct WORKREQ_FTPRENAMEFILEW *req;
2038 workRequest.asyncproc = AsyncFtpRenameFileProc;
2039 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
2040 req = &workRequest.u.FtpRenameFileW;
2041 req->lpszSrcFile = WININET_strdupW(lpszSrc);
2042 req->lpszDestFile = WININET_strdupW(lpszDest);
2044 r = INTERNET_AsyncCall(&workRequest);
2046 else
2048 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2051 lend:
2052 WININET_Release( &lpwfs->hdr );
2054 return r;
2057 /***********************************************************************
2058 * FTP_FtpRenameFileW (Internal)
2060 * Rename a file on the ftp server
2062 * RETURNS
2063 * TRUE on success
2064 * FALSE on failure
2067 BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs,
2068 LPCWSTR lpszSrc, LPCWSTR lpszDest)
2070 INT nResCode;
2071 BOOL bSuccess = FALSE;
2072 LPWININETAPPINFOW hIC = NULL;
2074 TRACE("\n");
2076 /* Clear any error information */
2077 INTERNET_SetLastError(0);
2079 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2080 goto lend;
2082 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2083 if (nResCode == 350)
2085 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2086 goto lend;
2088 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2091 if (nResCode == 250)
2092 bSuccess = TRUE;
2093 else
2094 FTP_SetResponseError(nResCode);
2096 lend:
2097 hIC = lpwfs->lpAppInfo;
2098 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2100 INTERNET_ASYNC_RESULT iar;
2102 iar.dwResult = (DWORD)bSuccess;
2103 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2104 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2105 &iar, sizeof(INTERNET_ASYNC_RESULT));
2108 return bSuccess;
2111 /***********************************************************************
2112 * FtpCommandA (WININET.@)
2114 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2115 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2117 BOOL r;
2118 WCHAR *cmdW;
2120 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2121 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2123 if (fExpectResponse)
2125 FIXME("data connection not supported\n");
2126 return FALSE;
2129 if (!lpszCommand || !lpszCommand[0])
2131 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2132 return FALSE;
2135 if (!(cmdW = WININET_strdup_AtoW(lpszCommand)))
2137 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2138 return FALSE;
2141 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2143 HeapFree(GetProcessHeap(), 0, cmdW);
2144 return r;
2147 /***********************************************************************
2148 * FtpCommandW (WININET.@)
2150 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2151 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2153 BOOL r = FALSE;
2154 LPWININETFTPSESSIONW lpwfs;
2155 LPSTR cmd = NULL;
2156 DWORD len, nBytesSent= 0;
2157 INT nResCode, nRC = 0;
2159 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2160 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2162 if (!lpszCommand || !lpszCommand[0])
2164 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2165 return FALSE;
2168 if (fExpectResponse)
2170 FIXME("data connection not supported\n");
2171 return FALSE;
2174 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
2175 if (!lpwfs)
2177 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2178 return FALSE;
2181 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2183 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2184 goto lend;
2187 if (lpwfs->download_in_progress != NULL)
2189 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2190 goto lend;
2193 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2194 if ((cmd = HeapAlloc(GetProcessHeap(), 0, len )))
2195 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2196 else
2198 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2199 goto lend;
2202 strcat(cmd, szCRLF);
2203 len--;
2205 TRACE("Sending (%s) len(%d)\n", cmd, len);
2206 while ((nBytesSent < len) && (nRC != -1))
2208 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2209 if (nRC != -1)
2211 nBytesSent += nRC;
2212 TRACE("Sent %d bytes\n", nRC);
2216 if (nBytesSent)
2218 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2219 if (nResCode > 0 && nResCode < 400)
2220 r = TRUE;
2221 else
2222 FTP_SetResponseError(nResCode);
2225 lend:
2226 WININET_Release( &lpwfs->hdr );
2227 HeapFree(GetProcessHeap(), 0, cmd);
2228 return r;
2232 /***********************************************************************
2233 * FTPSESSION_Destroy (internal)
2235 * Deallocate session handle
2237 static void FTPSESSION_Destroy(object_header_t *hdr)
2239 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2241 TRACE("\n");
2243 WININET_Release(&lpwfs->lpAppInfo->hdr);
2245 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2246 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2247 HeapFree(GetProcessHeap(), 0, lpwfs);
2250 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2252 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2254 TRACE("\n");
2256 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2257 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2259 if (lpwfs->download_in_progress != NULL)
2260 lpwfs->download_in_progress->session_deleted = TRUE;
2262 if (lpwfs->sndSocket != -1)
2263 closesocket(lpwfs->sndSocket);
2265 if (lpwfs->lstnSocket != -1)
2266 closesocket(lpwfs->lstnSocket);
2268 if (lpwfs->pasvSocket != -1)
2269 closesocket(lpwfs->pasvSocket);
2271 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2272 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2275 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2277 switch(option) {
2278 case INTERNET_OPTION_HANDLE_TYPE:
2279 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2281 if (*size < sizeof(ULONG))
2282 return ERROR_INSUFFICIENT_BUFFER;
2284 *size = sizeof(DWORD);
2285 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2286 return ERROR_SUCCESS;
2289 return INET_QueryOption(option, buffer, size, unicode);
2292 static const object_vtbl_t FTPSESSIONVtbl = {
2293 FTPSESSION_Destroy,
2294 FTPSESSION_CloseConnection,
2295 FTPSESSION_QueryOption,
2296 NULL,
2297 NULL,
2298 NULL,
2299 NULL,
2300 NULL,
2301 NULL
2305 /***********************************************************************
2306 * FTP_Connect (internal)
2308 * Connect to a ftp server
2310 * RETURNS
2311 * HINTERNET a session handle on success
2312 * NULL on failure
2314 * NOTES:
2316 * Windows uses 'anonymous' as the username, when given a NULL username
2317 * and a NULL password. The password is first looked up in:
2319 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2321 * If this entry is not present it uses the current username as the password.
2325 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
2326 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2327 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2328 DWORD dwInternalFlags)
2330 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2331 'M','i','c','r','o','s','o','f','t','\\',
2332 'W','i','n','d','o','w','s','\\',
2333 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2334 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2335 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2336 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2337 static const WCHAR szEmpty[] = {'\0'};
2338 struct sockaddr_in socketAddr;
2339 INT nsocket = -1;
2340 UINT sock_namelen;
2341 BOOL bSuccess = FALSE;
2342 LPWININETFTPSESSIONW lpwfs = NULL;
2343 HINTERNET handle = NULL;
2345 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2346 hIC, debugstr_w(lpszServerName),
2347 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2349 assert( hIC->hdr.htype == WH_HINIT );
2351 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2353 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2354 goto lerror;
2357 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
2358 if (NULL == lpwfs)
2360 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2361 goto lerror;
2364 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2365 nServerPort = INTERNET_DEFAULT_FTP_PORT;
2367 lpwfs->hdr.htype = WH_HFTPSESSION;
2368 lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
2369 lpwfs->hdr.dwFlags = dwFlags;
2370 lpwfs->hdr.dwContext = dwContext;
2371 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2372 lpwfs->hdr.refs = 1;
2373 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2374 lpwfs->download_in_progress = NULL;
2375 lpwfs->sndSocket = -1;
2376 lpwfs->lstnSocket = -1;
2377 lpwfs->pasvSocket = -1;
2379 WININET_AddRef( &hIC->hdr );
2380 lpwfs->lpAppInfo = hIC;
2381 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2383 handle = WININET_AllocHandle( &lpwfs->hdr );
2384 if( !handle )
2386 ERR("Failed to alloc handle\n");
2387 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2388 goto lerror;
2391 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2392 if(strchrW(hIC->lpszProxy, ' '))
2393 FIXME("Several proxies not implemented.\n");
2394 if(hIC->lpszProxyBypass)
2395 FIXME("Proxy bypass is ignored.\n");
2397 if (!lpszUserName || !strlenW(lpszUserName)) {
2398 HKEY key;
2399 WCHAR szPassword[MAX_PATH];
2400 DWORD len = sizeof(szPassword);
2402 lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
2404 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2405 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2406 /* Nothing in the registry, get the username and use that as the password */
2407 if (!GetUserNameW(szPassword, &len)) {
2408 /* Should never get here, but use an empty password as failsafe */
2409 strcpyW(szPassword, szEmpty);
2412 RegCloseKey(key);
2414 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2415 lpwfs->lpszPassword = WININET_strdupW(szPassword);
2417 else {
2418 lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
2420 if (lpszPassword)
2421 lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
2422 else
2423 lpwfs->lpszPassword = WININET_strdupW(szEmpty);
2426 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2427 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2429 INTERNET_ASYNC_RESULT iar;
2431 iar.dwResult = (DWORD_PTR)handle;
2432 iar.dwError = ERROR_SUCCESS;
2434 SendAsyncCallback(&hIC->hdr, dwContext,
2435 INTERNET_STATUS_HANDLE_CREATED, &iar,
2436 sizeof(INTERNET_ASYNC_RESULT));
2439 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2440 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2442 sock_namelen = sizeof(socketAddr);
2443 if (!GetAddress(lpszServerName, nServerPort,
2444 (struct sockaddr *)&socketAddr, &sock_namelen))
2446 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2447 goto lerror;
2450 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2451 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2453 if (socketAddr.sin_family != AF_INET)
2455 WARN("unsupported address family %d\n", socketAddr.sin_family);
2456 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2457 goto lerror;
2459 nsocket = socket(AF_INET,SOCK_STREAM,0);
2460 if (nsocket == -1)
2462 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2463 goto lerror;
2466 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2467 &socketAddr, sock_namelen);
2469 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2471 ERR("Unable to connect (%s)\n", strerror(errno));
2472 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2473 closesocket(nsocket);
2475 else
2477 TRACE("Connected to server\n");
2478 lpwfs->sndSocket = nsocket;
2479 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2480 &socketAddr, sock_namelen);
2482 sock_namelen = sizeof(lpwfs->socketAddress);
2483 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2485 if (FTP_ConnectToHost(lpwfs))
2487 TRACE("Successfully logged into server\n");
2488 bSuccess = TRUE;
2492 lerror:
2493 if (lpwfs) WININET_Release( &lpwfs->hdr );
2495 if (!bSuccess && handle)
2497 WININET_FreeHandle( handle );
2498 handle = NULL;
2501 return handle;
2505 /***********************************************************************
2506 * FTP_ConnectToHost (internal)
2508 * Connect to a ftp server
2510 * RETURNS
2511 * TRUE on success
2512 * NULL on failure
2515 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
2517 INT nResCode;
2518 BOOL bSuccess = FALSE;
2520 TRACE("\n");
2521 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2523 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2524 goto lend;
2526 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2527 if (nResCode)
2529 /* Login successful... */
2530 if (nResCode == 230)
2531 bSuccess = TRUE;
2532 /* User name okay, need password... */
2533 else if (nResCode == 331)
2534 bSuccess = FTP_SendPassword(lpwfs);
2535 /* Need account for login... */
2536 else if (nResCode == 332)
2537 bSuccess = FTP_SendAccount(lpwfs);
2538 else
2539 FTP_SetResponseError(nResCode);
2542 TRACE("Returning %d\n", bSuccess);
2543 lend:
2544 return bSuccess;
2548 /***********************************************************************
2549 * FTP_SendCommandA (internal)
2551 * Send command to server
2553 * RETURNS
2554 * TRUE on success
2555 * NULL on failure
2558 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2559 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2561 DWORD len;
2562 CHAR *buf;
2563 DWORD nBytesSent = 0;
2564 int nRC = 0;
2565 DWORD dwParamLen;
2567 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2569 if (lpfnStatusCB)
2571 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2574 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2575 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2576 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2578 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2579 return FALSE;
2581 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2582 dwParamLen ? lpszParam : "", szCRLF);
2584 TRACE("Sending (%s) len(%d)\n", buf, len);
2585 while((nBytesSent < len) && (nRC != -1))
2587 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2588 nBytesSent += nRC;
2591 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2593 if (lpfnStatusCB)
2595 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2596 &nBytesSent, sizeof(DWORD));
2599 TRACE("Sent %d bytes\n", nBytesSent);
2600 return (nRC != -1);
2603 /***********************************************************************
2604 * FTP_SendCommand (internal)
2606 * Send command to server
2608 * RETURNS
2609 * TRUE on success
2610 * NULL on failure
2613 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2614 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2616 BOOL ret;
2617 LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
2618 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2619 HeapFree(GetProcessHeap(), 0, lpszParamA);
2620 return ret;
2623 /***********************************************************************
2624 * FTP_ReceiveResponse (internal)
2626 * Receive response from server
2628 * RETURNS
2629 * Reply code on success
2630 * 0 on failure
2633 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD_PTR dwContext)
2635 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2636 DWORD nRecv;
2637 INT rc = 0;
2638 char firstprefix[5];
2639 BOOL multiline = FALSE;
2641 TRACE("socket(%d)\n", lpwfs->sndSocket);
2643 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2645 while(1)
2647 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2648 goto lerror;
2650 if (nRecv >= 3)
2652 if(!multiline)
2654 if(lpszResponse[3] != '-')
2655 break;
2656 else
2657 { /* Start of multiline response. Loop until we get "nnn " */
2658 multiline = TRUE;
2659 memcpy(firstprefix, lpszResponse, 3);
2660 firstprefix[3] = ' ';
2661 firstprefix[4] = '\0';
2664 else
2666 if(!memcmp(firstprefix, lpszResponse, 4))
2667 break;
2672 if (nRecv >= 3)
2674 rc = atoi(lpszResponse);
2676 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2677 &nRecv, sizeof(DWORD));
2680 lerror:
2681 TRACE("return %d\n", rc);
2682 return rc;
2686 /***********************************************************************
2687 * FTP_SendPassword (internal)
2689 * Send password to ftp server
2691 * RETURNS
2692 * TRUE on success
2693 * NULL on failure
2696 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2698 INT nResCode;
2699 BOOL bSuccess = FALSE;
2701 TRACE("\n");
2702 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2703 goto lend;
2705 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2706 if (nResCode)
2708 TRACE("Received reply code %d\n", nResCode);
2709 /* Login successful... */
2710 if (nResCode == 230)
2711 bSuccess = TRUE;
2712 /* Command not implemented, superfluous at the server site... */
2713 /* Need account for login... */
2714 else if (nResCode == 332)
2715 bSuccess = FTP_SendAccount(lpwfs);
2716 else
2717 FTP_SetResponseError(nResCode);
2720 lend:
2721 TRACE("Returning %d\n", bSuccess);
2722 return bSuccess;
2726 /***********************************************************************
2727 * FTP_SendAccount (internal)
2731 * RETURNS
2732 * TRUE on success
2733 * FALSE on failure
2736 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2738 INT nResCode;
2739 BOOL bSuccess = FALSE;
2741 TRACE("\n");
2742 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2743 goto lend;
2745 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2746 if (nResCode)
2747 bSuccess = TRUE;
2748 else
2749 FTP_SetResponseError(nResCode);
2751 lend:
2752 return bSuccess;
2756 /***********************************************************************
2757 * FTP_SendStore (internal)
2759 * Send request to upload file to ftp server
2761 * RETURNS
2762 * TRUE on success
2763 * FALSE on failure
2766 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2768 INT nResCode;
2769 BOOL bSuccess = FALSE;
2771 TRACE("\n");
2772 if (!FTP_InitListenSocket(lpwfs))
2773 goto lend;
2775 if (!FTP_SendType(lpwfs, dwType))
2776 goto lend;
2778 if (!FTP_SendPortOrPasv(lpwfs))
2779 goto lend;
2781 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2782 goto lend;
2783 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2784 if (nResCode)
2786 if (nResCode == 150 || nResCode == 125)
2787 bSuccess = TRUE;
2788 else
2789 FTP_SetResponseError(nResCode);
2792 lend:
2793 if (!bSuccess && lpwfs->lstnSocket != -1)
2795 closesocket(lpwfs->lstnSocket);
2796 lpwfs->lstnSocket = -1;
2799 return bSuccess;
2803 /***********************************************************************
2804 * FTP_InitListenSocket (internal)
2806 * Create a socket to listen for server response
2808 * RETURNS
2809 * TRUE on success
2810 * FALSE on failure
2813 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2815 BOOL bSuccess = FALSE;
2816 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2818 TRACE("\n");
2820 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2821 if (lpwfs->lstnSocket == -1)
2823 TRACE("Unable to create listening socket\n");
2824 goto lend;
2827 /* We obtain our ip addr from the name of the command channel socket */
2828 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2830 /* and get the system to assign us a port */
2831 lpwfs->lstnSocketAddress.sin_port = htons(0);
2833 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2835 TRACE("Unable to bind socket\n");
2836 goto lend;
2839 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2841 TRACE("listen failed\n");
2842 goto lend;
2845 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2846 bSuccess = TRUE;
2848 lend:
2849 if (!bSuccess && lpwfs->lstnSocket != -1)
2851 closesocket(lpwfs->lstnSocket);
2852 lpwfs->lstnSocket = -1;
2855 return bSuccess;
2859 /***********************************************************************
2860 * FTP_SendType (internal)
2862 * Tell server type of data being transferred
2864 * RETURNS
2865 * TRUE on success
2866 * FALSE on failure
2868 * W98SE doesn't cache the type that's currently set
2869 * (i.e. it sends it always),
2870 * so we probably don't want to do that either.
2872 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2874 INT nResCode;
2875 WCHAR type[] = { 'I','\0' };
2876 BOOL bSuccess = FALSE;
2878 TRACE("\n");
2879 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2880 type[0] = 'A';
2882 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2883 goto lend;
2885 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2886 if (nResCode)
2888 if (nResCode == 2)
2889 bSuccess = TRUE;
2890 else
2891 FTP_SetResponseError(nResCode);
2894 lend:
2895 return bSuccess;
2899 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2900 /***********************************************************************
2901 * FTP_GetFileSize (internal)
2903 * Retrieves from the server the size of the given file
2905 * RETURNS
2906 * TRUE on success
2907 * FALSE on failure
2910 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2912 INT nResCode;
2913 BOOL bSuccess = FALSE;
2915 TRACE("\n");
2917 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2918 goto lend;
2920 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2921 if (nResCode)
2923 if (nResCode == 213) {
2924 /* Now parses the output to get the actual file size */
2925 int i;
2926 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2928 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2929 if (lpszResponseBuffer[i] == '\0') return FALSE;
2930 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2932 bSuccess = TRUE;
2933 } else {
2934 FTP_SetResponseError(nResCode);
2938 lend:
2939 return bSuccess;
2941 #endif
2944 /***********************************************************************
2945 * FTP_SendPort (internal)
2947 * Tell server which port to use
2949 * RETURNS
2950 * TRUE on success
2951 * FALSE on failure
2954 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2956 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2957 INT nResCode;
2958 WCHAR szIPAddress[64];
2959 BOOL bSuccess = FALSE;
2960 TRACE("\n");
2962 sprintfW(szIPAddress, szIPFormat,
2963 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2964 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2965 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2966 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2967 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2968 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2970 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2971 goto lend;
2973 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2974 if (nResCode)
2976 if (nResCode == 200)
2977 bSuccess = TRUE;
2978 else
2979 FTP_SetResponseError(nResCode);
2982 lend:
2983 return bSuccess;
2987 /***********************************************************************
2988 * FTP_DoPassive (internal)
2990 * Tell server that we want to do passive transfers
2991 * and connect data socket
2993 * RETURNS
2994 * TRUE on success
2995 * FALSE on failure
2998 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
3000 INT nResCode;
3001 BOOL bSuccess = FALSE;
3003 TRACE("\n");
3004 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3005 goto lend;
3007 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3008 if (nResCode)
3010 if (nResCode == 227)
3012 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3013 LPSTR p;
3014 int f[6];
3015 int i;
3016 char *pAddr, *pPort;
3017 INT nsocket = -1;
3018 struct sockaddr_in dataSocketAddress;
3020 p = lpszResponseBuffer+4; /* skip status code */
3021 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3023 if (*p == '\0')
3025 ERR("no address found in response, aborting\n");
3026 goto lend;
3029 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3030 &f[4], &f[5]) != 6)
3032 ERR("unknown response address format '%s', aborting\n", p);
3033 goto lend;
3035 for (i=0; i < 6; i++)
3036 f[i] = f[i] & 0xff;
3038 dataSocketAddress = lpwfs->socketAddress;
3039 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3040 pPort = (char *)&(dataSocketAddress.sin_port);
3041 pAddr[0] = f[0];
3042 pAddr[1] = f[1];
3043 pAddr[2] = f[2];
3044 pAddr[3] = f[3];
3045 pPort[0] = f[4];
3046 pPort[1] = f[5];
3048 nsocket = socket(AF_INET,SOCK_STREAM,0);
3049 if (nsocket == -1)
3050 goto lend;
3052 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3054 ERR("can't connect passive FTP data port.\n");
3055 closesocket(nsocket);
3056 goto lend;
3058 lpwfs->pasvSocket = nsocket;
3059 bSuccess = TRUE;
3061 else
3062 FTP_SetResponseError(nResCode);
3065 lend:
3066 return bSuccess;
3070 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
3072 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3074 if (!FTP_DoPassive(lpwfs))
3075 return FALSE;
3077 else
3079 if (!FTP_SendPort(lpwfs))
3080 return FALSE;
3082 return TRUE;
3086 /***********************************************************************
3087 * FTP_GetDataSocket (internal)
3089 * Either accepts an incoming data socket connection from the server
3090 * or just returns the already opened socket after a PASV command
3091 * in case of passive FTP.
3094 * RETURNS
3095 * TRUE on success
3096 * FALSE on failure
3099 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
3101 struct sockaddr_in saddr;
3102 socklen_t addrlen = sizeof(struct sockaddr);
3104 TRACE("\n");
3105 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3107 *nDataSocket = lpwfs->pasvSocket;
3109 else
3111 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3112 closesocket(lpwfs->lstnSocket);
3113 lpwfs->lstnSocket = -1;
3115 return *nDataSocket != -1;
3119 /***********************************************************************
3120 * FTP_SendData (internal)
3122 * Send data to the server
3124 * RETURNS
3125 * TRUE on success
3126 * FALSE on failure
3129 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
3131 BY_HANDLE_FILE_INFORMATION fi;
3132 DWORD nBytesRead = 0;
3133 DWORD nBytesSent = 0;
3134 DWORD nTotalSent = 0;
3135 DWORD nBytesToSend, nLen;
3136 int nRC = 1;
3137 time_t s_long_time, e_long_time;
3138 LONG nSeconds;
3139 CHAR *lpszBuffer;
3141 TRACE("\n");
3142 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3144 /* Get the size of the file. */
3145 GetFileInformationByHandle(hFile, &fi);
3146 time(&s_long_time);
3150 nBytesToSend = nBytesRead - nBytesSent;
3152 if (nBytesToSend <= 0)
3154 /* Read data from file. */
3155 nBytesSent = 0;
3156 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3157 ERR("Failed reading from file\n");
3159 if (nBytesRead > 0)
3160 nBytesToSend = nBytesRead;
3161 else
3162 break;
3165 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3166 DATA_PACKET_SIZE : nBytesToSend;
3167 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3169 if (nRC != -1)
3171 nBytesSent += nRC;
3172 nTotalSent += nRC;
3175 /* Do some computation to display the status. */
3176 time(&e_long_time);
3177 nSeconds = e_long_time - s_long_time;
3178 if( nSeconds / 60 > 0 )
3180 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3181 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3182 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3184 else
3186 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3187 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3188 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3190 } while (nRC != -1);
3192 TRACE("file transfer complete!\n");
3194 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3196 return nTotalSent;
3200 /***********************************************************************
3201 * FTP_SendRetrieve (internal)
3203 * Send request to retrieve a file
3205 * RETURNS
3206 * Number of bytes to be received on success
3207 * 0 on failure
3210 static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3212 INT nResCode;
3213 BOOL ret;
3215 TRACE("\n");
3216 if (!(ret = FTP_InitListenSocket(lpwfs)))
3217 goto lend;
3219 if (!(ret = FTP_SendType(lpwfs, dwType)))
3220 goto lend;
3222 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3223 goto lend;
3225 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3226 goto lend;
3228 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3229 if ((nResCode != 125) && (nResCode != 150)) {
3230 /* That means that we got an error getting the file. */
3231 FTP_SetResponseError(nResCode);
3232 ret = FALSE;
3235 lend:
3236 if (!ret && lpwfs->lstnSocket != -1)
3238 closesocket(lpwfs->lstnSocket);
3239 lpwfs->lstnSocket = -1;
3242 return ret;
3246 /***********************************************************************
3247 * FTP_RetrieveData (internal)
3249 * Retrieve data from server
3251 * RETURNS
3252 * TRUE on success
3253 * FALSE on failure
3256 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
3258 DWORD nBytesWritten;
3259 INT nRC = 0;
3260 CHAR *lpszBuffer;
3262 TRACE("\n");
3264 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3265 if (NULL == lpszBuffer)
3267 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3268 return FALSE;
3271 while (nRC != -1)
3273 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3274 if (nRC != -1)
3276 /* other side closed socket. */
3277 if (nRC == 0)
3278 goto recv_end;
3279 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3283 TRACE("Data transfer complete\n");
3285 recv_end:
3286 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3288 return (nRC != -1);
3291 /***********************************************************************
3292 * FTPFINDNEXT_Destroy (internal)
3294 * Deallocate session handle
3296 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3298 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3299 DWORD i;
3301 TRACE("\n");
3303 WININET_Release(&lpwfn->lpFtpSession->hdr);
3305 for (i = 0; i < lpwfn->size; i++)
3307 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3310 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3311 HeapFree(GetProcessHeap(), 0, lpwfn);
3314 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3316 WIN32_FIND_DATAW *find_data = data;
3317 DWORD res = ERROR_SUCCESS;
3319 TRACE("index(%d) size(%d)\n", find->index, find->size);
3321 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3323 if (find->index < find->size) {
3324 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3325 find->index++;
3327 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3328 }else {
3329 res = ERROR_NO_MORE_FILES;
3332 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3334 INTERNET_ASYNC_RESULT iar;
3336 iar.dwResult = (res == ERROR_SUCCESS);
3337 iar.dwError = res;
3339 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3340 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3341 sizeof(INTERNET_ASYNC_RESULT));
3344 return res;
3347 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3349 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3351 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3354 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3356 switch(option) {
3357 case INTERNET_OPTION_HANDLE_TYPE:
3358 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3360 if (*size < sizeof(ULONG))
3361 return ERROR_INSUFFICIENT_BUFFER;
3363 *size = sizeof(DWORD);
3364 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3365 return ERROR_SUCCESS;
3368 return INET_QueryOption(option, buffer, size, unicode);
3371 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3373 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3375 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3377 WORKREQUEST workRequest;
3378 struct WORKREQ_FTPFINDNEXTW *req;
3380 workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3381 workRequest.hdr = WININET_AddRef( &find->hdr );
3382 req = &workRequest.u.FtpFindNextW;
3383 req->lpFindFileData = data;
3385 INTERNET_AsyncCall(&workRequest);
3387 return ERROR_SUCCESS;
3390 return FTPFINDNEXT_FindNextFileProc(find, data);
3393 static const object_vtbl_t FTPFINDNEXTVtbl = {
3394 FTPFINDNEXT_Destroy,
3395 NULL,
3396 FTPFINDNEXT_QueryOption,
3397 NULL,
3398 NULL,
3399 NULL,
3400 NULL,
3401 NULL,
3402 NULL,
3403 FTPFINDNEXT_FindNextFileW
3406 /***********************************************************************
3407 * FTP_ReceiveFileList (internal)
3409 * Read file list from server
3411 * RETURNS
3412 * Handle to file list on success
3413 * NULL on failure
3416 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3417 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3419 DWORD dwSize = 0;
3420 LPFILEPROPERTIESW lpafp = NULL;
3421 LPWININETFTPFINDNEXTW lpwfn = NULL;
3422 HINTERNET handle = 0;
3424 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3426 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3428 if(lpFindFileData)
3429 FTP_ConvertFileProp(lpafp, lpFindFileData);
3431 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3432 if (lpwfn)
3434 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3435 lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3436 lpwfn->hdr.dwContext = dwContext;
3437 lpwfn->hdr.refs = 1;
3438 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3439 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3440 lpwfn->size = dwSize;
3441 lpwfn->lpafp = lpafp;
3443 WININET_AddRef( &lpwfs->hdr );
3444 lpwfn->lpFtpSession = lpwfs;
3445 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3447 handle = WININET_AllocHandle( &lpwfn->hdr );
3451 if( lpwfn )
3452 WININET_Release( &lpwfn->hdr );
3454 TRACE("Matched %d files\n", dwSize);
3455 return handle;
3459 /***********************************************************************
3460 * FTP_ConvertFileProp (internal)
3462 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3464 * RETURNS
3465 * TRUE on success
3466 * FALSE on failure
3469 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3471 BOOL bSuccess = FALSE;
3473 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3475 if (lpafp)
3477 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3478 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3479 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3481 /* Not all fields are filled in */
3482 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3483 lpFindFileData->nFileSizeLow = lpafp->nSize;
3485 if (lpafp->bIsDirectory)
3486 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3488 if (lpafp->lpszName)
3489 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3491 bSuccess = TRUE;
3494 return bSuccess;
3497 /***********************************************************************
3498 * FTP_ParseNextFile (internal)
3500 * Parse the next line in file listing
3502 * RETURNS
3503 * TRUE on success
3504 * FALSE on failure
3506 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3508 static const char szSpace[] = " \t";
3509 DWORD nBufLen;
3510 char *pszLine;
3511 char *pszToken;
3512 char *pszTmp;
3513 BOOL found = FALSE;
3514 int i;
3516 lpfp->lpszName = NULL;
3517 do {
3518 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3519 return FALSE;
3521 pszToken = strtok(pszLine, szSpace);
3522 /* ls format
3523 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3525 * For instance:
3526 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3528 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3529 if(!FTP_ParsePermission(pszToken, lpfp))
3530 lpfp->bIsDirectory = FALSE;
3531 for(i=0; i<=3; i++) {
3532 if(!(pszToken = strtok(NULL, szSpace)))
3533 break;
3535 if(!pszToken) continue;
3536 if(lpfp->bIsDirectory) {
3537 TRACE("Is directory\n");
3538 lpfp->nSize = 0;
3540 else {
3541 TRACE("Size: %s\n", pszToken);
3542 lpfp->nSize = atol(pszToken);
3545 lpfp->tmLastModified.wSecond = 0;
3546 lpfp->tmLastModified.wMinute = 0;
3547 lpfp->tmLastModified.wHour = 0;
3548 lpfp->tmLastModified.wDay = 0;
3549 lpfp->tmLastModified.wMonth = 0;
3550 lpfp->tmLastModified.wYear = 0;
3552 /* Determine month */
3553 pszToken = strtok(NULL, szSpace);
3554 if(!pszToken) continue;
3555 if(strlen(pszToken) >= 3) {
3556 pszToken[3] = 0;
3557 if((pszTmp = StrStrIA(szMonths, pszToken)))
3558 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3560 /* Determine day */
3561 pszToken = strtok(NULL, szSpace);
3562 if(!pszToken) continue;
3563 lpfp->tmLastModified.wDay = atoi(pszToken);
3564 /* Determine time or year */
3565 pszToken = strtok(NULL, szSpace);
3566 if(!pszToken) continue;
3567 if((pszTmp = strchr(pszToken, ':'))) {
3568 SYSTEMTIME curr_time;
3569 *pszTmp = 0;
3570 pszTmp++;
3571 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3572 lpfp->tmLastModified.wHour = atoi(pszToken);
3573 GetLocalTime( &curr_time );
3574 lpfp->tmLastModified.wYear = curr_time.wYear;
3576 else {
3577 lpfp->tmLastModified.wYear = atoi(pszToken);
3578 lpfp->tmLastModified.wHour = 12;
3580 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3581 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3582 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3584 pszToken = strtok(NULL, szSpace);
3585 if(!pszToken) continue;
3586 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3587 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3589 /* NT way of parsing ... :
3591 07-13-03 08:55PM <DIR> sakpatch
3592 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3594 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3595 int mon, mday, year, hour, min;
3596 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3598 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3599 lpfp->tmLastModified.wDay = mday;
3600 lpfp->tmLastModified.wMonth = mon;
3601 lpfp->tmLastModified.wYear = year;
3603 /* Hacky and bad Y2K protection :-) */
3604 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3606 pszToken = strtok(NULL, szSpace);
3607 if(!pszToken) continue;
3608 sscanf(pszToken, "%d:%d", &hour, &min);
3609 lpfp->tmLastModified.wHour = hour;
3610 lpfp->tmLastModified.wMinute = min;
3611 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3612 lpfp->tmLastModified.wHour += 12;
3614 lpfp->tmLastModified.wSecond = 0;
3616 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3617 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3618 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3620 pszToken = strtok(NULL, szSpace);
3621 if(!pszToken) continue;
3622 if(!strcasecmp(pszToken, "<DIR>")) {
3623 lpfp->bIsDirectory = TRUE;
3624 lpfp->nSize = 0;
3625 TRACE("Is directory\n");
3627 else {
3628 lpfp->bIsDirectory = FALSE;
3629 lpfp->nSize = atol(pszToken);
3630 TRACE("Size: %d\n", lpfp->nSize);
3633 pszToken = strtok(NULL, szSpace);
3634 if(!pszToken) continue;
3635 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3636 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3638 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3639 else if(pszToken[0] == '+') {
3640 FIXME("EPLF Format not implemented\n");
3643 if(lpfp->lpszName) {
3644 if((lpszSearchFile == NULL) ||
3645 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3646 found = TRUE;
3647 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3649 else {
3650 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3651 lpfp->lpszName = NULL;
3654 } while(!found);
3655 return TRUE;
3658 /***********************************************************************
3659 * FTP_ParseDirectory (internal)
3661 * Parse string of directory information
3663 * RETURNS
3664 * TRUE on success
3665 * FALSE on failure
3667 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3668 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3670 BOOL bSuccess = TRUE;
3671 INT sizeFilePropArray = 500;/*20; */
3672 INT indexFilePropArray = -1;
3674 TRACE("\n");
3676 /* Allocate initial file properties array */
3677 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3678 if (!*lpafp)
3679 return FALSE;
3681 do {
3682 if (indexFilePropArray+1 >= sizeFilePropArray)
3684 LPFILEPROPERTIESW tmpafp;
3686 sizeFilePropArray *= 2;
3687 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3688 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3689 if (NULL == tmpafp)
3691 bSuccess = FALSE;
3692 break;
3695 *lpafp = tmpafp;
3697 indexFilePropArray++;
3698 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3700 if (bSuccess && indexFilePropArray)
3702 if (indexFilePropArray < sizeFilePropArray - 1)
3704 LPFILEPROPERTIESW tmpafp;
3706 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3707 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3708 if (NULL != tmpafp)
3709 *lpafp = tmpafp;
3711 *dwfp = indexFilePropArray;
3713 else
3715 HeapFree(GetProcessHeap(), 0, *lpafp);
3716 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3717 bSuccess = FALSE;
3720 return bSuccess;
3724 /***********************************************************************
3725 * FTP_ParsePermission (internal)
3727 * Parse permission string of directory information
3729 * RETURNS
3730 * TRUE on success
3731 * FALSE on failure
3734 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3736 BOOL bSuccess = TRUE;
3737 unsigned short nPermission = 0;
3738 INT nPos = 1;
3739 INT nLast = 9;
3741 TRACE("\n");
3742 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3744 bSuccess = FALSE;
3745 return bSuccess;
3748 lpfp->bIsDirectory = (*lpszPermission == 'd');
3751 switch (nPos)
3753 case 1:
3754 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3755 break;
3756 case 2:
3757 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3758 break;
3759 case 3:
3760 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3761 break;
3762 case 4:
3763 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3764 break;
3765 case 5:
3766 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3767 break;
3768 case 6:
3769 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3770 break;
3771 case 7:
3772 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3773 break;
3774 case 8:
3775 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3776 break;
3777 case 9:
3778 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3779 break;
3781 nPos++;
3782 }while (nPos <= nLast);
3784 lpfp->permissions = nPermission;
3785 return bSuccess;
3789 /***********************************************************************
3790 * FTP_SetResponseError (internal)
3792 * Set the appropriate error code for a given response from the server
3794 * RETURNS
3797 static DWORD FTP_SetResponseError(DWORD dwResponse)
3799 DWORD dwCode = 0;
3801 switch(dwResponse)
3803 case 425: /* Cannot open data connection. */
3804 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3805 break;
3807 case 426: /* Connection closed, transer aborted. */
3808 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3809 break;
3811 case 530: /* Not logged in. Login incorrect. */
3812 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3813 break;
3815 case 421: /* Service not available - Server may be shutting down. */
3816 case 450: /* File action not taken. File may be busy. */
3817 case 451: /* Action aborted. Server error. */
3818 case 452: /* Action not taken. Insufficient storage space on server. */
3819 case 500: /* Syntax error. Command unrecognized. */
3820 case 501: /* Syntax error. Error in parameters or arguments. */
3821 case 502: /* Command not implemented. */
3822 case 503: /* Bad sequence of commands. */
3823 case 504: /* Command not implemented for that parameter. */
3824 case 532: /* Need account for storing files */
3825 case 550: /* File action not taken. File not found or no access. */
3826 case 551: /* Requested action aborted. Page type unknown */
3827 case 552: /* Action aborted. Exceeded storage allocation */
3828 case 553: /* Action not taken. File name not allowed. */
3830 default:
3831 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3832 break;
3835 INTERNET_SetLastError(dwCode);
3836 return dwCode;