dxgi: Implement IDXGIFactory::GetParent().
[wine/testsucceed.git] / dlls / wininet / ftp.c
blobbbef8cad676b9c04ed92b4f953c9241fa2633fcd
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 _ftp_session_t ftp_session_t;
73 typedef struct
75 object_header_t hdr;
76 ftp_session_t *lpFtpSession;
77 BOOL session_deleted;
78 int nDataSocket;
79 WCHAR *cache_file;
80 HANDLE cache_file_handle;
81 } ftp_file_t;
83 struct _ftp_session_t
85 object_header_t hdr;
86 appinfo_t *lpAppInfo;
87 int sndSocket;
88 int lstnSocket;
89 int pasvSocket; /* data socket connected by us in case of passive FTP */
90 ftp_file_t *download_in_progress;
91 struct sockaddr_in socketAddress;
92 struct sockaddr_in lstnSocketAddress;
93 LPWSTR servername;
94 INTERNET_PORT serverport;
95 LPWSTR lpszPassword;
96 LPWSTR lpszUserName;
99 typedef struct
101 BOOL bIsDirectory;
102 LPWSTR lpszName;
103 DWORD nSize;
104 SYSTEMTIME tmLastModified;
105 unsigned short permissions;
106 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
108 typedef struct
110 object_header_t hdr;
111 ftp_session_t *lpFtpSession;
112 DWORD index;
113 DWORD size;
114 LPFILEPROPERTIESW lpafp;
115 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
117 #define DATA_PACKET_SIZE 0x2000
118 #define szCRLF "\r\n"
119 #define MAX_BACKLOG 5
121 /* Testing shows that Windows only accepts dwFlags where the last
122 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
124 #define FTP_CONDITION_MASK 0x0007
126 typedef enum {
127 /* FTP commands with arguments. */
128 FTP_CMD_ACCT,
129 FTP_CMD_CWD,
130 FTP_CMD_DELE,
131 FTP_CMD_MKD,
132 FTP_CMD_PASS,
133 FTP_CMD_PORT,
134 FTP_CMD_RETR,
135 FTP_CMD_RMD,
136 FTP_CMD_RNFR,
137 FTP_CMD_RNTO,
138 FTP_CMD_STOR,
139 FTP_CMD_TYPE,
140 FTP_CMD_USER,
141 FTP_CMD_SIZE,
143 /* FTP commands without arguments. */
144 FTP_CMD_ABOR,
145 FTP_CMD_LIST,
146 FTP_CMD_NLST,
147 FTP_CMD_PASV,
148 FTP_CMD_PWD,
149 FTP_CMD_QUIT,
150 } FTP_COMMAND;
152 static const CHAR *const szFtpCommands[] = {
153 "ACCT",
154 "CWD",
155 "DELE",
156 "MKD",
157 "PASS",
158 "PORT",
159 "RETR",
160 "RMD",
161 "RNFR",
162 "RNTO",
163 "STOR",
164 "TYPE",
165 "USER",
166 "SIZE",
167 "ABOR",
168 "LIST",
169 "NLST",
170 "PASV",
171 "PWD",
172 "QUIT",
175 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
176 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
178 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
179 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext);
180 static BOOL FTP_SendStore(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
181 static BOOL FTP_GetDataSocket(ftp_session_t*, LPINT nDataSocket);
182 static BOOL FTP_SendData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
183 static INT FTP_ReceiveResponse(ftp_session_t*, DWORD_PTR dwContext);
184 static BOOL FTP_SendRetrieve(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
185 static BOOL FTP_RetrieveFileData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
186 static BOOL FTP_InitListenSocket(ftp_session_t*);
187 static BOOL FTP_ConnectToHost(ftp_session_t*);
188 static BOOL FTP_SendPassword(ftp_session_t*);
189 static BOOL FTP_SendAccount(ftp_session_t*);
190 static BOOL FTP_SendType(ftp_session_t*, DWORD dwType);
191 static BOOL FTP_SendPort(ftp_session_t*);
192 static BOOL FTP_DoPassive(ftp_session_t*);
193 static BOOL FTP_SendPortOrPasv(ftp_session_t*);
194 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
195 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
196 static BOOL FTP_ParseDirectory(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
197 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
198 static HINTERNET FTP_ReceiveFileList(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
199 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
200 static DWORD FTP_SetResponseError(DWORD dwResponse);
201 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
202 static BOOL FTP_FtpPutFileW(ftp_session_t*, LPCWSTR lpszLocalFile,
203 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
204 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
205 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
206 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t*,
207 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
208 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t*, LPWSTR lpszCurrentDirectory,
209 LPDWORD lpdwCurrentDirectory);
210 static BOOL FTP_FtpRenameFileW(ftp_session_t*, LPCWSTR lpszSrc, LPCWSTR lpszDest);
211 static BOOL FTP_FtpRemoveDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
212 static BOOL FTP_FtpDeleteFileW(ftp_session_t*, LPCWSTR lpszFileName);
213 static BOOL FTP_FtpGetFileW(ftp_session_t*, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
214 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
215 DWORD_PTR dwContext);
218 /***********************************************************************
219 * FtpPutFileA (WININET.@)
221 * Uploads a file to the FTP server
223 * RETURNS
224 * TRUE on success
225 * FALSE on failure
228 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
229 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
231 LPWSTR lpwzLocalFile;
232 LPWSTR lpwzNewRemoteFile;
233 BOOL ret;
235 lpwzLocalFile = heap_strdupAtoW(lpszLocalFile);
236 lpwzNewRemoteFile = heap_strdupAtoW(lpszNewRemoteFile);
237 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
238 dwFlags, dwContext);
239 HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
240 HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
241 return ret;
244 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
246 struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
247 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
249 TRACE("%p\n", lpwfs);
251 FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
252 req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
254 HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
255 HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
258 /***********************************************************************
259 * FtpPutFileW (WININET.@)
261 * Uploads a file to the FTP server
263 * RETURNS
264 * TRUE on success
265 * FALSE on failure
268 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
269 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
271 ftp_session_t *lpwfs;
272 appinfo_t *hIC = NULL;
273 BOOL r = FALSE;
275 if (!lpszLocalFile || !lpszNewRemoteFile)
277 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
278 return FALSE;
281 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
282 if (!lpwfs)
284 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
285 return FALSE;
288 if (WH_HFTPSESSION != lpwfs->hdr.htype)
290 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
291 goto lend;
294 if (lpwfs->download_in_progress != NULL)
296 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
297 goto lend;
300 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
302 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
303 goto lend;
306 hIC = lpwfs->lpAppInfo;
307 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
309 WORKREQUEST workRequest;
310 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
312 workRequest.asyncproc = AsyncFtpPutFileProc;
313 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
314 req->lpszLocalFile = heap_strdupW(lpszLocalFile);
315 req->lpszNewRemoteFile = heap_strdupW(lpszNewRemoteFile);
316 req->dwFlags = dwFlags;
317 req->dwContext = dwContext;
319 r = INTERNET_AsyncCall(&workRequest);
321 else
323 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
324 lpszNewRemoteFile, dwFlags, dwContext);
327 lend:
328 WININET_Release( &lpwfs->hdr );
330 return r;
333 /***********************************************************************
334 * FTP_FtpPutFileW (Internal)
336 * Uploads a file to the FTP server
338 * RETURNS
339 * TRUE on success
340 * FALSE on failure
343 static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
344 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
346 HANDLE hFile;
347 BOOL bSuccess = FALSE;
348 appinfo_t *hIC = NULL;
349 INT nResCode;
351 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
353 /* Clear any error information */
354 INTERNET_SetLastError(0);
356 /* Open file to be uploaded */
357 if (INVALID_HANDLE_VALUE ==
358 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
359 /* Let CreateFile set the appropriate error */
360 return FALSE;
362 hIC = lpwfs->lpAppInfo;
364 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
366 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
368 INT nDataSocket;
370 /* Get data socket to server */
371 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
373 FTP_SendData(lpwfs, nDataSocket, hFile);
374 closesocket(nDataSocket);
375 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
376 if (nResCode)
378 if (nResCode == 226)
379 bSuccess = TRUE;
380 else
381 FTP_SetResponseError(nResCode);
386 if (lpwfs->lstnSocket != -1)
388 closesocket(lpwfs->lstnSocket);
389 lpwfs->lstnSocket = -1;
392 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
394 INTERNET_ASYNC_RESULT iar;
396 iar.dwResult = (DWORD)bSuccess;
397 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
398 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
399 &iar, sizeof(INTERNET_ASYNC_RESULT));
402 CloseHandle(hFile);
404 return bSuccess;
408 /***********************************************************************
409 * FtpSetCurrentDirectoryA (WININET.@)
411 * Change the working directory on the FTP server
413 * RETURNS
414 * TRUE on success
415 * FALSE on failure
418 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
420 LPWSTR lpwzDirectory;
421 BOOL ret;
423 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
424 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
425 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
426 return ret;
430 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
432 struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
433 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
435 TRACE("%p\n", lpwfs);
437 FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
438 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
441 /***********************************************************************
442 * FtpSetCurrentDirectoryW (WININET.@)
444 * Change the working directory on the FTP server
446 * RETURNS
447 * TRUE on success
448 * FALSE on failure
451 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
453 ftp_session_t *lpwfs = NULL;
454 appinfo_t *hIC = NULL;
455 BOOL r = FALSE;
457 if (!lpszDirectory)
459 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
460 goto lend;
463 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
464 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
466 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
467 goto lend;
470 if (lpwfs->download_in_progress != NULL)
472 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
473 goto lend;
476 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
478 hIC = lpwfs->lpAppInfo;
479 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
481 WORKREQUEST workRequest;
482 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
484 workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
485 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
486 req = &workRequest.u.FtpSetCurrentDirectoryW;
487 req->lpszDirectory = heap_strdupW(lpszDirectory);
489 r = INTERNET_AsyncCall(&workRequest);
491 else
493 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
496 lend:
497 if( lpwfs )
498 WININET_Release( &lpwfs->hdr );
500 return r;
504 /***********************************************************************
505 * FTP_FtpSetCurrentDirectoryW (Internal)
507 * Change the working directory on the FTP server
509 * RETURNS
510 * TRUE on success
511 * FALSE on failure
514 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
516 INT nResCode;
517 appinfo_t *hIC = NULL;
518 DWORD bSuccess = FALSE;
520 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
522 /* Clear any error information */
523 INTERNET_SetLastError(0);
525 hIC = lpwfs->lpAppInfo;
526 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
527 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
528 goto lend;
530 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
532 if (nResCode)
534 if (nResCode == 250)
535 bSuccess = TRUE;
536 else
537 FTP_SetResponseError(nResCode);
540 lend:
541 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
543 INTERNET_ASYNC_RESULT iar;
545 iar.dwResult = bSuccess;
546 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
547 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
548 &iar, sizeof(INTERNET_ASYNC_RESULT));
550 return bSuccess;
554 /***********************************************************************
555 * FtpCreateDirectoryA (WININET.@)
557 * Create new directory on the FTP server
559 * RETURNS
560 * TRUE on success
561 * FALSE on failure
564 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
566 LPWSTR lpwzDirectory;
567 BOOL ret;
569 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
570 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
571 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
572 return ret;
576 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
578 struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
579 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
581 TRACE(" %p\n", lpwfs);
583 FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
584 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
587 /***********************************************************************
588 * FtpCreateDirectoryW (WININET.@)
590 * Create new directory on the FTP server
592 * RETURNS
593 * TRUE on success
594 * FALSE on failure
597 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
599 ftp_session_t *lpwfs;
600 appinfo_t *hIC = NULL;
601 BOOL r = FALSE;
603 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
604 if (!lpwfs)
606 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
607 return FALSE;
610 if (WH_HFTPSESSION != lpwfs->hdr.htype)
612 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
613 goto lend;
616 if (lpwfs->download_in_progress != NULL)
618 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
619 goto lend;
622 if (!lpszDirectory)
624 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
625 goto lend;
628 hIC = lpwfs->lpAppInfo;
629 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
631 WORKREQUEST workRequest;
632 struct WORKREQ_FTPCREATEDIRECTORYW *req;
634 workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
635 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
636 req = &workRequest.u.FtpCreateDirectoryW;
637 req->lpszDirectory = heap_strdupW(lpszDirectory);
639 r = INTERNET_AsyncCall(&workRequest);
641 else
643 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
645 lend:
646 WININET_Release( &lpwfs->hdr );
648 return r;
652 /***********************************************************************
653 * FTP_FtpCreateDirectoryW (Internal)
655 * Create new directory on the FTP server
657 * RETURNS
658 * TRUE on success
659 * FALSE on failure
662 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
664 INT nResCode;
665 BOOL bSuccess = FALSE;
666 appinfo_t *hIC = NULL;
668 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
670 /* Clear any error information */
671 INTERNET_SetLastError(0);
673 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
674 goto lend;
676 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
677 if (nResCode)
679 if (nResCode == 257)
680 bSuccess = TRUE;
681 else
682 FTP_SetResponseError(nResCode);
685 lend:
686 hIC = lpwfs->lpAppInfo;
687 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
689 INTERNET_ASYNC_RESULT iar;
691 iar.dwResult = (DWORD)bSuccess;
692 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
693 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
694 &iar, sizeof(INTERNET_ASYNC_RESULT));
697 return bSuccess;
700 /***********************************************************************
701 * FtpFindFirstFileA (WININET.@)
703 * Search the specified directory
705 * RETURNS
706 * HINTERNET on success
707 * NULL on failure
710 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
711 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
713 LPWSTR lpwzSearchFile;
714 WIN32_FIND_DATAW wfd;
715 LPWIN32_FIND_DATAW lpFindFileDataW;
716 HINTERNET ret;
718 lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
719 lpFindFileDataW = lpFindFileData?&wfd:NULL;
720 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
721 HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
723 if (ret && lpFindFileData)
724 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
726 return ret;
730 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
732 struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
733 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
735 TRACE("%p\n", lpwfs);
737 FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
738 req->lpFindFileData, req->dwFlags, req->dwContext);
739 HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
742 /***********************************************************************
743 * FtpFindFirstFileW (WININET.@)
745 * Search the specified directory
747 * RETURNS
748 * HINTERNET on success
749 * NULL on failure
752 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
753 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
755 ftp_session_t *lpwfs;
756 appinfo_t *hIC = NULL;
757 HINTERNET r = NULL;
759 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
760 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
762 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
763 goto lend;
766 if (lpwfs->download_in_progress != NULL)
768 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
769 goto lend;
772 hIC = lpwfs->lpAppInfo;
773 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
775 WORKREQUEST workRequest;
776 struct WORKREQ_FTPFINDFIRSTFILEW *req;
778 workRequest.asyncproc = AsyncFtpFindFirstFileProc;
779 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
780 req = &workRequest.u.FtpFindFirstFileW;
781 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : heap_strdupW(lpszSearchFile);
782 req->lpFindFileData = lpFindFileData;
783 req->dwFlags = dwFlags;
784 req->dwContext= dwContext;
786 INTERNET_AsyncCall(&workRequest);
787 r = NULL;
789 else
791 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
792 dwFlags, dwContext);
794 lend:
795 if( lpwfs )
796 WININET_Release( &lpwfs->hdr );
798 return r;
802 /***********************************************************************
803 * FTP_FtpFindFirstFileW (Internal)
805 * Search the specified directory
807 * RETURNS
808 * HINTERNET on success
809 * NULL on failure
812 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
813 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
815 INT nResCode;
816 appinfo_t *hIC = NULL;
817 HINTERNET hFindNext = NULL;
819 TRACE("\n");
821 /* Clear any error information */
822 INTERNET_SetLastError(0);
824 if (!FTP_InitListenSocket(lpwfs))
825 goto lend;
827 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
828 goto lend;
830 if (!FTP_SendPortOrPasv(lpwfs))
831 goto lend;
833 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
834 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
835 goto lend;
837 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
838 if (nResCode)
840 if (nResCode == 125 || nResCode == 150)
842 INT nDataSocket;
844 /* Get data socket to server */
845 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
847 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
848 closesocket(nDataSocket);
849 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
850 if (nResCode != 226 && nResCode != 250)
851 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
854 else
855 FTP_SetResponseError(nResCode);
858 lend:
859 if (lpwfs->lstnSocket != -1)
861 closesocket(lpwfs->lstnSocket);
862 lpwfs->lstnSocket = -1;
865 hIC = lpwfs->lpAppInfo;
866 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
868 INTERNET_ASYNC_RESULT iar;
870 if (hFindNext)
872 iar.dwResult = (DWORD_PTR)hFindNext;
873 iar.dwError = ERROR_SUCCESS;
874 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
875 &iar, sizeof(INTERNET_ASYNC_RESULT));
878 iar.dwResult = (DWORD_PTR)hFindNext;
879 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
880 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
881 &iar, sizeof(INTERNET_ASYNC_RESULT));
884 return hFindNext;
888 /***********************************************************************
889 * FtpGetCurrentDirectoryA (WININET.@)
891 * Retrieves the current directory
893 * RETURNS
894 * TRUE on success
895 * FALSE on failure
898 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
899 LPDWORD lpdwCurrentDirectory)
901 WCHAR *dir = NULL;
902 DWORD len;
903 BOOL ret;
905 if(lpdwCurrentDirectory) {
906 len = *lpdwCurrentDirectory;
907 if(lpszCurrentDirectory)
909 dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
910 if (NULL == dir)
912 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
913 return FALSE;
917 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
919 if (ret && lpszCurrentDirectory)
920 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
922 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
923 HeapFree(GetProcessHeap(), 0, dir);
924 return ret;
928 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
930 struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
931 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
933 TRACE("%p\n", lpwfs);
935 FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
938 /***********************************************************************
939 * FtpGetCurrentDirectoryW (WININET.@)
941 * Retrieves the current directory
943 * RETURNS
944 * TRUE on success
945 * FALSE on failure
948 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
949 LPDWORD lpdwCurrentDirectory)
951 ftp_session_t *lpwfs;
952 appinfo_t *hIC = NULL;
953 BOOL r = FALSE;
955 TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
957 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
958 if (NULL == lpwfs)
960 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
961 goto lend;
964 if (WH_HFTPSESSION != lpwfs->hdr.htype)
966 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
967 goto lend;
970 if (!lpdwCurrentDirectory)
972 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
973 goto lend;
976 if (lpszCurrentDirectory == NULL)
978 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
979 goto lend;
982 if (lpwfs->download_in_progress != NULL)
984 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
985 goto lend;
988 hIC = lpwfs->lpAppInfo;
989 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
991 WORKREQUEST workRequest;
992 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
994 workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
995 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
996 req = &workRequest.u.FtpGetCurrentDirectoryW;
997 req->lpszDirectory = lpszCurrentDirectory;
998 req->lpdwDirectory = lpdwCurrentDirectory;
1000 r = INTERNET_AsyncCall(&workRequest);
1002 else
1004 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
1005 lpdwCurrentDirectory);
1008 lend:
1009 if( lpwfs )
1010 WININET_Release( &lpwfs->hdr );
1012 return r;
1016 /***********************************************************************
1017 * FTP_FtpGetCurrentDirectoryW (Internal)
1019 * Retrieves the current directory
1021 * RETURNS
1022 * TRUE on success
1023 * FALSE on failure
1026 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
1027 LPDWORD lpdwCurrentDirectory)
1029 INT nResCode;
1030 appinfo_t *hIC = NULL;
1031 DWORD bSuccess = FALSE;
1033 /* Clear any error information */
1034 INTERNET_SetLastError(0);
1036 hIC = lpwfs->lpAppInfo;
1037 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1038 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1039 goto lend;
1041 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1042 if (nResCode)
1044 if (nResCode == 257) /* Extract directory name */
1046 DWORD firstpos, lastpos, len;
1047 LPWSTR lpszResponseBuffer = heap_strdupAtoW(INTERNET_GetResponseBuffer());
1049 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1051 if ('"' == lpszResponseBuffer[lastpos])
1053 if (!firstpos)
1054 firstpos = lastpos;
1055 else
1056 break;
1059 len = lastpos - firstpos;
1060 if (*lpdwCurrentDirectory >= len)
1062 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1063 lpszCurrentDirectory[len - 1] = 0;
1064 *lpdwCurrentDirectory = len;
1065 bSuccess = TRUE;
1067 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1069 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
1071 else
1072 FTP_SetResponseError(nResCode);
1075 lend:
1076 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1078 INTERNET_ASYNC_RESULT iar;
1080 iar.dwResult = bSuccess;
1081 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1082 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1083 &iar, sizeof(INTERNET_ASYNC_RESULT));
1086 return bSuccess;
1090 /***********************************************************************
1091 * FTPFILE_Destroy(internal)
1093 * Closes the file transfer handle. This also 'cleans' the data queue of
1094 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1097 static void FTPFILE_Destroy(object_header_t *hdr)
1099 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1100 ftp_session_t *lpwfs = lpwh->lpFtpSession;
1101 INT nResCode;
1103 TRACE("\n");
1105 if (lpwh->cache_file_handle != INVALID_HANDLE_VALUE)
1106 CloseHandle(lpwh->cache_file_handle);
1108 HeapFree(GetProcessHeap(), 0, lpwh->cache_file);
1110 if (!lpwh->session_deleted)
1111 lpwfs->download_in_progress = NULL;
1113 if (lpwh->nDataSocket != -1)
1114 closesocket(lpwh->nDataSocket);
1116 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1117 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1119 WININET_Release(&lpwh->lpFtpSession->hdr);
1121 HeapFree(GetProcessHeap(), 0, lpwh);
1124 static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1126 switch(option) {
1127 case INTERNET_OPTION_HANDLE_TYPE:
1128 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1130 if (*size < sizeof(ULONG))
1131 return ERROR_INSUFFICIENT_BUFFER;
1133 *size = sizeof(DWORD);
1134 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1135 return ERROR_SUCCESS;
1136 case INTERNET_OPTION_DATAFILE_NAME:
1138 DWORD required;
1139 ftp_file_t *file = (ftp_file_t *)hdr;
1141 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1143 if (!file->cache_file)
1145 *size = 0;
1146 return ERROR_INTERNET_ITEM_NOT_FOUND;
1148 if (unicode)
1150 required = (lstrlenW(file->cache_file) + 1) * sizeof(WCHAR);
1151 if (*size < required)
1152 return ERROR_INSUFFICIENT_BUFFER;
1154 *size = required;
1155 memcpy(buffer, file->cache_file, *size);
1156 return ERROR_SUCCESS;
1158 else
1160 required = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, NULL, 0, NULL, NULL);
1161 if (required > *size)
1162 return ERROR_INSUFFICIENT_BUFFER;
1164 *size = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, buffer, *size, NULL, NULL);
1165 return ERROR_SUCCESS;
1169 return INET_QueryOption(option, buffer, size, unicode);
1172 static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
1174 ftp_file_t *file = (ftp_file_t*)hdr;
1175 int res;
1176 DWORD error;
1178 if (file->nDataSocket == -1)
1179 return ERROR_INTERNET_DISCONNECTED;
1181 /* FIXME: FTP should use NETCON_ stuff */
1182 res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1183 *read = res>0 ? res : 0;
1185 error = res >= 0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME */
1186 if (error == ERROR_SUCCESS && file->cache_file)
1188 DWORD bytes_written;
1190 if (!WriteFile(file->cache_file_handle, buffer, *read, &bytes_written, NULL))
1191 WARN("WriteFile failed: %u\n", GetLastError());
1193 return error;
1196 static DWORD FTPFILE_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
1197 DWORD flags, DWORD_PTR context)
1199 return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1202 static DWORD FTPFILE_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
1203 DWORD flags, DWORD_PTR context)
1205 return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1208 static DWORD FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1210 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1211 int res;
1213 res = send(lpwh->nDataSocket, buffer, size, 0);
1215 *written = res>0 ? res : 0;
1216 return res >= 0 ? ERROR_SUCCESS : sock_get_error(errno);
1219 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
1221 INTERNET_ASYNC_RESULT iar;
1222 BYTE buffer[4096];
1223 int available;
1225 TRACE("%p\n", file);
1227 available = recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1229 if(available != -1) {
1230 iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1231 iar.dwError = first_notif ? 0 : available;
1232 }else {
1233 iar.dwResult = 0;
1234 iar.dwError = INTERNET_GetLastError();
1237 INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1238 sizeof(INTERNET_ASYNC_RESULT));
1241 static void FTPFILE_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
1243 ftp_file_t *file = (ftp_file_t*)workRequest->hdr;
1245 FTP_ReceiveRequestData(file, FALSE);
1248 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1250 ftp_file_t *file = (ftp_file_t*) hdr;
1251 int retval, unread = 0;
1253 TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
1255 #ifdef FIONREAD
1256 retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
1257 if (!retval)
1258 TRACE("%d bytes of queued, but unread data\n", unread);
1259 #else
1260 FIXME("FIONREAD not available\n");
1261 #endif
1263 *available = unread;
1265 if(!unread) {
1266 BYTE byte;
1268 *available = 0;
1270 retval = recv(file->nDataSocket, &byte, 1, MSG_PEEK);
1271 if(retval > 0) {
1272 WORKREQUEST workRequest;
1274 *available = 0;
1275 workRequest.asyncproc = FTPFILE_AsyncQueryDataAvailableProc;
1276 workRequest.hdr = WININET_AddRef( &file->hdr );
1278 INTERNET_AsyncCall(&workRequest);
1280 return ERROR_IO_PENDING;
1284 return ERROR_SUCCESS;
1288 static const object_vtbl_t FTPFILEVtbl = {
1289 FTPFILE_Destroy,
1290 NULL,
1291 FTPFILE_QueryOption,
1292 NULL,
1293 FTPFILE_ReadFile,
1294 FTPFILE_ReadFileExA,
1295 FTPFILE_ReadFileExW,
1296 FTPFILE_WriteFile,
1297 FTPFILE_QueryDataAvailable,
1298 NULL
1301 /***********************************************************************
1302 * FTP_FtpOpenFileW (Internal)
1304 * Open a remote file for writing or reading
1306 * RETURNS
1307 * HINTERNET handle on success
1308 * NULL on failure
1311 HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1312 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1313 DWORD_PTR dwContext)
1315 INT nDataSocket;
1316 BOOL bSuccess = FALSE;
1317 ftp_file_t *lpwh = NULL;
1318 appinfo_t *hIC = NULL;
1319 HINTERNET handle = NULL;
1321 TRACE("\n");
1323 /* Clear any error information */
1324 INTERNET_SetLastError(0);
1326 if (GENERIC_READ == fdwAccess)
1328 /* Set up socket to retrieve data */
1329 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1331 else if (GENERIC_WRITE == fdwAccess)
1333 /* Set up socket to send data */
1334 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1337 /* Get data socket to server */
1338 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1340 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(ftp_file_t));
1341 lpwh->hdr.htype = WH_HFILE;
1342 lpwh->hdr.vtbl = &FTPFILEVtbl;
1343 lpwh->hdr.dwFlags = dwFlags;
1344 lpwh->hdr.dwContext = dwContext;
1345 lpwh->hdr.dwInternalFlags = 0;
1346 lpwh->hdr.refs = 1;
1347 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1348 lpwh->nDataSocket = nDataSocket;
1349 lpwh->cache_file = NULL;
1350 lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1351 lpwh->session_deleted = FALSE;
1353 WININET_AddRef( &lpwfs->hdr );
1354 lpwh->lpFtpSession = lpwfs;
1355 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1357 handle = WININET_AllocHandle( &lpwh->hdr );
1358 if( !handle )
1359 goto lend;
1361 /* Indicate that a download is currently in progress */
1362 lpwfs->download_in_progress = lpwh;
1365 if (lpwfs->lstnSocket != -1)
1367 closesocket(lpwfs->lstnSocket);
1368 lpwfs->lstnSocket = -1;
1371 if (bSuccess && fdwAccess == GENERIC_READ)
1373 WCHAR filename[MAX_PATH + 1];
1374 URL_COMPONENTSW uc;
1375 DWORD len;
1377 memset(&uc, 0, sizeof(uc));
1378 uc.dwStructSize = sizeof(uc);
1379 uc.nScheme = INTERNET_SCHEME_FTP;
1380 uc.lpszHostName = lpwfs->servername;
1381 uc.nPort = lpwfs->serverport;
1382 uc.lpszUserName = lpwfs->lpszUserName;
1383 uc.lpszUrlPath = heap_strdupW(lpszFileName);
1385 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1387 WCHAR *url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1389 if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1391 lpwh->cache_file = heap_strdupW(filename);
1392 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1393 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1394 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1396 WARN("Could not create cache file: %u\n", GetLastError());
1397 HeapFree(GetProcessHeap(), 0, lpwh->cache_file);
1398 lpwh->cache_file = NULL;
1401 HeapFree(GetProcessHeap(), 0, url);
1403 HeapFree(GetProcessHeap(), 0, uc.lpszUrlPath);
1406 hIC = lpwfs->lpAppInfo;
1407 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1409 INTERNET_ASYNC_RESULT iar;
1411 if (lpwh)
1413 iar.dwResult = (DWORD_PTR)handle;
1414 iar.dwError = ERROR_SUCCESS;
1415 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1416 &iar, sizeof(INTERNET_ASYNC_RESULT));
1419 if(bSuccess) {
1420 FTP_ReceiveRequestData(lpwh, TRUE);
1421 }else {
1422 iar.dwResult = 0;
1423 iar.dwError = INTERNET_GetLastError();
1424 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1425 &iar, sizeof(INTERNET_ASYNC_RESULT));
1429 lend:
1430 if( lpwh )
1431 WININET_Release( &lpwh->hdr );
1433 return handle;
1437 /***********************************************************************
1438 * FtpOpenFileA (WININET.@)
1440 * Open a remote file for writing or reading
1442 * RETURNS
1443 * HINTERNET handle on success
1444 * NULL on failure
1447 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1448 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1449 DWORD_PTR dwContext)
1451 LPWSTR lpwzFileName;
1452 HINTERNET ret;
1454 lpwzFileName = heap_strdupAtoW(lpszFileName);
1455 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1456 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1457 return ret;
1461 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1463 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1464 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1466 TRACE("%p\n", lpwfs);
1468 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1469 req->dwAccess, req->dwFlags, req->dwContext);
1470 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1473 /***********************************************************************
1474 * FtpOpenFileW (WININET.@)
1476 * Open a remote file for writing or reading
1478 * RETURNS
1479 * HINTERNET handle on success
1480 * NULL on failure
1483 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1484 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1485 DWORD_PTR dwContext)
1487 ftp_session_t *lpwfs;
1488 appinfo_t *hIC = NULL;
1489 HINTERNET r = NULL;
1491 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1492 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1494 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1495 if (!lpwfs)
1497 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1498 return FALSE;
1501 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1503 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1504 goto lend;
1507 if ((!lpszFileName) ||
1508 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1509 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1511 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1512 goto lend;
1515 if (lpwfs->download_in_progress != NULL)
1517 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1518 goto lend;
1521 hIC = lpwfs->lpAppInfo;
1522 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1524 WORKREQUEST workRequest;
1525 struct WORKREQ_FTPOPENFILEW *req;
1527 workRequest.asyncproc = AsyncFtpOpenFileProc;
1528 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1529 req = &workRequest.u.FtpOpenFileW;
1530 req->lpszFilename = heap_strdupW(lpszFileName);
1531 req->dwAccess = fdwAccess;
1532 req->dwFlags = dwFlags;
1533 req->dwContext = dwContext;
1535 INTERNET_AsyncCall(&workRequest);
1536 r = NULL;
1538 else
1540 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1543 lend:
1544 WININET_Release( &lpwfs->hdr );
1546 return r;
1550 /***********************************************************************
1551 * FtpGetFileA (WININET.@)
1553 * Retrieve file from the FTP server
1555 * RETURNS
1556 * TRUE on success
1557 * FALSE on failure
1560 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1561 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1562 DWORD_PTR dwContext)
1564 LPWSTR lpwzRemoteFile;
1565 LPWSTR lpwzNewFile;
1566 BOOL ret;
1568 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1569 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1570 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1571 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1572 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1573 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1574 return ret;
1578 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1580 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1581 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1583 TRACE("%p\n", lpwfs);
1585 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1586 req->lpszNewFile, req->fFailIfExists,
1587 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1588 HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1589 HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1593 /***********************************************************************
1594 * FtpGetFileW (WININET.@)
1596 * Retrieve file from the FTP server
1598 * RETURNS
1599 * TRUE on success
1600 * FALSE on failure
1603 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1604 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1605 DWORD_PTR dwContext)
1607 ftp_session_t *lpwfs;
1608 appinfo_t *hIC = NULL;
1609 BOOL r = FALSE;
1611 if (!lpszRemoteFile || !lpszNewFile)
1613 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1614 return FALSE;
1617 lpwfs = (ftp_session_t*) WININET_GetObject( hInternet );
1618 if (!lpwfs)
1620 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1621 return FALSE;
1624 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1626 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1627 goto lend;
1630 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1632 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1633 goto lend;
1636 if (lpwfs->download_in_progress != NULL)
1638 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1639 goto lend;
1642 hIC = lpwfs->lpAppInfo;
1643 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1645 WORKREQUEST workRequest;
1646 struct WORKREQ_FTPGETFILEW *req;
1648 workRequest.asyncproc = AsyncFtpGetFileProc;
1649 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1650 req = &workRequest.u.FtpGetFileW;
1651 req->lpszRemoteFile = heap_strdupW(lpszRemoteFile);
1652 req->lpszNewFile = heap_strdupW(lpszNewFile);
1653 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1654 req->fFailIfExists = fFailIfExists;
1655 req->dwFlags = dwInternetFlags;
1656 req->dwContext = dwContext;
1658 r = INTERNET_AsyncCall(&workRequest);
1660 else
1662 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1663 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1666 lend:
1667 WININET_Release( &lpwfs->hdr );
1669 return r;
1673 /***********************************************************************
1674 * FTP_FtpGetFileW (Internal)
1676 * Retrieve file from the FTP server
1678 * RETURNS
1679 * TRUE on success
1680 * FALSE on failure
1683 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1684 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1685 DWORD_PTR dwContext)
1687 BOOL bSuccess = FALSE;
1688 HANDLE hFile;
1689 appinfo_t *hIC = NULL;
1691 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1693 /* Clear any error information */
1694 INTERNET_SetLastError(0);
1696 /* Ensure we can write to lpszNewfile by opening it */
1697 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1698 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1699 if (INVALID_HANDLE_VALUE == hFile)
1700 return FALSE;
1702 /* Set up socket to retrieve data */
1703 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1705 INT nDataSocket;
1707 /* Get data socket to server */
1708 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1710 INT nResCode;
1712 /* Receive data */
1713 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1714 closesocket(nDataSocket);
1716 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1717 if (nResCode)
1719 if (nResCode == 226)
1720 bSuccess = TRUE;
1721 else
1722 FTP_SetResponseError(nResCode);
1727 if (lpwfs->lstnSocket != -1)
1729 closesocket(lpwfs->lstnSocket);
1730 lpwfs->lstnSocket = -1;
1733 CloseHandle(hFile);
1735 hIC = lpwfs->lpAppInfo;
1736 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1738 INTERNET_ASYNC_RESULT iar;
1740 iar.dwResult = (DWORD)bSuccess;
1741 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1742 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1743 &iar, sizeof(INTERNET_ASYNC_RESULT));
1746 if (!bSuccess) DeleteFileW(lpszNewFile);
1747 return bSuccess;
1750 /***********************************************************************
1751 * FtpGetFileSize (WININET.@)
1753 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1755 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1757 if (lpdwFileSizeHigh)
1758 *lpdwFileSizeHigh = 0;
1760 return 0;
1763 /***********************************************************************
1764 * FtpDeleteFileA (WININET.@)
1766 * Delete a file on the ftp server
1768 * RETURNS
1769 * TRUE on success
1770 * FALSE on failure
1773 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1775 LPWSTR lpwzFileName;
1776 BOOL ret;
1778 lpwzFileName = heap_strdupAtoW(lpszFileName);
1779 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1780 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1781 return ret;
1784 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1786 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1787 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1789 TRACE("%p\n", lpwfs);
1791 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1792 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1795 /***********************************************************************
1796 * FtpDeleteFileW (WININET.@)
1798 * Delete a file on the ftp server
1800 * RETURNS
1801 * TRUE on success
1802 * FALSE on failure
1805 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1807 ftp_session_t *lpwfs;
1808 appinfo_t *hIC = NULL;
1809 BOOL r = FALSE;
1811 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1812 if (!lpwfs)
1814 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1815 return FALSE;
1818 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1820 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1821 goto lend;
1824 if (lpwfs->download_in_progress != NULL)
1826 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1827 goto lend;
1830 if (!lpszFileName)
1832 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1833 goto lend;
1836 hIC = lpwfs->lpAppInfo;
1837 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1839 WORKREQUEST workRequest;
1840 struct WORKREQ_FTPDELETEFILEW *req;
1842 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1843 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1844 req = &workRequest.u.FtpDeleteFileW;
1845 req->lpszFilename = heap_strdupW(lpszFileName);
1847 r = INTERNET_AsyncCall(&workRequest);
1849 else
1851 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1854 lend:
1855 WININET_Release( &lpwfs->hdr );
1857 return r;
1860 /***********************************************************************
1861 * FTP_FtpDeleteFileW (Internal)
1863 * Delete a file on the ftp server
1865 * RETURNS
1866 * TRUE on success
1867 * FALSE on failure
1870 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1872 INT nResCode;
1873 BOOL bSuccess = FALSE;
1874 appinfo_t *hIC = NULL;
1876 TRACE("%p\n", lpwfs);
1878 /* Clear any error information */
1879 INTERNET_SetLastError(0);
1881 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1882 goto lend;
1884 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1885 if (nResCode)
1887 if (nResCode == 250)
1888 bSuccess = TRUE;
1889 else
1890 FTP_SetResponseError(nResCode);
1892 lend:
1893 hIC = lpwfs->lpAppInfo;
1894 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1896 INTERNET_ASYNC_RESULT iar;
1898 iar.dwResult = (DWORD)bSuccess;
1899 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1900 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1901 &iar, sizeof(INTERNET_ASYNC_RESULT));
1904 return bSuccess;
1908 /***********************************************************************
1909 * FtpRemoveDirectoryA (WININET.@)
1911 * Remove a directory on the ftp server
1913 * RETURNS
1914 * TRUE on success
1915 * FALSE on failure
1918 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1920 LPWSTR lpwzDirectory;
1921 BOOL ret;
1923 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1924 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1925 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1926 return ret;
1929 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1931 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1932 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1934 TRACE("%p\n", lpwfs);
1936 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1937 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1940 /***********************************************************************
1941 * FtpRemoveDirectoryW (WININET.@)
1943 * Remove a directory on the ftp server
1945 * RETURNS
1946 * TRUE on success
1947 * FALSE on failure
1950 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1952 ftp_session_t *lpwfs;
1953 appinfo_t *hIC = NULL;
1954 BOOL r = FALSE;
1956 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1957 if (!lpwfs)
1959 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1960 return FALSE;
1963 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1965 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1966 goto lend;
1969 if (lpwfs->download_in_progress != NULL)
1971 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1972 goto lend;
1975 if (!lpszDirectory)
1977 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1978 goto lend;
1981 hIC = lpwfs->lpAppInfo;
1982 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1984 WORKREQUEST workRequest;
1985 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1987 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1988 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1989 req = &workRequest.u.FtpRemoveDirectoryW;
1990 req->lpszDirectory = heap_strdupW(lpszDirectory);
1992 r = INTERNET_AsyncCall(&workRequest);
1994 else
1996 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1999 lend:
2000 WININET_Release( &lpwfs->hdr );
2002 return r;
2005 /***********************************************************************
2006 * FTP_FtpRemoveDirectoryW (Internal)
2008 * Remove a directory on the ftp server
2010 * RETURNS
2011 * TRUE on success
2012 * FALSE on failure
2015 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
2017 INT nResCode;
2018 BOOL bSuccess = FALSE;
2019 appinfo_t *hIC = NULL;
2021 TRACE("\n");
2023 /* Clear any error information */
2024 INTERNET_SetLastError(0);
2026 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2027 goto lend;
2029 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2030 if (nResCode)
2032 if (nResCode == 250)
2033 bSuccess = TRUE;
2034 else
2035 FTP_SetResponseError(nResCode);
2038 lend:
2039 hIC = lpwfs->lpAppInfo;
2040 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2042 INTERNET_ASYNC_RESULT iar;
2044 iar.dwResult = (DWORD)bSuccess;
2045 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2046 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2047 &iar, sizeof(INTERNET_ASYNC_RESULT));
2050 return bSuccess;
2054 /***********************************************************************
2055 * FtpRenameFileA (WININET.@)
2057 * Rename a file on the ftp server
2059 * RETURNS
2060 * TRUE on success
2061 * FALSE on failure
2064 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2066 LPWSTR lpwzSrc;
2067 LPWSTR lpwzDest;
2068 BOOL ret;
2070 lpwzSrc = heap_strdupAtoW(lpszSrc);
2071 lpwzDest = heap_strdupAtoW(lpszDest);
2072 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2073 HeapFree(GetProcessHeap(), 0, lpwzSrc);
2074 HeapFree(GetProcessHeap(), 0, lpwzDest);
2075 return ret;
2078 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
2080 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
2081 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
2083 TRACE("%p\n", lpwfs);
2085 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
2086 HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
2087 HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
2090 /***********************************************************************
2091 * FtpRenameFileW (WININET.@)
2093 * Rename a file on the ftp server
2095 * RETURNS
2096 * TRUE on success
2097 * FALSE on failure
2100 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2102 ftp_session_t *lpwfs;
2103 appinfo_t *hIC = NULL;
2104 BOOL r = FALSE;
2106 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
2107 if (!lpwfs)
2109 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2110 return FALSE;
2113 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2115 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2116 goto lend;
2119 if (lpwfs->download_in_progress != NULL)
2121 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2122 goto lend;
2125 if (!lpszSrc || !lpszDest)
2127 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2128 goto lend;
2131 hIC = lpwfs->lpAppInfo;
2132 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2134 WORKREQUEST workRequest;
2135 struct WORKREQ_FTPRENAMEFILEW *req;
2137 workRequest.asyncproc = AsyncFtpRenameFileProc;
2138 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
2139 req = &workRequest.u.FtpRenameFileW;
2140 req->lpszSrcFile = heap_strdupW(lpszSrc);
2141 req->lpszDestFile = heap_strdupW(lpszDest);
2143 r = INTERNET_AsyncCall(&workRequest);
2145 else
2147 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2150 lend:
2151 WININET_Release( &lpwfs->hdr );
2153 return r;
2156 /***********************************************************************
2157 * FTP_FtpRenameFileW (Internal)
2159 * Rename a file on the ftp server
2161 * RETURNS
2162 * TRUE on success
2163 * FALSE on failure
2166 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2168 INT nResCode;
2169 BOOL bSuccess = FALSE;
2170 appinfo_t *hIC = NULL;
2172 TRACE("\n");
2174 /* Clear any error information */
2175 INTERNET_SetLastError(0);
2177 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2178 goto lend;
2180 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2181 if (nResCode == 350)
2183 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2184 goto lend;
2186 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2189 if (nResCode == 250)
2190 bSuccess = TRUE;
2191 else
2192 FTP_SetResponseError(nResCode);
2194 lend:
2195 hIC = lpwfs->lpAppInfo;
2196 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2198 INTERNET_ASYNC_RESULT iar;
2200 iar.dwResult = (DWORD)bSuccess;
2201 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2202 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2203 &iar, sizeof(INTERNET_ASYNC_RESULT));
2206 return bSuccess;
2209 /***********************************************************************
2210 * FtpCommandA (WININET.@)
2212 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2213 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2215 BOOL r;
2216 WCHAR *cmdW;
2218 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2219 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2221 if (fExpectResponse)
2223 FIXME("data connection not supported\n");
2224 return FALSE;
2227 if (!lpszCommand || !lpszCommand[0])
2229 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2230 return FALSE;
2233 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2235 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2236 return FALSE;
2239 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2241 HeapFree(GetProcessHeap(), 0, cmdW);
2242 return r;
2245 /***********************************************************************
2246 * FtpCommandW (WININET.@)
2248 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2249 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2251 BOOL r = FALSE;
2252 ftp_session_t *lpwfs;
2253 LPSTR cmd = NULL;
2254 DWORD len, nBytesSent= 0;
2255 INT nResCode, nRC = 0;
2257 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2258 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2260 if (!lpszCommand || !lpszCommand[0])
2262 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2263 return FALSE;
2266 if (fExpectResponse)
2268 FIXME("data connection not supported\n");
2269 return FALSE;
2272 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
2273 if (!lpwfs)
2275 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2276 return FALSE;
2279 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2281 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2282 goto lend;
2285 if (lpwfs->download_in_progress != NULL)
2287 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2288 goto lend;
2291 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2292 if ((cmd = HeapAlloc(GetProcessHeap(), 0, len )))
2293 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2294 else
2296 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2297 goto lend;
2300 strcat(cmd, szCRLF);
2301 len--;
2303 TRACE("Sending (%s) len(%d)\n", cmd, len);
2304 while ((nBytesSent < len) && (nRC != -1))
2306 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2307 if (nRC != -1)
2309 nBytesSent += nRC;
2310 TRACE("Sent %d bytes\n", nRC);
2314 if (nBytesSent)
2316 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2317 if (nResCode > 0 && nResCode < 400)
2318 r = TRUE;
2319 else
2320 FTP_SetResponseError(nResCode);
2323 lend:
2324 WININET_Release( &lpwfs->hdr );
2325 HeapFree(GetProcessHeap(), 0, cmd);
2326 return r;
2330 /***********************************************************************
2331 * FTPSESSION_Destroy (internal)
2333 * Deallocate session handle
2335 static void FTPSESSION_Destroy(object_header_t *hdr)
2337 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2339 TRACE("\n");
2341 WININET_Release(&lpwfs->lpAppInfo->hdr);
2343 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2344 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2345 HeapFree(GetProcessHeap(), 0, lpwfs->servername);
2346 HeapFree(GetProcessHeap(), 0, lpwfs);
2349 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2351 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2353 TRACE("\n");
2355 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2356 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2358 if (lpwfs->download_in_progress != NULL)
2359 lpwfs->download_in_progress->session_deleted = TRUE;
2361 if (lpwfs->sndSocket != -1)
2362 closesocket(lpwfs->sndSocket);
2364 if (lpwfs->lstnSocket != -1)
2365 closesocket(lpwfs->lstnSocket);
2367 if (lpwfs->pasvSocket != -1)
2368 closesocket(lpwfs->pasvSocket);
2370 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2371 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2374 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2376 switch(option) {
2377 case INTERNET_OPTION_HANDLE_TYPE:
2378 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2380 if (*size < sizeof(ULONG))
2381 return ERROR_INSUFFICIENT_BUFFER;
2383 *size = sizeof(DWORD);
2384 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2385 return ERROR_SUCCESS;
2388 return INET_QueryOption(option, buffer, size, unicode);
2391 static const object_vtbl_t FTPSESSIONVtbl = {
2392 FTPSESSION_Destroy,
2393 FTPSESSION_CloseConnection,
2394 FTPSESSION_QueryOption,
2395 NULL,
2396 NULL,
2397 NULL,
2398 NULL,
2399 NULL,
2400 NULL
2404 /***********************************************************************
2405 * FTP_Connect (internal)
2407 * Connect to a ftp server
2409 * RETURNS
2410 * HINTERNET a session handle on success
2411 * NULL on failure
2413 * NOTES:
2415 * Windows uses 'anonymous' as the username, when given a NULL username
2416 * and a NULL password. The password is first looked up in:
2418 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2420 * If this entry is not present it uses the current username as the password.
2424 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2425 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2426 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2427 DWORD dwInternalFlags)
2429 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2430 'M','i','c','r','o','s','o','f','t','\\',
2431 'W','i','n','d','o','w','s','\\',
2432 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2433 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2434 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2435 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2436 static const WCHAR szEmpty[] = {'\0'};
2437 struct sockaddr_in socketAddr;
2438 INT nsocket = -1;
2439 UINT sock_namelen;
2440 BOOL bSuccess = FALSE;
2441 ftp_session_t *lpwfs = NULL;
2442 HINTERNET handle = NULL;
2444 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2445 hIC, debugstr_w(lpszServerName),
2446 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2448 assert( hIC->hdr.htype == WH_HINIT );
2450 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2452 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2453 goto lerror;
2456 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(ftp_session_t));
2457 if (NULL == lpwfs)
2459 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2460 goto lerror;
2463 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2464 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2465 else
2466 lpwfs->serverport = nServerPort;
2468 lpwfs->hdr.htype = WH_HFTPSESSION;
2469 lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
2470 lpwfs->hdr.dwFlags = dwFlags;
2471 lpwfs->hdr.dwContext = dwContext;
2472 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2473 lpwfs->hdr.refs = 1;
2474 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2475 lpwfs->download_in_progress = NULL;
2476 lpwfs->sndSocket = -1;
2477 lpwfs->lstnSocket = -1;
2478 lpwfs->pasvSocket = -1;
2480 WININET_AddRef( &hIC->hdr );
2481 lpwfs->lpAppInfo = hIC;
2482 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2484 handle = WININET_AllocHandle( &lpwfs->hdr );
2485 if( !handle )
2487 ERR("Failed to alloc handle\n");
2488 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2489 goto lerror;
2492 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2493 if(strchrW(hIC->lpszProxy, ' '))
2494 FIXME("Several proxies not implemented.\n");
2495 if(hIC->lpszProxyBypass)
2496 FIXME("Proxy bypass is ignored.\n");
2498 if (!lpszUserName || !strlenW(lpszUserName)) {
2499 HKEY key;
2500 WCHAR szPassword[MAX_PATH];
2501 DWORD len = sizeof(szPassword);
2503 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2505 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2506 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2507 /* Nothing in the registry, get the username and use that as the password */
2508 if (!GetUserNameW(szPassword, &len)) {
2509 /* Should never get here, but use an empty password as failsafe */
2510 strcpyW(szPassword, szEmpty);
2513 RegCloseKey(key);
2515 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2516 lpwfs->lpszPassword = heap_strdupW(szPassword);
2518 else {
2519 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2520 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2522 lpwfs->servername = heap_strdupW(lpszServerName);
2524 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2525 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2527 INTERNET_ASYNC_RESULT iar;
2529 iar.dwResult = (DWORD_PTR)handle;
2530 iar.dwError = ERROR_SUCCESS;
2532 SendAsyncCallback(&hIC->hdr, dwContext,
2533 INTERNET_STATUS_HANDLE_CREATED, &iar,
2534 sizeof(INTERNET_ASYNC_RESULT));
2537 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2538 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2540 sock_namelen = sizeof(socketAddr);
2541 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen))
2543 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2544 goto lerror;
2547 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2548 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2550 if (socketAddr.sin_family != AF_INET)
2552 WARN("unsupported address family %d\n", socketAddr.sin_family);
2553 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2554 goto lerror;
2556 nsocket = socket(AF_INET,SOCK_STREAM,0);
2557 if (nsocket == -1)
2559 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2560 goto lerror;
2563 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2564 &socketAddr, sock_namelen);
2566 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2568 ERR("Unable to connect (%s)\n", strerror(errno));
2569 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2570 closesocket(nsocket);
2572 else
2574 TRACE("Connected to server\n");
2575 lpwfs->sndSocket = nsocket;
2576 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2577 &socketAddr, sock_namelen);
2579 sock_namelen = sizeof(lpwfs->socketAddress);
2580 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2582 if (FTP_ConnectToHost(lpwfs))
2584 TRACE("Successfully logged into server\n");
2585 bSuccess = TRUE;
2589 lerror:
2590 if (lpwfs) WININET_Release( &lpwfs->hdr );
2592 if (!bSuccess && handle)
2594 WININET_FreeHandle( handle );
2595 handle = NULL;
2598 return handle;
2602 /***********************************************************************
2603 * FTP_ConnectToHost (internal)
2605 * Connect to a ftp server
2607 * RETURNS
2608 * TRUE on success
2609 * NULL on failure
2612 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2614 INT nResCode;
2615 BOOL bSuccess = FALSE;
2617 TRACE("\n");
2618 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2620 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2621 goto lend;
2623 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2624 if (nResCode)
2626 /* Login successful... */
2627 if (nResCode == 230)
2628 bSuccess = TRUE;
2629 /* User name okay, need password... */
2630 else if (nResCode == 331)
2631 bSuccess = FTP_SendPassword(lpwfs);
2632 /* Need account for login... */
2633 else if (nResCode == 332)
2634 bSuccess = FTP_SendAccount(lpwfs);
2635 else
2636 FTP_SetResponseError(nResCode);
2639 TRACE("Returning %d\n", bSuccess);
2640 lend:
2641 return bSuccess;
2645 /***********************************************************************
2646 * FTP_SendCommandA (internal)
2648 * Send command to server
2650 * RETURNS
2651 * TRUE on success
2652 * NULL on failure
2655 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2656 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2658 DWORD len;
2659 CHAR *buf;
2660 DWORD nBytesSent = 0;
2661 int nRC = 0;
2662 DWORD dwParamLen;
2664 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2666 if (lpfnStatusCB)
2668 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2671 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2672 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2673 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2675 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2676 return FALSE;
2678 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2679 dwParamLen ? lpszParam : "", szCRLF);
2681 TRACE("Sending (%s) len(%d)\n", buf, len);
2682 while((nBytesSent < len) && (nRC != -1))
2684 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2685 nBytesSent += nRC;
2688 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2690 if (lpfnStatusCB)
2692 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2693 &nBytesSent, sizeof(DWORD));
2696 TRACE("Sent %d bytes\n", nBytesSent);
2697 return (nRC != -1);
2700 /***********************************************************************
2701 * FTP_SendCommand (internal)
2703 * Send command to server
2705 * RETURNS
2706 * TRUE on success
2707 * NULL on failure
2710 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2711 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2713 BOOL ret;
2714 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2715 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2716 HeapFree(GetProcessHeap(), 0, lpszParamA);
2717 return ret;
2720 /***********************************************************************
2721 * FTP_ReceiveResponse (internal)
2723 * Receive response from server
2725 * RETURNS
2726 * Reply code on success
2727 * 0 on failure
2730 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2732 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2733 DWORD nRecv;
2734 INT rc = 0;
2735 char firstprefix[5];
2736 BOOL multiline = FALSE;
2738 TRACE("socket(%d)\n", lpwfs->sndSocket);
2740 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2742 while(1)
2744 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2745 goto lerror;
2747 if (nRecv >= 3)
2749 if(!multiline)
2751 if(lpszResponse[3] != '-')
2752 break;
2753 else
2754 { /* Start of multiline response. Loop until we get "nnn " */
2755 multiline = TRUE;
2756 memcpy(firstprefix, lpszResponse, 3);
2757 firstprefix[3] = ' ';
2758 firstprefix[4] = '\0';
2761 else
2763 if(!memcmp(firstprefix, lpszResponse, 4))
2764 break;
2769 if (nRecv >= 3)
2771 rc = atoi(lpszResponse);
2773 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2774 &nRecv, sizeof(DWORD));
2777 lerror:
2778 TRACE("return %d\n", rc);
2779 return rc;
2783 /***********************************************************************
2784 * FTP_SendPassword (internal)
2786 * Send password to ftp server
2788 * RETURNS
2789 * TRUE on success
2790 * NULL on failure
2793 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2795 INT nResCode;
2796 BOOL bSuccess = FALSE;
2798 TRACE("\n");
2799 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2800 goto lend;
2802 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2803 if (nResCode)
2805 TRACE("Received reply code %d\n", nResCode);
2806 /* Login successful... */
2807 if (nResCode == 230)
2808 bSuccess = TRUE;
2809 /* Command not implemented, superfluous at the server site... */
2810 /* Need account for login... */
2811 else if (nResCode == 332)
2812 bSuccess = FTP_SendAccount(lpwfs);
2813 else
2814 FTP_SetResponseError(nResCode);
2817 lend:
2818 TRACE("Returning %d\n", bSuccess);
2819 return bSuccess;
2823 /***********************************************************************
2824 * FTP_SendAccount (internal)
2828 * RETURNS
2829 * TRUE on success
2830 * FALSE on failure
2833 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2835 INT nResCode;
2836 BOOL bSuccess = FALSE;
2838 TRACE("\n");
2839 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2840 goto lend;
2842 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2843 if (nResCode)
2844 bSuccess = TRUE;
2845 else
2846 FTP_SetResponseError(nResCode);
2848 lend:
2849 return bSuccess;
2853 /***********************************************************************
2854 * FTP_SendStore (internal)
2856 * Send request to upload file to ftp server
2858 * RETURNS
2859 * TRUE on success
2860 * FALSE on failure
2863 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2865 INT nResCode;
2866 BOOL bSuccess = FALSE;
2868 TRACE("\n");
2869 if (!FTP_InitListenSocket(lpwfs))
2870 goto lend;
2872 if (!FTP_SendType(lpwfs, dwType))
2873 goto lend;
2875 if (!FTP_SendPortOrPasv(lpwfs))
2876 goto lend;
2878 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2879 goto lend;
2880 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2881 if (nResCode)
2883 if (nResCode == 150 || nResCode == 125)
2884 bSuccess = TRUE;
2885 else
2886 FTP_SetResponseError(nResCode);
2889 lend:
2890 if (!bSuccess && lpwfs->lstnSocket != -1)
2892 closesocket(lpwfs->lstnSocket);
2893 lpwfs->lstnSocket = -1;
2896 return bSuccess;
2900 /***********************************************************************
2901 * FTP_InitListenSocket (internal)
2903 * Create a socket to listen for server response
2905 * RETURNS
2906 * TRUE on success
2907 * FALSE on failure
2910 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2912 BOOL bSuccess = FALSE;
2913 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2915 TRACE("\n");
2917 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2918 if (lpwfs->lstnSocket == -1)
2920 TRACE("Unable to create listening socket\n");
2921 goto lend;
2924 /* We obtain our ip addr from the name of the command channel socket */
2925 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2927 /* and get the system to assign us a port */
2928 lpwfs->lstnSocketAddress.sin_port = htons(0);
2930 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2932 TRACE("Unable to bind socket\n");
2933 goto lend;
2936 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2938 TRACE("listen failed\n");
2939 goto lend;
2942 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2943 bSuccess = TRUE;
2945 lend:
2946 if (!bSuccess && lpwfs->lstnSocket != -1)
2948 closesocket(lpwfs->lstnSocket);
2949 lpwfs->lstnSocket = -1;
2952 return bSuccess;
2956 /***********************************************************************
2957 * FTP_SendType (internal)
2959 * Tell server type of data being transferred
2961 * RETURNS
2962 * TRUE on success
2963 * FALSE on failure
2965 * W98SE doesn't cache the type that's currently set
2966 * (i.e. it sends it always),
2967 * so we probably don't want to do that either.
2969 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2971 INT nResCode;
2972 WCHAR type[] = { 'I','\0' };
2973 BOOL bSuccess = FALSE;
2975 TRACE("\n");
2976 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2977 type[0] = 'A';
2979 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2980 goto lend;
2982 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2983 if (nResCode)
2985 if (nResCode == 2)
2986 bSuccess = TRUE;
2987 else
2988 FTP_SetResponseError(nResCode);
2991 lend:
2992 return bSuccess;
2996 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2997 /***********************************************************************
2998 * FTP_GetFileSize (internal)
3000 * Retrieves from the server the size of the given file
3002 * RETURNS
3003 * TRUE on success
3004 * FALSE on failure
3007 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
3009 INT nResCode;
3010 BOOL bSuccess = FALSE;
3012 TRACE("\n");
3014 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
3015 goto lend;
3017 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3018 if (nResCode)
3020 if (nResCode == 213) {
3021 /* Now parses the output to get the actual file size */
3022 int i;
3023 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3025 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3026 if (lpszResponseBuffer[i] == '\0') return FALSE;
3027 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3029 bSuccess = TRUE;
3030 } else {
3031 FTP_SetResponseError(nResCode);
3035 lend:
3036 return bSuccess;
3038 #endif
3041 /***********************************************************************
3042 * FTP_SendPort (internal)
3044 * Tell server which port to use
3046 * RETURNS
3047 * TRUE on success
3048 * FALSE on failure
3051 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3053 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3054 INT nResCode;
3055 WCHAR szIPAddress[64];
3056 BOOL bSuccess = FALSE;
3057 TRACE("\n");
3059 sprintfW(szIPAddress, szIPFormat,
3060 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
3061 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
3062 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
3063 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
3064 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3065 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3067 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3068 goto lend;
3070 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3071 if (nResCode)
3073 if (nResCode == 200)
3074 bSuccess = TRUE;
3075 else
3076 FTP_SetResponseError(nResCode);
3079 lend:
3080 return bSuccess;
3084 /***********************************************************************
3085 * FTP_DoPassive (internal)
3087 * Tell server that we want to do passive transfers
3088 * and connect data socket
3090 * RETURNS
3091 * TRUE on success
3092 * FALSE on failure
3095 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3097 INT nResCode;
3098 BOOL bSuccess = FALSE;
3100 TRACE("\n");
3101 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3102 goto lend;
3104 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3105 if (nResCode)
3107 if (nResCode == 227)
3109 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3110 LPSTR p;
3111 int f[6];
3112 int i;
3113 char *pAddr, *pPort;
3114 INT nsocket = -1;
3115 struct sockaddr_in dataSocketAddress;
3117 p = lpszResponseBuffer+4; /* skip status code */
3118 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3120 if (*p == '\0')
3122 ERR("no address found in response, aborting\n");
3123 goto lend;
3126 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3127 &f[4], &f[5]) != 6)
3129 ERR("unknown response address format '%s', aborting\n", p);
3130 goto lend;
3132 for (i=0; i < 6; i++)
3133 f[i] = f[i] & 0xff;
3135 dataSocketAddress = lpwfs->socketAddress;
3136 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3137 pPort = (char *)&(dataSocketAddress.sin_port);
3138 pAddr[0] = f[0];
3139 pAddr[1] = f[1];
3140 pAddr[2] = f[2];
3141 pAddr[3] = f[3];
3142 pPort[0] = f[4];
3143 pPort[1] = f[5];
3145 nsocket = socket(AF_INET,SOCK_STREAM,0);
3146 if (nsocket == -1)
3147 goto lend;
3149 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3151 ERR("can't connect passive FTP data port.\n");
3152 closesocket(nsocket);
3153 goto lend;
3155 lpwfs->pasvSocket = nsocket;
3156 bSuccess = TRUE;
3158 else
3159 FTP_SetResponseError(nResCode);
3162 lend:
3163 return bSuccess;
3167 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3169 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3171 if (!FTP_DoPassive(lpwfs))
3172 return FALSE;
3174 else
3176 if (!FTP_SendPort(lpwfs))
3177 return FALSE;
3179 return TRUE;
3183 /***********************************************************************
3184 * FTP_GetDataSocket (internal)
3186 * Either accepts an incoming data socket connection from the server
3187 * or just returns the already opened socket after a PASV command
3188 * in case of passive FTP.
3191 * RETURNS
3192 * TRUE on success
3193 * FALSE on failure
3196 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3198 struct sockaddr_in saddr;
3199 socklen_t addrlen = sizeof(struct sockaddr);
3201 TRACE("\n");
3202 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3204 *nDataSocket = lpwfs->pasvSocket;
3205 lpwfs->pasvSocket = -1;
3207 else
3209 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3210 closesocket(lpwfs->lstnSocket);
3211 lpwfs->lstnSocket = -1;
3213 return *nDataSocket != -1;
3217 /***********************************************************************
3218 * FTP_SendData (internal)
3220 * Send data to the server
3222 * RETURNS
3223 * TRUE on success
3224 * FALSE on failure
3227 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3229 BY_HANDLE_FILE_INFORMATION fi;
3230 DWORD nBytesRead = 0;
3231 DWORD nBytesSent = 0;
3232 DWORD nTotalSent = 0;
3233 DWORD nBytesToSend, nLen;
3234 int nRC = 1;
3235 time_t s_long_time, e_long_time;
3236 LONG nSeconds;
3237 CHAR *lpszBuffer;
3239 TRACE("\n");
3240 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3242 /* Get the size of the file. */
3243 GetFileInformationByHandle(hFile, &fi);
3244 time(&s_long_time);
3248 nBytesToSend = nBytesRead - nBytesSent;
3250 if (nBytesToSend <= 0)
3252 /* Read data from file. */
3253 nBytesSent = 0;
3254 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3255 ERR("Failed reading from file\n");
3257 if (nBytesRead > 0)
3258 nBytesToSend = nBytesRead;
3259 else
3260 break;
3263 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3264 DATA_PACKET_SIZE : nBytesToSend;
3265 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3267 if (nRC != -1)
3269 nBytesSent += nRC;
3270 nTotalSent += nRC;
3273 /* Do some computation to display the status. */
3274 time(&e_long_time);
3275 nSeconds = e_long_time - s_long_time;
3276 if( nSeconds / 60 > 0 )
3278 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3279 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3280 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3282 else
3284 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3285 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3286 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3288 } while (nRC != -1);
3290 TRACE("file transfer complete!\n");
3292 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3294 return nTotalSent;
3298 /***********************************************************************
3299 * FTP_SendRetrieve (internal)
3301 * Send request to retrieve a file
3303 * RETURNS
3304 * Number of bytes to be received on success
3305 * 0 on failure
3308 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3310 INT nResCode;
3311 BOOL ret;
3313 TRACE("\n");
3314 if (!(ret = FTP_InitListenSocket(lpwfs)))
3315 goto lend;
3317 if (!(ret = FTP_SendType(lpwfs, dwType)))
3318 goto lend;
3320 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3321 goto lend;
3323 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3324 goto lend;
3326 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3327 if ((nResCode != 125) && (nResCode != 150)) {
3328 /* That means that we got an error getting the file. */
3329 FTP_SetResponseError(nResCode);
3330 ret = FALSE;
3333 lend:
3334 if (!ret && lpwfs->lstnSocket != -1)
3336 closesocket(lpwfs->lstnSocket);
3337 lpwfs->lstnSocket = -1;
3340 return ret;
3344 /***********************************************************************
3345 * FTP_RetrieveData (internal)
3347 * Retrieve data from server
3349 * RETURNS
3350 * TRUE on success
3351 * FALSE on failure
3354 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3356 DWORD nBytesWritten;
3357 INT nRC = 0;
3358 CHAR *lpszBuffer;
3360 TRACE("\n");
3362 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3363 if (NULL == lpszBuffer)
3365 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3366 return FALSE;
3369 while (nRC != -1)
3371 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3372 if (nRC != -1)
3374 /* other side closed socket. */
3375 if (nRC == 0)
3376 goto recv_end;
3377 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3381 TRACE("Data transfer complete\n");
3383 recv_end:
3384 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3386 return (nRC != -1);
3389 /***********************************************************************
3390 * FTPFINDNEXT_Destroy (internal)
3392 * Deallocate session handle
3394 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3396 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3397 DWORD i;
3399 TRACE("\n");
3401 WININET_Release(&lpwfn->lpFtpSession->hdr);
3403 for (i = 0; i < lpwfn->size; i++)
3405 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3408 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3409 HeapFree(GetProcessHeap(), 0, lpwfn);
3412 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3414 WIN32_FIND_DATAW *find_data = data;
3415 DWORD res = ERROR_SUCCESS;
3417 TRACE("index(%d) size(%d)\n", find->index, find->size);
3419 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3421 if (find->index < find->size) {
3422 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3423 find->index++;
3425 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3426 }else {
3427 res = ERROR_NO_MORE_FILES;
3430 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3432 INTERNET_ASYNC_RESULT iar;
3434 iar.dwResult = (res == ERROR_SUCCESS);
3435 iar.dwError = res;
3437 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3438 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3439 sizeof(INTERNET_ASYNC_RESULT));
3442 return res;
3445 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3447 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3449 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3452 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3454 switch(option) {
3455 case INTERNET_OPTION_HANDLE_TYPE:
3456 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3458 if (*size < sizeof(ULONG))
3459 return ERROR_INSUFFICIENT_BUFFER;
3461 *size = sizeof(DWORD);
3462 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3463 return ERROR_SUCCESS;
3466 return INET_QueryOption(option, buffer, size, unicode);
3469 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3471 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3473 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3475 WORKREQUEST workRequest;
3476 struct WORKREQ_FTPFINDNEXTW *req;
3478 workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3479 workRequest.hdr = WININET_AddRef( &find->hdr );
3480 req = &workRequest.u.FtpFindNextW;
3481 req->lpFindFileData = data;
3483 INTERNET_AsyncCall(&workRequest);
3485 return ERROR_SUCCESS;
3488 return FTPFINDNEXT_FindNextFileProc(find, data);
3491 static const object_vtbl_t FTPFINDNEXTVtbl = {
3492 FTPFINDNEXT_Destroy,
3493 NULL,
3494 FTPFINDNEXT_QueryOption,
3495 NULL,
3496 NULL,
3497 NULL,
3498 NULL,
3499 NULL,
3500 NULL,
3501 FTPFINDNEXT_FindNextFileW
3504 /***********************************************************************
3505 * FTP_ReceiveFileList (internal)
3507 * Read file list from server
3509 * RETURNS
3510 * Handle to file list on success
3511 * NULL on failure
3514 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3515 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3517 DWORD dwSize = 0;
3518 LPFILEPROPERTIESW lpafp = NULL;
3519 LPWININETFTPFINDNEXTW lpwfn = NULL;
3520 HINTERNET handle = 0;
3522 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3524 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3526 if(lpFindFileData)
3527 FTP_ConvertFileProp(lpafp, lpFindFileData);
3529 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3530 if (lpwfn)
3532 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3533 lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3534 lpwfn->hdr.dwContext = dwContext;
3535 lpwfn->hdr.refs = 1;
3536 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3537 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3538 lpwfn->size = dwSize;
3539 lpwfn->lpafp = lpafp;
3541 WININET_AddRef( &lpwfs->hdr );
3542 lpwfn->lpFtpSession = lpwfs;
3543 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3545 handle = WININET_AllocHandle( &lpwfn->hdr );
3549 if( lpwfn )
3550 WININET_Release( &lpwfn->hdr );
3552 TRACE("Matched %d files\n", dwSize);
3553 return handle;
3557 /***********************************************************************
3558 * FTP_ConvertFileProp (internal)
3560 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3562 * RETURNS
3563 * TRUE on success
3564 * FALSE on failure
3567 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3569 BOOL bSuccess = FALSE;
3571 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3573 if (lpafp)
3575 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3576 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3577 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3579 /* Not all fields are filled in */
3580 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3581 lpFindFileData->nFileSizeLow = lpafp->nSize;
3583 if (lpafp->bIsDirectory)
3584 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3586 if (lpafp->lpszName)
3587 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3589 bSuccess = TRUE;
3592 return bSuccess;
3595 /***********************************************************************
3596 * FTP_ParseNextFile (internal)
3598 * Parse the next line in file listing
3600 * RETURNS
3601 * TRUE on success
3602 * FALSE on failure
3604 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3606 static const char szSpace[] = " \t";
3607 DWORD nBufLen;
3608 char *pszLine;
3609 char *pszToken;
3610 char *pszTmp;
3611 BOOL found = FALSE;
3612 int i;
3614 lpfp->lpszName = NULL;
3615 do {
3616 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3617 return FALSE;
3619 pszToken = strtok(pszLine, szSpace);
3620 /* ls format
3621 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3623 * For instance:
3624 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3626 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3627 if(!FTP_ParsePermission(pszToken, lpfp))
3628 lpfp->bIsDirectory = FALSE;
3629 for(i=0; i<=3; i++) {
3630 if(!(pszToken = strtok(NULL, szSpace)))
3631 break;
3633 if(!pszToken) continue;
3634 if(lpfp->bIsDirectory) {
3635 TRACE("Is directory\n");
3636 lpfp->nSize = 0;
3638 else {
3639 TRACE("Size: %s\n", pszToken);
3640 lpfp->nSize = atol(pszToken);
3643 lpfp->tmLastModified.wSecond = 0;
3644 lpfp->tmLastModified.wMinute = 0;
3645 lpfp->tmLastModified.wHour = 0;
3646 lpfp->tmLastModified.wDay = 0;
3647 lpfp->tmLastModified.wMonth = 0;
3648 lpfp->tmLastModified.wYear = 0;
3650 /* Determine month */
3651 pszToken = strtok(NULL, szSpace);
3652 if(!pszToken) continue;
3653 if(strlen(pszToken) >= 3) {
3654 pszToken[3] = 0;
3655 if((pszTmp = StrStrIA(szMonths, pszToken)))
3656 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3658 /* Determine day */
3659 pszToken = strtok(NULL, szSpace);
3660 if(!pszToken) continue;
3661 lpfp->tmLastModified.wDay = atoi(pszToken);
3662 /* Determine time or year */
3663 pszToken = strtok(NULL, szSpace);
3664 if(!pszToken) continue;
3665 if((pszTmp = strchr(pszToken, ':'))) {
3666 SYSTEMTIME curr_time;
3667 *pszTmp = 0;
3668 pszTmp++;
3669 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3670 lpfp->tmLastModified.wHour = atoi(pszToken);
3671 GetLocalTime( &curr_time );
3672 lpfp->tmLastModified.wYear = curr_time.wYear;
3674 else {
3675 lpfp->tmLastModified.wYear = atoi(pszToken);
3676 lpfp->tmLastModified.wHour = 12;
3678 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3679 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3680 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3682 pszToken = strtok(NULL, szSpace);
3683 if(!pszToken) continue;
3684 lpfp->lpszName = heap_strdupAtoW(pszToken);
3685 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3687 /* NT way of parsing ... :
3689 07-13-03 08:55PM <DIR> sakpatch
3690 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3692 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3693 int mon, mday, year, hour, min;
3694 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3696 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3697 lpfp->tmLastModified.wDay = mday;
3698 lpfp->tmLastModified.wMonth = mon;
3699 lpfp->tmLastModified.wYear = year;
3701 /* Hacky and bad Y2K protection :-) */
3702 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3704 pszToken = strtok(NULL, szSpace);
3705 if(!pszToken) continue;
3706 sscanf(pszToken, "%d:%d", &hour, &min);
3707 lpfp->tmLastModified.wHour = hour;
3708 lpfp->tmLastModified.wMinute = min;
3709 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3710 lpfp->tmLastModified.wHour += 12;
3712 lpfp->tmLastModified.wSecond = 0;
3714 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3715 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3716 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3718 pszToken = strtok(NULL, szSpace);
3719 if(!pszToken) continue;
3720 if(!strcasecmp(pszToken, "<DIR>")) {
3721 lpfp->bIsDirectory = TRUE;
3722 lpfp->nSize = 0;
3723 TRACE("Is directory\n");
3725 else {
3726 lpfp->bIsDirectory = FALSE;
3727 lpfp->nSize = atol(pszToken);
3728 TRACE("Size: %d\n", lpfp->nSize);
3731 pszToken = strtok(NULL, szSpace);
3732 if(!pszToken) continue;
3733 lpfp->lpszName = heap_strdupAtoW(pszToken);
3734 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3736 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3737 else if(pszToken[0] == '+') {
3738 FIXME("EPLF Format not implemented\n");
3741 if(lpfp->lpszName) {
3742 if((lpszSearchFile == NULL) ||
3743 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3744 found = TRUE;
3745 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3747 else {
3748 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3749 lpfp->lpszName = NULL;
3752 } while(!found);
3753 return TRUE;
3756 /***********************************************************************
3757 * FTP_ParseDirectory (internal)
3759 * Parse string of directory information
3761 * RETURNS
3762 * TRUE on success
3763 * FALSE on failure
3765 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3766 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3768 BOOL bSuccess = TRUE;
3769 INT sizeFilePropArray = 500;/*20; */
3770 INT indexFilePropArray = -1;
3772 TRACE("\n");
3774 /* Allocate initial file properties array */
3775 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3776 if (!*lpafp)
3777 return FALSE;
3779 do {
3780 if (indexFilePropArray+1 >= sizeFilePropArray)
3782 LPFILEPROPERTIESW tmpafp;
3784 sizeFilePropArray *= 2;
3785 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3786 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3787 if (NULL == tmpafp)
3789 bSuccess = FALSE;
3790 break;
3793 *lpafp = tmpafp;
3795 indexFilePropArray++;
3796 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3798 if (bSuccess && indexFilePropArray)
3800 if (indexFilePropArray < sizeFilePropArray - 1)
3802 LPFILEPROPERTIESW tmpafp;
3804 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3805 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3806 if (NULL != tmpafp)
3807 *lpafp = tmpafp;
3809 *dwfp = indexFilePropArray;
3811 else
3813 HeapFree(GetProcessHeap(), 0, *lpafp);
3814 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3815 bSuccess = FALSE;
3818 return bSuccess;
3822 /***********************************************************************
3823 * FTP_ParsePermission (internal)
3825 * Parse permission string of directory information
3827 * RETURNS
3828 * TRUE on success
3829 * FALSE on failure
3832 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3834 BOOL bSuccess = TRUE;
3835 unsigned short nPermission = 0;
3836 INT nPos = 1;
3837 INT nLast = 9;
3839 TRACE("\n");
3840 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3842 bSuccess = FALSE;
3843 return bSuccess;
3846 lpfp->bIsDirectory = (*lpszPermission == 'd');
3849 switch (nPos)
3851 case 1:
3852 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3853 break;
3854 case 2:
3855 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3856 break;
3857 case 3:
3858 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3859 break;
3860 case 4:
3861 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3862 break;
3863 case 5:
3864 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3865 break;
3866 case 6:
3867 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3868 break;
3869 case 7:
3870 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3871 break;
3872 case 8:
3873 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3874 break;
3875 case 9:
3876 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3877 break;
3879 nPos++;
3880 }while (nPos <= nLast);
3882 lpfp->permissions = nPermission;
3883 return bSuccess;
3887 /***********************************************************************
3888 * FTP_SetResponseError (internal)
3890 * Set the appropriate error code for a given response from the server
3892 * RETURNS
3895 static DWORD FTP_SetResponseError(DWORD dwResponse)
3897 DWORD dwCode = 0;
3899 switch(dwResponse)
3901 case 425: /* Cannot open data connection. */
3902 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3903 break;
3905 case 426: /* Connection closed, transer aborted. */
3906 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3907 break;
3909 case 530: /* Not logged in. Login incorrect. */
3910 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3911 break;
3913 case 421: /* Service not available - Server may be shutting down. */
3914 case 450: /* File action not taken. File may be busy. */
3915 case 451: /* Action aborted. Server error. */
3916 case 452: /* Action not taken. Insufficient storage space on server. */
3917 case 500: /* Syntax error. Command unrecognized. */
3918 case 501: /* Syntax error. Error in parameters or arguments. */
3919 case 502: /* Command not implemented. */
3920 case 503: /* Bad sequence of commands. */
3921 case 504: /* Command not implemented for that parameter. */
3922 case 532: /* Need account for storing files */
3923 case 550: /* File action not taken. File not found or no access. */
3924 case 551: /* Requested action aborted. Page type unknown */
3925 case 552: /* Action aborted. Exceeded storage allocation */
3926 case 553: /* Action not taken. File name not allowed. */
3928 default:
3929 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3930 break;
3933 INTERNET_SetLastError(dwCode);
3934 return dwCode;