rpcrt4: Support multiple interfaces and transfer syntaxes in bind and bind ack messages.
[wine.git] / dlls / wininet / ftp.c
blobf0c3cbd02c47302c145aafa875d83ee8f5cf9ba0
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 } ftp_file_t;
81 struct _ftp_session_t
83 object_header_t hdr;
84 appinfo_t *lpAppInfo;
85 int sndSocket;
86 int lstnSocket;
87 int pasvSocket; /* data socket connected by us in case of passive FTP */
88 ftp_file_t *download_in_progress;
89 struct sockaddr_in socketAddress;
90 struct sockaddr_in lstnSocketAddress;
91 LPWSTR lpszPassword;
92 LPWSTR lpszUserName;
95 typedef struct
97 BOOL bIsDirectory;
98 LPWSTR lpszName;
99 DWORD nSize;
100 SYSTEMTIME tmLastModified;
101 unsigned short permissions;
102 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
104 typedef struct
106 object_header_t hdr;
107 ftp_session_t *lpFtpSession;
108 DWORD index;
109 DWORD size;
110 LPFILEPROPERTIESW lpafp;
111 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
113 #define DATA_PACKET_SIZE 0x2000
114 #define szCRLF "\r\n"
115 #define MAX_BACKLOG 5
117 /* Testing shows that Windows only accepts dwFlags where the last
118 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
120 #define FTP_CONDITION_MASK 0x0007
122 typedef enum {
123 /* FTP commands with arguments. */
124 FTP_CMD_ACCT,
125 FTP_CMD_CWD,
126 FTP_CMD_DELE,
127 FTP_CMD_MKD,
128 FTP_CMD_PASS,
129 FTP_CMD_PORT,
130 FTP_CMD_RETR,
131 FTP_CMD_RMD,
132 FTP_CMD_RNFR,
133 FTP_CMD_RNTO,
134 FTP_CMD_STOR,
135 FTP_CMD_TYPE,
136 FTP_CMD_USER,
137 FTP_CMD_SIZE,
139 /* FTP commands without arguments. */
140 FTP_CMD_ABOR,
141 FTP_CMD_LIST,
142 FTP_CMD_NLST,
143 FTP_CMD_PASV,
144 FTP_CMD_PWD,
145 FTP_CMD_QUIT,
146 } FTP_COMMAND;
148 static const CHAR *const szFtpCommands[] = {
149 "ACCT",
150 "CWD",
151 "DELE",
152 "MKD",
153 "PASS",
154 "PORT",
155 "RETR",
156 "RMD",
157 "RNFR",
158 "RNTO",
159 "STOR",
160 "TYPE",
161 "USER",
162 "SIZE",
163 "ABOR",
164 "LIST",
165 "NLST",
166 "PASV",
167 "PWD",
168 "QUIT",
171 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
172 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
174 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
175 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext);
176 static BOOL FTP_SendStore(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
177 static BOOL FTP_GetDataSocket(ftp_session_t*, LPINT nDataSocket);
178 static BOOL FTP_SendData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
179 static INT FTP_ReceiveResponse(ftp_session_t*, DWORD_PTR dwContext);
180 static BOOL FTP_SendRetrieve(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
181 static BOOL FTP_RetrieveFileData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
182 static BOOL FTP_InitListenSocket(ftp_session_t*);
183 static BOOL FTP_ConnectToHost(ftp_session_t*);
184 static BOOL FTP_SendPassword(ftp_session_t*);
185 static BOOL FTP_SendAccount(ftp_session_t*);
186 static BOOL FTP_SendType(ftp_session_t*, DWORD dwType);
187 static BOOL FTP_SendPort(ftp_session_t*);
188 static BOOL FTP_DoPassive(ftp_session_t*);
189 static BOOL FTP_SendPortOrPasv(ftp_session_t*);
190 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
191 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
192 static BOOL FTP_ParseDirectory(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
193 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
194 static HINTERNET FTP_ReceiveFileList(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
195 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
196 static DWORD FTP_SetResponseError(DWORD dwResponse);
197 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
198 static BOOL FTP_FtpPutFileW(ftp_session_t*, LPCWSTR lpszLocalFile,
199 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
200 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
201 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
202 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t*,
203 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
204 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t*, LPWSTR lpszCurrentDirectory,
205 LPDWORD lpdwCurrentDirectory);
206 static BOOL FTP_FtpRenameFileW(ftp_session_t*, LPCWSTR lpszSrc, LPCWSTR lpszDest);
207 static BOOL FTP_FtpRemoveDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
208 static BOOL FTP_FtpDeleteFileW(ftp_session_t*, LPCWSTR lpszFileName);
209 static BOOL FTP_FtpGetFileW(ftp_session_t*, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
210 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
211 DWORD_PTR dwContext);
214 /***********************************************************************
215 * FtpPutFileA (WININET.@)
217 * Uploads a file to the FTP server
219 * RETURNS
220 * TRUE on success
221 * FALSE on failure
224 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
225 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
227 LPWSTR lpwzLocalFile;
228 LPWSTR lpwzNewRemoteFile;
229 BOOL ret;
231 lpwzLocalFile = heap_strdupAtoW(lpszLocalFile);
232 lpwzNewRemoteFile = heap_strdupAtoW(lpszNewRemoteFile);
233 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
234 dwFlags, dwContext);
235 HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
236 HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
237 return ret;
240 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
242 struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
243 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
245 TRACE("%p\n", lpwfs);
247 FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
248 req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
250 HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
251 HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
254 /***********************************************************************
255 * FtpPutFileW (WININET.@)
257 * Uploads a file to the FTP server
259 * RETURNS
260 * TRUE on success
261 * FALSE on failure
264 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
265 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
267 ftp_session_t *lpwfs;
268 appinfo_t *hIC = NULL;
269 BOOL r = FALSE;
271 if (!lpszLocalFile || !lpszNewRemoteFile)
273 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
274 return FALSE;
277 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
278 if (!lpwfs)
280 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
281 return FALSE;
284 if (WH_HFTPSESSION != lpwfs->hdr.htype)
286 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
287 goto lend;
290 if (lpwfs->download_in_progress != NULL)
292 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
293 goto lend;
296 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
298 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
299 goto lend;
302 hIC = lpwfs->lpAppInfo;
303 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
305 WORKREQUEST workRequest;
306 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
308 workRequest.asyncproc = AsyncFtpPutFileProc;
309 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
310 req->lpszLocalFile = heap_strdupW(lpszLocalFile);
311 req->lpszNewRemoteFile = heap_strdupW(lpszNewRemoteFile);
312 req->dwFlags = dwFlags;
313 req->dwContext = dwContext;
315 r = INTERNET_AsyncCall(&workRequest);
317 else
319 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
320 lpszNewRemoteFile, dwFlags, dwContext);
323 lend:
324 WININET_Release( &lpwfs->hdr );
326 return r;
329 /***********************************************************************
330 * FTP_FtpPutFileW (Internal)
332 * Uploads a file to the FTP server
334 * RETURNS
335 * TRUE on success
336 * FALSE on failure
339 static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
340 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
342 HANDLE hFile;
343 BOOL bSuccess = FALSE;
344 appinfo_t *hIC = NULL;
345 INT nResCode;
347 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
349 /* Clear any error information */
350 INTERNET_SetLastError(0);
352 /* Open file to be uploaded */
353 if (INVALID_HANDLE_VALUE ==
354 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
355 /* Let CreateFile set the appropriate error */
356 return FALSE;
358 hIC = lpwfs->lpAppInfo;
360 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
362 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
364 INT nDataSocket;
366 /* Get data socket to server */
367 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
369 FTP_SendData(lpwfs, nDataSocket, hFile);
370 closesocket(nDataSocket);
371 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
372 if (nResCode)
374 if (nResCode == 226)
375 bSuccess = TRUE;
376 else
377 FTP_SetResponseError(nResCode);
382 if (lpwfs->lstnSocket != -1)
384 closesocket(lpwfs->lstnSocket);
385 lpwfs->lstnSocket = -1;
388 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
390 INTERNET_ASYNC_RESULT iar;
392 iar.dwResult = (DWORD)bSuccess;
393 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
394 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
395 &iar, sizeof(INTERNET_ASYNC_RESULT));
398 CloseHandle(hFile);
400 return bSuccess;
404 /***********************************************************************
405 * FtpSetCurrentDirectoryA (WININET.@)
407 * Change the working directory on the FTP server
409 * RETURNS
410 * TRUE on success
411 * FALSE on failure
414 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
416 LPWSTR lpwzDirectory;
417 BOOL ret;
419 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
420 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
421 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
422 return ret;
426 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
428 struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
429 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
431 TRACE("%p\n", lpwfs);
433 FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
434 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
437 /***********************************************************************
438 * FtpSetCurrentDirectoryW (WININET.@)
440 * Change the working directory on the FTP server
442 * RETURNS
443 * TRUE on success
444 * FALSE on failure
447 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
449 ftp_session_t *lpwfs = NULL;
450 appinfo_t *hIC = NULL;
451 BOOL r = FALSE;
453 if (!lpszDirectory)
455 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
456 goto lend;
459 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
460 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
462 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
463 goto lend;
466 if (lpwfs->download_in_progress != NULL)
468 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
469 goto lend;
472 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
474 hIC = lpwfs->lpAppInfo;
475 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
477 WORKREQUEST workRequest;
478 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
480 workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
481 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
482 req = &workRequest.u.FtpSetCurrentDirectoryW;
483 req->lpszDirectory = heap_strdupW(lpszDirectory);
485 r = INTERNET_AsyncCall(&workRequest);
487 else
489 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
492 lend:
493 if( lpwfs )
494 WININET_Release( &lpwfs->hdr );
496 return r;
500 /***********************************************************************
501 * FTP_FtpSetCurrentDirectoryW (Internal)
503 * Change the working directory on the FTP server
505 * RETURNS
506 * TRUE on success
507 * FALSE on failure
510 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
512 INT nResCode;
513 appinfo_t *hIC = NULL;
514 DWORD bSuccess = FALSE;
516 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
518 /* Clear any error information */
519 INTERNET_SetLastError(0);
521 hIC = lpwfs->lpAppInfo;
522 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
523 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
524 goto lend;
526 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
528 if (nResCode)
530 if (nResCode == 250)
531 bSuccess = TRUE;
532 else
533 FTP_SetResponseError(nResCode);
536 lend:
537 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
539 INTERNET_ASYNC_RESULT iar;
541 iar.dwResult = bSuccess;
542 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
543 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
544 &iar, sizeof(INTERNET_ASYNC_RESULT));
546 return bSuccess;
550 /***********************************************************************
551 * FtpCreateDirectoryA (WININET.@)
553 * Create new directory on the FTP server
555 * RETURNS
556 * TRUE on success
557 * FALSE on failure
560 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
562 LPWSTR lpwzDirectory;
563 BOOL ret;
565 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
566 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
567 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
568 return ret;
572 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
574 struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
575 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
577 TRACE(" %p\n", lpwfs);
579 FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
580 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
583 /***********************************************************************
584 * FtpCreateDirectoryW (WININET.@)
586 * Create new directory on the FTP server
588 * RETURNS
589 * TRUE on success
590 * FALSE on failure
593 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
595 ftp_session_t *lpwfs;
596 appinfo_t *hIC = NULL;
597 BOOL r = FALSE;
599 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
600 if (!lpwfs)
602 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
603 return FALSE;
606 if (WH_HFTPSESSION != lpwfs->hdr.htype)
608 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
609 goto lend;
612 if (lpwfs->download_in_progress != NULL)
614 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
615 goto lend;
618 if (!lpszDirectory)
620 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
621 goto lend;
624 hIC = lpwfs->lpAppInfo;
625 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
627 WORKREQUEST workRequest;
628 struct WORKREQ_FTPCREATEDIRECTORYW *req;
630 workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
631 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
632 req = &workRequest.u.FtpCreateDirectoryW;
633 req->lpszDirectory = heap_strdupW(lpszDirectory);
635 r = INTERNET_AsyncCall(&workRequest);
637 else
639 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
641 lend:
642 WININET_Release( &lpwfs->hdr );
644 return r;
648 /***********************************************************************
649 * FTP_FtpCreateDirectoryW (Internal)
651 * Create new directory on the FTP server
653 * RETURNS
654 * TRUE on success
655 * FALSE on failure
658 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
660 INT nResCode;
661 BOOL bSuccess = FALSE;
662 appinfo_t *hIC = NULL;
664 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
666 /* Clear any error information */
667 INTERNET_SetLastError(0);
669 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
670 goto lend;
672 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
673 if (nResCode)
675 if (nResCode == 257)
676 bSuccess = TRUE;
677 else
678 FTP_SetResponseError(nResCode);
681 lend:
682 hIC = lpwfs->lpAppInfo;
683 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
685 INTERNET_ASYNC_RESULT iar;
687 iar.dwResult = (DWORD)bSuccess;
688 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
689 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
690 &iar, sizeof(INTERNET_ASYNC_RESULT));
693 return bSuccess;
696 /***********************************************************************
697 * FtpFindFirstFileA (WININET.@)
699 * Search the specified directory
701 * RETURNS
702 * HINTERNET on success
703 * NULL on failure
706 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
707 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
709 LPWSTR lpwzSearchFile;
710 WIN32_FIND_DATAW wfd;
711 LPWIN32_FIND_DATAW lpFindFileDataW;
712 HINTERNET ret;
714 lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
715 lpFindFileDataW = lpFindFileData?&wfd:NULL;
716 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
717 HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
719 if (ret && lpFindFileData)
720 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
722 return ret;
726 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
728 struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
729 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
731 TRACE("%p\n", lpwfs);
733 FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
734 req->lpFindFileData, req->dwFlags, req->dwContext);
735 HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
738 /***********************************************************************
739 * FtpFindFirstFileW (WININET.@)
741 * Search the specified directory
743 * RETURNS
744 * HINTERNET on success
745 * NULL on failure
748 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
749 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
751 ftp_session_t *lpwfs;
752 appinfo_t *hIC = NULL;
753 HINTERNET r = NULL;
755 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
756 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
758 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
759 goto lend;
762 if (lpwfs->download_in_progress != NULL)
764 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
765 goto lend;
768 hIC = lpwfs->lpAppInfo;
769 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
771 WORKREQUEST workRequest;
772 struct WORKREQ_FTPFINDFIRSTFILEW *req;
774 workRequest.asyncproc = AsyncFtpFindFirstFileProc;
775 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
776 req = &workRequest.u.FtpFindFirstFileW;
777 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : heap_strdupW(lpszSearchFile);
778 req->lpFindFileData = lpFindFileData;
779 req->dwFlags = dwFlags;
780 req->dwContext= dwContext;
782 INTERNET_AsyncCall(&workRequest);
783 r = NULL;
785 else
787 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
788 dwFlags, dwContext);
790 lend:
791 if( lpwfs )
792 WININET_Release( &lpwfs->hdr );
794 return r;
798 /***********************************************************************
799 * FTP_FtpFindFirstFileW (Internal)
801 * Search the specified directory
803 * RETURNS
804 * HINTERNET on success
805 * NULL on failure
808 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
809 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
811 INT nResCode;
812 appinfo_t *hIC = NULL;
813 HINTERNET hFindNext = NULL;
815 TRACE("\n");
817 /* Clear any error information */
818 INTERNET_SetLastError(0);
820 if (!FTP_InitListenSocket(lpwfs))
821 goto lend;
823 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
824 goto lend;
826 if (!FTP_SendPortOrPasv(lpwfs))
827 goto lend;
829 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
830 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
831 goto lend;
833 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
834 if (nResCode)
836 if (nResCode == 125 || nResCode == 150)
838 INT nDataSocket;
840 /* Get data socket to server */
841 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
843 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
844 closesocket(nDataSocket);
845 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
846 if (nResCode != 226 && nResCode != 250)
847 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
850 else
851 FTP_SetResponseError(nResCode);
854 lend:
855 if (lpwfs->lstnSocket != -1)
857 closesocket(lpwfs->lstnSocket);
858 lpwfs->lstnSocket = -1;
861 hIC = lpwfs->lpAppInfo;
862 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
864 INTERNET_ASYNC_RESULT iar;
866 if (hFindNext)
868 iar.dwResult = (DWORD_PTR)hFindNext;
869 iar.dwError = ERROR_SUCCESS;
870 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
871 &iar, sizeof(INTERNET_ASYNC_RESULT));
874 iar.dwResult = (DWORD_PTR)hFindNext;
875 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
876 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
877 &iar, sizeof(INTERNET_ASYNC_RESULT));
880 return hFindNext;
884 /***********************************************************************
885 * FtpGetCurrentDirectoryA (WININET.@)
887 * Retrieves the current directory
889 * RETURNS
890 * TRUE on success
891 * FALSE on failure
894 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
895 LPDWORD lpdwCurrentDirectory)
897 WCHAR *dir = NULL;
898 DWORD len;
899 BOOL ret;
901 if(lpdwCurrentDirectory) {
902 len = *lpdwCurrentDirectory;
903 if(lpszCurrentDirectory)
905 dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
906 if (NULL == dir)
908 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
909 return FALSE;
913 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
915 if (ret && lpszCurrentDirectory)
916 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
918 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
919 HeapFree(GetProcessHeap(), 0, dir);
920 return ret;
924 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
926 struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
927 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
929 TRACE("%p\n", lpwfs);
931 FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
934 /***********************************************************************
935 * FtpGetCurrentDirectoryW (WININET.@)
937 * Retrieves the current directory
939 * RETURNS
940 * TRUE on success
941 * FALSE on failure
944 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
945 LPDWORD lpdwCurrentDirectory)
947 ftp_session_t *lpwfs;
948 appinfo_t *hIC = NULL;
949 BOOL r = FALSE;
951 TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
953 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
954 if (NULL == lpwfs)
956 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
957 goto lend;
960 if (WH_HFTPSESSION != lpwfs->hdr.htype)
962 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
963 goto lend;
966 if (!lpdwCurrentDirectory)
968 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
969 goto lend;
972 if (lpszCurrentDirectory == NULL)
974 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
975 goto lend;
978 if (lpwfs->download_in_progress != NULL)
980 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
981 goto lend;
984 hIC = lpwfs->lpAppInfo;
985 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
987 WORKREQUEST workRequest;
988 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
990 workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
991 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
992 req = &workRequest.u.FtpGetCurrentDirectoryW;
993 req->lpszDirectory = lpszCurrentDirectory;
994 req->lpdwDirectory = lpdwCurrentDirectory;
996 r = INTERNET_AsyncCall(&workRequest);
998 else
1000 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
1001 lpdwCurrentDirectory);
1004 lend:
1005 if( lpwfs )
1006 WININET_Release( &lpwfs->hdr );
1008 return r;
1012 /***********************************************************************
1013 * FTP_FtpGetCurrentDirectoryW (Internal)
1015 * Retrieves the current directory
1017 * RETURNS
1018 * TRUE on success
1019 * FALSE on failure
1022 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
1023 LPDWORD lpdwCurrentDirectory)
1025 INT nResCode;
1026 appinfo_t *hIC = NULL;
1027 DWORD bSuccess = FALSE;
1029 /* Clear any error information */
1030 INTERNET_SetLastError(0);
1032 hIC = lpwfs->lpAppInfo;
1033 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1034 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1035 goto lend;
1037 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1038 if (nResCode)
1040 if (nResCode == 257) /* Extract directory name */
1042 DWORD firstpos, lastpos, len;
1043 LPWSTR lpszResponseBuffer = heap_strdupAtoW(INTERNET_GetResponseBuffer());
1045 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1047 if ('"' == lpszResponseBuffer[lastpos])
1049 if (!firstpos)
1050 firstpos = lastpos;
1051 else
1052 break;
1055 len = lastpos - firstpos;
1056 if (*lpdwCurrentDirectory >= len)
1058 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1059 lpszCurrentDirectory[len - 1] = 0;
1060 *lpdwCurrentDirectory = len;
1061 bSuccess = TRUE;
1063 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1065 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
1067 else
1068 FTP_SetResponseError(nResCode);
1071 lend:
1072 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1074 INTERNET_ASYNC_RESULT iar;
1076 iar.dwResult = bSuccess;
1077 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1078 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1079 &iar, sizeof(INTERNET_ASYNC_RESULT));
1082 return bSuccess;
1086 /***********************************************************************
1087 * FTPFILE_Destroy(internal)
1089 * Closes the file transfer handle. This also 'cleans' the data queue of
1090 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1093 static void FTPFILE_Destroy(object_header_t *hdr)
1095 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1096 ftp_session_t *lpwfs = lpwh->lpFtpSession;
1097 INT nResCode;
1099 TRACE("\n");
1101 if (!lpwh->session_deleted)
1102 lpwfs->download_in_progress = NULL;
1104 if (lpwh->nDataSocket != -1)
1105 closesocket(lpwh->nDataSocket);
1107 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1108 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1110 WININET_Release(&lpwh->lpFtpSession->hdr);
1112 HeapFree(GetProcessHeap(), 0, lpwh);
1115 static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1117 switch(option) {
1118 case INTERNET_OPTION_HANDLE_TYPE:
1119 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1121 if (*size < sizeof(ULONG))
1122 return ERROR_INSUFFICIENT_BUFFER;
1124 *size = sizeof(DWORD);
1125 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1126 return ERROR_SUCCESS;
1129 return INET_QueryOption(option, buffer, size, unicode);
1132 static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
1134 ftp_file_t *file = (ftp_file_t*)hdr;
1135 int res;
1137 if (file->nDataSocket == -1)
1138 return ERROR_INTERNET_DISCONNECTED;
1140 /* FIXME: FTP should use NETCON_ stuff */
1141 res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1142 *read = res>0 ? res : 0;
1144 return res>=0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME*/
1147 static DWORD FTPFILE_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
1148 DWORD flags, DWORD_PTR context)
1150 return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1153 static DWORD FTPFILE_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
1154 DWORD flags, DWORD_PTR context)
1156 return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1159 static BOOL FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1161 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1162 int res;
1164 res = send(lpwh->nDataSocket, buffer, size, 0);
1166 *written = res>0 ? res : 0;
1167 return res >= 0;
1170 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
1172 INTERNET_ASYNC_RESULT iar;
1173 BYTE buffer[4096];
1174 int available;
1176 TRACE("%p\n", file);
1178 available = recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1180 if(available != -1) {
1181 iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1182 iar.dwError = first_notif ? 0 : available;
1183 }else {
1184 iar.dwResult = 0;
1185 iar.dwError = INTERNET_GetLastError();
1188 INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1189 sizeof(INTERNET_ASYNC_RESULT));
1192 static void FTPFILE_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
1194 ftp_file_t *file = (ftp_file_t*)workRequest->hdr;
1196 FTP_ReceiveRequestData(file, FALSE);
1199 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1201 ftp_file_t *file = (ftp_file_t*) hdr;
1202 int retval, unread = 0;
1204 TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
1206 #ifdef FIONREAD
1207 retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
1208 if (!retval)
1209 TRACE("%d bytes of queued, but unread data\n", unread);
1210 #else
1211 FIXME("FIONREAD not available\n");
1212 #endif
1214 *available = unread;
1216 if(!unread) {
1217 BYTE byte;
1219 *available = 0;
1221 retval = recv(file->nDataSocket, &byte, 1, MSG_PEEK);
1222 if(retval > 0) {
1223 WORKREQUEST workRequest;
1225 *available = 0;
1226 workRequest.asyncproc = FTPFILE_AsyncQueryDataAvailableProc;
1227 workRequest.hdr = WININET_AddRef( &file->hdr );
1229 INTERNET_AsyncCall(&workRequest);
1231 return ERROR_IO_PENDING;
1235 return ERROR_SUCCESS;
1239 static const object_vtbl_t FTPFILEVtbl = {
1240 FTPFILE_Destroy,
1241 NULL,
1242 FTPFILE_QueryOption,
1243 NULL,
1244 FTPFILE_ReadFile,
1245 FTPFILE_ReadFileExA,
1246 FTPFILE_ReadFileExW,
1247 FTPFILE_WriteFile,
1248 FTPFILE_QueryDataAvailable,
1249 NULL
1252 /***********************************************************************
1253 * FTP_FtpOpenFileW (Internal)
1255 * Open a remote file for writing or reading
1257 * RETURNS
1258 * HINTERNET handle on success
1259 * NULL on failure
1262 HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1263 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1264 DWORD_PTR dwContext)
1266 INT nDataSocket;
1267 BOOL bSuccess = FALSE;
1268 ftp_file_t *lpwh = NULL;
1269 appinfo_t *hIC = NULL;
1270 HINTERNET handle = NULL;
1272 TRACE("\n");
1274 /* Clear any error information */
1275 INTERNET_SetLastError(0);
1277 if (GENERIC_READ == fdwAccess)
1279 /* Set up socket to retrieve data */
1280 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1282 else if (GENERIC_WRITE == fdwAccess)
1284 /* Set up socket to send data */
1285 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1288 /* Get data socket to server */
1289 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1291 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(ftp_file_t));
1292 lpwh->hdr.htype = WH_HFILE;
1293 lpwh->hdr.vtbl = &FTPFILEVtbl;
1294 lpwh->hdr.dwFlags = dwFlags;
1295 lpwh->hdr.dwContext = dwContext;
1296 lpwh->hdr.dwInternalFlags = 0;
1297 lpwh->hdr.refs = 1;
1298 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1299 lpwh->nDataSocket = nDataSocket;
1300 lpwh->session_deleted = FALSE;
1302 WININET_AddRef( &lpwfs->hdr );
1303 lpwh->lpFtpSession = lpwfs;
1304 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1306 handle = WININET_AllocHandle( &lpwh->hdr );
1307 if( !handle )
1308 goto lend;
1310 /* Indicate that a download is currently in progress */
1311 lpwfs->download_in_progress = lpwh;
1314 if (lpwfs->lstnSocket != -1)
1316 closesocket(lpwfs->lstnSocket);
1317 lpwfs->lstnSocket = -1;
1320 hIC = lpwfs->lpAppInfo;
1321 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1323 INTERNET_ASYNC_RESULT iar;
1325 if (lpwh)
1327 iar.dwResult = (DWORD_PTR)handle;
1328 iar.dwError = ERROR_SUCCESS;
1329 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1330 &iar, sizeof(INTERNET_ASYNC_RESULT));
1333 if(bSuccess) {
1334 FTP_ReceiveRequestData(lpwh, TRUE);
1335 }else {
1336 iar.dwResult = 0;
1337 iar.dwError = INTERNET_GetLastError();
1338 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1339 &iar, sizeof(INTERNET_ASYNC_RESULT));
1343 lend:
1344 if( lpwh )
1345 WININET_Release( &lpwh->hdr );
1347 return handle;
1351 /***********************************************************************
1352 * FtpOpenFileA (WININET.@)
1354 * Open a remote file for writing or reading
1356 * RETURNS
1357 * HINTERNET handle on success
1358 * NULL on failure
1361 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1362 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1363 DWORD_PTR dwContext)
1365 LPWSTR lpwzFileName;
1366 HINTERNET ret;
1368 lpwzFileName = heap_strdupAtoW(lpszFileName);
1369 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1370 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1371 return ret;
1375 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1377 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1378 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1380 TRACE("%p\n", lpwfs);
1382 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1383 req->dwAccess, req->dwFlags, req->dwContext);
1384 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1387 /***********************************************************************
1388 * FtpOpenFileW (WININET.@)
1390 * Open a remote file for writing or reading
1392 * RETURNS
1393 * HINTERNET handle on success
1394 * NULL on failure
1397 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1398 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1399 DWORD_PTR dwContext)
1401 ftp_session_t *lpwfs;
1402 appinfo_t *hIC = NULL;
1403 HINTERNET r = NULL;
1405 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1406 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1408 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1409 if (!lpwfs)
1411 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1412 return FALSE;
1415 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1417 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1418 goto lend;
1421 if ((!lpszFileName) ||
1422 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1423 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1425 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1426 goto lend;
1429 if (lpwfs->download_in_progress != NULL)
1431 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1432 goto lend;
1435 hIC = lpwfs->lpAppInfo;
1436 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1438 WORKREQUEST workRequest;
1439 struct WORKREQ_FTPOPENFILEW *req;
1441 workRequest.asyncproc = AsyncFtpOpenFileProc;
1442 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1443 req = &workRequest.u.FtpOpenFileW;
1444 req->lpszFilename = heap_strdupW(lpszFileName);
1445 req->dwAccess = fdwAccess;
1446 req->dwFlags = dwFlags;
1447 req->dwContext = dwContext;
1449 INTERNET_AsyncCall(&workRequest);
1450 r = NULL;
1452 else
1454 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1457 lend:
1458 WININET_Release( &lpwfs->hdr );
1460 return r;
1464 /***********************************************************************
1465 * FtpGetFileA (WININET.@)
1467 * Retrieve file from the FTP server
1469 * RETURNS
1470 * TRUE on success
1471 * FALSE on failure
1474 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1475 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1476 DWORD_PTR dwContext)
1478 LPWSTR lpwzRemoteFile;
1479 LPWSTR lpwzNewFile;
1480 BOOL ret;
1482 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1483 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1484 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1485 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1486 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1487 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1488 return ret;
1492 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1494 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1495 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1497 TRACE("%p\n", lpwfs);
1499 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1500 req->lpszNewFile, req->fFailIfExists,
1501 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1502 HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1503 HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1507 /***********************************************************************
1508 * FtpGetFileW (WININET.@)
1510 * Retrieve file from the FTP server
1512 * RETURNS
1513 * TRUE on success
1514 * FALSE on failure
1517 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1518 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1519 DWORD_PTR dwContext)
1521 ftp_session_t *lpwfs;
1522 appinfo_t *hIC = NULL;
1523 BOOL r = FALSE;
1525 if (!lpszRemoteFile || !lpszNewFile)
1527 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1528 return FALSE;
1531 lpwfs = (ftp_session_t*) WININET_GetObject( hInternet );
1532 if (!lpwfs)
1534 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1535 return FALSE;
1538 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1540 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1541 goto lend;
1544 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1546 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1547 goto lend;
1550 if (lpwfs->download_in_progress != NULL)
1552 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1553 goto lend;
1556 hIC = lpwfs->lpAppInfo;
1557 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1559 WORKREQUEST workRequest;
1560 struct WORKREQ_FTPGETFILEW *req;
1562 workRequest.asyncproc = AsyncFtpGetFileProc;
1563 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1564 req = &workRequest.u.FtpGetFileW;
1565 req->lpszRemoteFile = heap_strdupW(lpszRemoteFile);
1566 req->lpszNewFile = heap_strdupW(lpszNewFile);
1567 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1568 req->fFailIfExists = fFailIfExists;
1569 req->dwFlags = dwInternetFlags;
1570 req->dwContext = dwContext;
1572 r = INTERNET_AsyncCall(&workRequest);
1574 else
1576 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1577 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1580 lend:
1581 WININET_Release( &lpwfs->hdr );
1583 return r;
1587 /***********************************************************************
1588 * FTP_FtpGetFileW (Internal)
1590 * Retrieve file from the FTP server
1592 * RETURNS
1593 * TRUE on success
1594 * FALSE on failure
1597 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1598 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1599 DWORD_PTR dwContext)
1601 BOOL bSuccess = FALSE;
1602 HANDLE hFile;
1603 appinfo_t *hIC = NULL;
1605 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1607 /* Clear any error information */
1608 INTERNET_SetLastError(0);
1610 /* Ensure we can write to lpszNewfile by opening it */
1611 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1612 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1613 if (INVALID_HANDLE_VALUE == hFile)
1614 return FALSE;
1616 /* Set up socket to retrieve data */
1617 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1619 INT nDataSocket;
1621 /* Get data socket to server */
1622 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1624 INT nResCode;
1626 /* Receive data */
1627 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1628 closesocket(nDataSocket);
1630 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1631 if (nResCode)
1633 if (nResCode == 226)
1634 bSuccess = TRUE;
1635 else
1636 FTP_SetResponseError(nResCode);
1641 if (lpwfs->lstnSocket != -1)
1643 closesocket(lpwfs->lstnSocket);
1644 lpwfs->lstnSocket = -1;
1647 CloseHandle(hFile);
1649 hIC = lpwfs->lpAppInfo;
1650 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1652 INTERNET_ASYNC_RESULT iar;
1654 iar.dwResult = (DWORD)bSuccess;
1655 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1656 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1657 &iar, sizeof(INTERNET_ASYNC_RESULT));
1660 if (!bSuccess) DeleteFileW(lpszNewFile);
1661 return bSuccess;
1664 /***********************************************************************
1665 * FtpGetFileSize (WININET.@)
1667 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1669 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1671 if (lpdwFileSizeHigh)
1672 *lpdwFileSizeHigh = 0;
1674 return 0;
1677 /***********************************************************************
1678 * FtpDeleteFileA (WININET.@)
1680 * Delete a file on the ftp server
1682 * RETURNS
1683 * TRUE on success
1684 * FALSE on failure
1687 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1689 LPWSTR lpwzFileName;
1690 BOOL ret;
1692 lpwzFileName = heap_strdupAtoW(lpszFileName);
1693 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1694 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1695 return ret;
1698 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1700 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1701 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1703 TRACE("%p\n", lpwfs);
1705 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1706 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1709 /***********************************************************************
1710 * FtpDeleteFileW (WININET.@)
1712 * Delete a file on the ftp server
1714 * RETURNS
1715 * TRUE on success
1716 * FALSE on failure
1719 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1721 ftp_session_t *lpwfs;
1722 appinfo_t *hIC = NULL;
1723 BOOL r = FALSE;
1725 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1726 if (!lpwfs)
1728 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1729 return FALSE;
1732 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1734 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1735 goto lend;
1738 if (lpwfs->download_in_progress != NULL)
1740 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1741 goto lend;
1744 if (!lpszFileName)
1746 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1747 goto lend;
1750 hIC = lpwfs->lpAppInfo;
1751 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1753 WORKREQUEST workRequest;
1754 struct WORKREQ_FTPDELETEFILEW *req;
1756 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1757 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1758 req = &workRequest.u.FtpDeleteFileW;
1759 req->lpszFilename = heap_strdupW(lpszFileName);
1761 r = INTERNET_AsyncCall(&workRequest);
1763 else
1765 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1768 lend:
1769 WININET_Release( &lpwfs->hdr );
1771 return r;
1774 /***********************************************************************
1775 * FTP_FtpDeleteFileW (Internal)
1777 * Delete a file on the ftp server
1779 * RETURNS
1780 * TRUE on success
1781 * FALSE on failure
1784 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1786 INT nResCode;
1787 BOOL bSuccess = FALSE;
1788 appinfo_t *hIC = NULL;
1790 TRACE("%p\n", lpwfs);
1792 /* Clear any error information */
1793 INTERNET_SetLastError(0);
1795 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1796 goto lend;
1798 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1799 if (nResCode)
1801 if (nResCode == 250)
1802 bSuccess = TRUE;
1803 else
1804 FTP_SetResponseError(nResCode);
1806 lend:
1807 hIC = lpwfs->lpAppInfo;
1808 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1810 INTERNET_ASYNC_RESULT iar;
1812 iar.dwResult = (DWORD)bSuccess;
1813 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1814 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1815 &iar, sizeof(INTERNET_ASYNC_RESULT));
1818 return bSuccess;
1822 /***********************************************************************
1823 * FtpRemoveDirectoryA (WININET.@)
1825 * Remove a directory on the ftp server
1827 * RETURNS
1828 * TRUE on success
1829 * FALSE on failure
1832 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1834 LPWSTR lpwzDirectory;
1835 BOOL ret;
1837 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1838 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1839 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1840 return ret;
1843 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1845 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1846 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1848 TRACE("%p\n", lpwfs);
1850 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1851 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1854 /***********************************************************************
1855 * FtpRemoveDirectoryW (WININET.@)
1857 * Remove a directory on the ftp server
1859 * RETURNS
1860 * TRUE on success
1861 * FALSE on failure
1864 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1866 ftp_session_t *lpwfs;
1867 appinfo_t *hIC = NULL;
1868 BOOL r = FALSE;
1870 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1871 if (!lpwfs)
1873 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1874 return FALSE;
1877 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1879 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1880 goto lend;
1883 if (lpwfs->download_in_progress != NULL)
1885 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1886 goto lend;
1889 if (!lpszDirectory)
1891 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1892 goto lend;
1895 hIC = lpwfs->lpAppInfo;
1896 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1898 WORKREQUEST workRequest;
1899 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1901 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1902 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1903 req = &workRequest.u.FtpRemoveDirectoryW;
1904 req->lpszDirectory = heap_strdupW(lpszDirectory);
1906 r = INTERNET_AsyncCall(&workRequest);
1908 else
1910 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1913 lend:
1914 WININET_Release( &lpwfs->hdr );
1916 return r;
1919 /***********************************************************************
1920 * FTP_FtpRemoveDirectoryW (Internal)
1922 * Remove a directory on the ftp server
1924 * RETURNS
1925 * TRUE on success
1926 * FALSE on failure
1929 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
1931 INT nResCode;
1932 BOOL bSuccess = FALSE;
1933 appinfo_t *hIC = NULL;
1935 TRACE("\n");
1937 /* Clear any error information */
1938 INTERNET_SetLastError(0);
1940 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1941 goto lend;
1943 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1944 if (nResCode)
1946 if (nResCode == 250)
1947 bSuccess = TRUE;
1948 else
1949 FTP_SetResponseError(nResCode);
1952 lend:
1953 hIC = lpwfs->lpAppInfo;
1954 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1956 INTERNET_ASYNC_RESULT iar;
1958 iar.dwResult = (DWORD)bSuccess;
1959 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1960 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1961 &iar, sizeof(INTERNET_ASYNC_RESULT));
1964 return bSuccess;
1968 /***********************************************************************
1969 * FtpRenameFileA (WININET.@)
1971 * Rename a file on the ftp server
1973 * RETURNS
1974 * TRUE on success
1975 * FALSE on failure
1978 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1980 LPWSTR lpwzSrc;
1981 LPWSTR lpwzDest;
1982 BOOL ret;
1984 lpwzSrc = heap_strdupAtoW(lpszSrc);
1985 lpwzDest = heap_strdupAtoW(lpszDest);
1986 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1987 HeapFree(GetProcessHeap(), 0, lpwzSrc);
1988 HeapFree(GetProcessHeap(), 0, lpwzDest);
1989 return ret;
1992 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
1994 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
1995 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1997 TRACE("%p\n", lpwfs);
1999 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
2000 HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
2001 HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
2004 /***********************************************************************
2005 * FtpRenameFileW (WININET.@)
2007 * Rename a file on the ftp server
2009 * RETURNS
2010 * TRUE on success
2011 * FALSE on failure
2014 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2016 ftp_session_t *lpwfs;
2017 appinfo_t *hIC = NULL;
2018 BOOL r = FALSE;
2020 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
2021 if (!lpwfs)
2023 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2024 return FALSE;
2027 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2029 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2030 goto lend;
2033 if (lpwfs->download_in_progress != NULL)
2035 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2036 goto lend;
2039 if (!lpszSrc || !lpszDest)
2041 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2042 goto lend;
2045 hIC = lpwfs->lpAppInfo;
2046 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2048 WORKREQUEST workRequest;
2049 struct WORKREQ_FTPRENAMEFILEW *req;
2051 workRequest.asyncproc = AsyncFtpRenameFileProc;
2052 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
2053 req = &workRequest.u.FtpRenameFileW;
2054 req->lpszSrcFile = heap_strdupW(lpszSrc);
2055 req->lpszDestFile = heap_strdupW(lpszDest);
2057 r = INTERNET_AsyncCall(&workRequest);
2059 else
2061 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2064 lend:
2065 WININET_Release( &lpwfs->hdr );
2067 return r;
2070 /***********************************************************************
2071 * FTP_FtpRenameFileW (Internal)
2073 * Rename a file on the ftp server
2075 * RETURNS
2076 * TRUE on success
2077 * FALSE on failure
2080 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2082 INT nResCode;
2083 BOOL bSuccess = FALSE;
2084 appinfo_t *hIC = NULL;
2086 TRACE("\n");
2088 /* Clear any error information */
2089 INTERNET_SetLastError(0);
2091 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2092 goto lend;
2094 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2095 if (nResCode == 350)
2097 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2098 goto lend;
2100 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2103 if (nResCode == 250)
2104 bSuccess = TRUE;
2105 else
2106 FTP_SetResponseError(nResCode);
2108 lend:
2109 hIC = lpwfs->lpAppInfo;
2110 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2112 INTERNET_ASYNC_RESULT iar;
2114 iar.dwResult = (DWORD)bSuccess;
2115 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2116 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2117 &iar, sizeof(INTERNET_ASYNC_RESULT));
2120 return bSuccess;
2123 /***********************************************************************
2124 * FtpCommandA (WININET.@)
2126 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2127 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2129 BOOL r;
2130 WCHAR *cmdW;
2132 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2133 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2135 if (fExpectResponse)
2137 FIXME("data connection not supported\n");
2138 return FALSE;
2141 if (!lpszCommand || !lpszCommand[0])
2143 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2144 return FALSE;
2147 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2149 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2150 return FALSE;
2153 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2155 HeapFree(GetProcessHeap(), 0, cmdW);
2156 return r;
2159 /***********************************************************************
2160 * FtpCommandW (WININET.@)
2162 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2163 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2165 BOOL r = FALSE;
2166 ftp_session_t *lpwfs;
2167 LPSTR cmd = NULL;
2168 DWORD len, nBytesSent= 0;
2169 INT nResCode, nRC = 0;
2171 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2172 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2174 if (!lpszCommand || !lpszCommand[0])
2176 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2177 return FALSE;
2180 if (fExpectResponse)
2182 FIXME("data connection not supported\n");
2183 return FALSE;
2186 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
2187 if (!lpwfs)
2189 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2190 return FALSE;
2193 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2195 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2196 goto lend;
2199 if (lpwfs->download_in_progress != NULL)
2201 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2202 goto lend;
2205 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2206 if ((cmd = HeapAlloc(GetProcessHeap(), 0, len )))
2207 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2208 else
2210 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2211 goto lend;
2214 strcat(cmd, szCRLF);
2215 len--;
2217 TRACE("Sending (%s) len(%d)\n", cmd, len);
2218 while ((nBytesSent < len) && (nRC != -1))
2220 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2221 if (nRC != -1)
2223 nBytesSent += nRC;
2224 TRACE("Sent %d bytes\n", nRC);
2228 if (nBytesSent)
2230 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2231 if (nResCode > 0 && nResCode < 400)
2232 r = TRUE;
2233 else
2234 FTP_SetResponseError(nResCode);
2237 lend:
2238 WININET_Release( &lpwfs->hdr );
2239 HeapFree(GetProcessHeap(), 0, cmd);
2240 return r;
2244 /***********************************************************************
2245 * FTPSESSION_Destroy (internal)
2247 * Deallocate session handle
2249 static void FTPSESSION_Destroy(object_header_t *hdr)
2251 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2253 TRACE("\n");
2255 WININET_Release(&lpwfs->lpAppInfo->hdr);
2257 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2258 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2259 HeapFree(GetProcessHeap(), 0, lpwfs);
2262 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2264 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2266 TRACE("\n");
2268 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2269 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2271 if (lpwfs->download_in_progress != NULL)
2272 lpwfs->download_in_progress->session_deleted = TRUE;
2274 if (lpwfs->sndSocket != -1)
2275 closesocket(lpwfs->sndSocket);
2277 if (lpwfs->lstnSocket != -1)
2278 closesocket(lpwfs->lstnSocket);
2280 if (lpwfs->pasvSocket != -1)
2281 closesocket(lpwfs->pasvSocket);
2283 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2284 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2287 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2289 switch(option) {
2290 case INTERNET_OPTION_HANDLE_TYPE:
2291 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2293 if (*size < sizeof(ULONG))
2294 return ERROR_INSUFFICIENT_BUFFER;
2296 *size = sizeof(DWORD);
2297 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2298 return ERROR_SUCCESS;
2301 return INET_QueryOption(option, buffer, size, unicode);
2304 static const object_vtbl_t FTPSESSIONVtbl = {
2305 FTPSESSION_Destroy,
2306 FTPSESSION_CloseConnection,
2307 FTPSESSION_QueryOption,
2308 NULL,
2309 NULL,
2310 NULL,
2311 NULL,
2312 NULL,
2313 NULL
2317 /***********************************************************************
2318 * FTP_Connect (internal)
2320 * Connect to a ftp server
2322 * RETURNS
2323 * HINTERNET a session handle on success
2324 * NULL on failure
2326 * NOTES:
2328 * Windows uses 'anonymous' as the username, when given a NULL username
2329 * and a NULL password. The password is first looked up in:
2331 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2333 * If this entry is not present it uses the current username as the password.
2337 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2338 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2339 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2340 DWORD dwInternalFlags)
2342 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2343 'M','i','c','r','o','s','o','f','t','\\',
2344 'W','i','n','d','o','w','s','\\',
2345 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2346 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2347 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2348 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2349 static const WCHAR szEmpty[] = {'\0'};
2350 struct sockaddr_in socketAddr;
2351 INT nsocket = -1;
2352 UINT sock_namelen;
2353 BOOL bSuccess = FALSE;
2354 ftp_session_t *lpwfs = NULL;
2355 HINTERNET handle = NULL;
2357 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2358 hIC, debugstr_w(lpszServerName),
2359 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2361 assert( hIC->hdr.htype == WH_HINIT );
2363 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2365 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2366 goto lerror;
2369 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(ftp_session_t));
2370 if (NULL == lpwfs)
2372 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2373 goto lerror;
2376 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2377 nServerPort = INTERNET_DEFAULT_FTP_PORT;
2379 lpwfs->hdr.htype = WH_HFTPSESSION;
2380 lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
2381 lpwfs->hdr.dwFlags = dwFlags;
2382 lpwfs->hdr.dwContext = dwContext;
2383 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2384 lpwfs->hdr.refs = 1;
2385 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2386 lpwfs->download_in_progress = NULL;
2387 lpwfs->sndSocket = -1;
2388 lpwfs->lstnSocket = -1;
2389 lpwfs->pasvSocket = -1;
2391 WININET_AddRef( &hIC->hdr );
2392 lpwfs->lpAppInfo = hIC;
2393 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2395 handle = WININET_AllocHandle( &lpwfs->hdr );
2396 if( !handle )
2398 ERR("Failed to alloc handle\n");
2399 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2400 goto lerror;
2403 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2404 if(strchrW(hIC->lpszProxy, ' '))
2405 FIXME("Several proxies not implemented.\n");
2406 if(hIC->lpszProxyBypass)
2407 FIXME("Proxy bypass is ignored.\n");
2409 if (!lpszUserName || !strlenW(lpszUserName)) {
2410 HKEY key;
2411 WCHAR szPassword[MAX_PATH];
2412 DWORD len = sizeof(szPassword);
2414 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2416 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2417 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2418 /* Nothing in the registry, get the username and use that as the password */
2419 if (!GetUserNameW(szPassword, &len)) {
2420 /* Should never get here, but use an empty password as failsafe */
2421 strcpyW(szPassword, szEmpty);
2424 RegCloseKey(key);
2426 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2427 lpwfs->lpszPassword = heap_strdupW(szPassword);
2429 else {
2430 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2431 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2434 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2435 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2437 INTERNET_ASYNC_RESULT iar;
2439 iar.dwResult = (DWORD_PTR)handle;
2440 iar.dwError = ERROR_SUCCESS;
2442 SendAsyncCallback(&hIC->hdr, dwContext,
2443 INTERNET_STATUS_HANDLE_CREATED, &iar,
2444 sizeof(INTERNET_ASYNC_RESULT));
2447 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2448 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2450 sock_namelen = sizeof(socketAddr);
2451 if (!GetAddress(lpszServerName, nServerPort,
2452 (struct sockaddr *)&socketAddr, &sock_namelen))
2454 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2455 goto lerror;
2458 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2459 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2461 if (socketAddr.sin_family != AF_INET)
2463 WARN("unsupported address family %d\n", socketAddr.sin_family);
2464 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2465 goto lerror;
2467 nsocket = socket(AF_INET,SOCK_STREAM,0);
2468 if (nsocket == -1)
2470 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2471 goto lerror;
2474 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2475 &socketAddr, sock_namelen);
2477 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2479 ERR("Unable to connect (%s)\n", strerror(errno));
2480 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2481 closesocket(nsocket);
2483 else
2485 TRACE("Connected to server\n");
2486 lpwfs->sndSocket = nsocket;
2487 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2488 &socketAddr, sock_namelen);
2490 sock_namelen = sizeof(lpwfs->socketAddress);
2491 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2493 if (FTP_ConnectToHost(lpwfs))
2495 TRACE("Successfully logged into server\n");
2496 bSuccess = TRUE;
2500 lerror:
2501 if (lpwfs) WININET_Release( &lpwfs->hdr );
2503 if (!bSuccess && handle)
2505 WININET_FreeHandle( handle );
2506 handle = NULL;
2509 return handle;
2513 /***********************************************************************
2514 * FTP_ConnectToHost (internal)
2516 * Connect to a ftp server
2518 * RETURNS
2519 * TRUE on success
2520 * NULL on failure
2523 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2525 INT nResCode;
2526 BOOL bSuccess = FALSE;
2528 TRACE("\n");
2529 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2531 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2532 goto lend;
2534 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2535 if (nResCode)
2537 /* Login successful... */
2538 if (nResCode == 230)
2539 bSuccess = TRUE;
2540 /* User name okay, need password... */
2541 else if (nResCode == 331)
2542 bSuccess = FTP_SendPassword(lpwfs);
2543 /* Need account for login... */
2544 else if (nResCode == 332)
2545 bSuccess = FTP_SendAccount(lpwfs);
2546 else
2547 FTP_SetResponseError(nResCode);
2550 TRACE("Returning %d\n", bSuccess);
2551 lend:
2552 return bSuccess;
2556 /***********************************************************************
2557 * FTP_SendCommandA (internal)
2559 * Send command to server
2561 * RETURNS
2562 * TRUE on success
2563 * NULL on failure
2566 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2567 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2569 DWORD len;
2570 CHAR *buf;
2571 DWORD nBytesSent = 0;
2572 int nRC = 0;
2573 DWORD dwParamLen;
2575 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2577 if (lpfnStatusCB)
2579 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2582 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2583 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2584 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2586 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2587 return FALSE;
2589 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2590 dwParamLen ? lpszParam : "", szCRLF);
2592 TRACE("Sending (%s) len(%d)\n", buf, len);
2593 while((nBytesSent < len) && (nRC != -1))
2595 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2596 nBytesSent += nRC;
2599 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2601 if (lpfnStatusCB)
2603 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2604 &nBytesSent, sizeof(DWORD));
2607 TRACE("Sent %d bytes\n", nBytesSent);
2608 return (nRC != -1);
2611 /***********************************************************************
2612 * FTP_SendCommand (internal)
2614 * Send command to server
2616 * RETURNS
2617 * TRUE on success
2618 * NULL on failure
2621 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2622 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2624 BOOL ret;
2625 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2626 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2627 HeapFree(GetProcessHeap(), 0, lpszParamA);
2628 return ret;
2631 /***********************************************************************
2632 * FTP_ReceiveResponse (internal)
2634 * Receive response from server
2636 * RETURNS
2637 * Reply code on success
2638 * 0 on failure
2641 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2643 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2644 DWORD nRecv;
2645 INT rc = 0;
2646 char firstprefix[5];
2647 BOOL multiline = FALSE;
2649 TRACE("socket(%d)\n", lpwfs->sndSocket);
2651 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2653 while(1)
2655 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2656 goto lerror;
2658 if (nRecv >= 3)
2660 if(!multiline)
2662 if(lpszResponse[3] != '-')
2663 break;
2664 else
2665 { /* Start of multiline response. Loop until we get "nnn " */
2666 multiline = TRUE;
2667 memcpy(firstprefix, lpszResponse, 3);
2668 firstprefix[3] = ' ';
2669 firstprefix[4] = '\0';
2672 else
2674 if(!memcmp(firstprefix, lpszResponse, 4))
2675 break;
2680 if (nRecv >= 3)
2682 rc = atoi(lpszResponse);
2684 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2685 &nRecv, sizeof(DWORD));
2688 lerror:
2689 TRACE("return %d\n", rc);
2690 return rc;
2694 /***********************************************************************
2695 * FTP_SendPassword (internal)
2697 * Send password to ftp server
2699 * RETURNS
2700 * TRUE on success
2701 * NULL on failure
2704 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2706 INT nResCode;
2707 BOOL bSuccess = FALSE;
2709 TRACE("\n");
2710 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2711 goto lend;
2713 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2714 if (nResCode)
2716 TRACE("Received reply code %d\n", nResCode);
2717 /* Login successful... */
2718 if (nResCode == 230)
2719 bSuccess = TRUE;
2720 /* Command not implemented, superfluous at the server site... */
2721 /* Need account for login... */
2722 else if (nResCode == 332)
2723 bSuccess = FTP_SendAccount(lpwfs);
2724 else
2725 FTP_SetResponseError(nResCode);
2728 lend:
2729 TRACE("Returning %d\n", bSuccess);
2730 return bSuccess;
2734 /***********************************************************************
2735 * FTP_SendAccount (internal)
2739 * RETURNS
2740 * TRUE on success
2741 * FALSE on failure
2744 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2746 INT nResCode;
2747 BOOL bSuccess = FALSE;
2749 TRACE("\n");
2750 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2751 goto lend;
2753 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2754 if (nResCode)
2755 bSuccess = TRUE;
2756 else
2757 FTP_SetResponseError(nResCode);
2759 lend:
2760 return bSuccess;
2764 /***********************************************************************
2765 * FTP_SendStore (internal)
2767 * Send request to upload file to ftp server
2769 * RETURNS
2770 * TRUE on success
2771 * FALSE on failure
2774 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2776 INT nResCode;
2777 BOOL bSuccess = FALSE;
2779 TRACE("\n");
2780 if (!FTP_InitListenSocket(lpwfs))
2781 goto lend;
2783 if (!FTP_SendType(lpwfs, dwType))
2784 goto lend;
2786 if (!FTP_SendPortOrPasv(lpwfs))
2787 goto lend;
2789 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2790 goto lend;
2791 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2792 if (nResCode)
2794 if (nResCode == 150 || nResCode == 125)
2795 bSuccess = TRUE;
2796 else
2797 FTP_SetResponseError(nResCode);
2800 lend:
2801 if (!bSuccess && lpwfs->lstnSocket != -1)
2803 closesocket(lpwfs->lstnSocket);
2804 lpwfs->lstnSocket = -1;
2807 return bSuccess;
2811 /***********************************************************************
2812 * FTP_InitListenSocket (internal)
2814 * Create a socket to listen for server response
2816 * RETURNS
2817 * TRUE on success
2818 * FALSE on failure
2821 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2823 BOOL bSuccess = FALSE;
2824 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2826 TRACE("\n");
2828 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2829 if (lpwfs->lstnSocket == -1)
2831 TRACE("Unable to create listening socket\n");
2832 goto lend;
2835 /* We obtain our ip addr from the name of the command channel socket */
2836 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2838 /* and get the system to assign us a port */
2839 lpwfs->lstnSocketAddress.sin_port = htons(0);
2841 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2843 TRACE("Unable to bind socket\n");
2844 goto lend;
2847 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2849 TRACE("listen failed\n");
2850 goto lend;
2853 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2854 bSuccess = TRUE;
2856 lend:
2857 if (!bSuccess && lpwfs->lstnSocket != -1)
2859 closesocket(lpwfs->lstnSocket);
2860 lpwfs->lstnSocket = -1;
2863 return bSuccess;
2867 /***********************************************************************
2868 * FTP_SendType (internal)
2870 * Tell server type of data being transferred
2872 * RETURNS
2873 * TRUE on success
2874 * FALSE on failure
2876 * W98SE doesn't cache the type that's currently set
2877 * (i.e. it sends it always),
2878 * so we probably don't want to do that either.
2880 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2882 INT nResCode;
2883 WCHAR type[] = { 'I','\0' };
2884 BOOL bSuccess = FALSE;
2886 TRACE("\n");
2887 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2888 type[0] = 'A';
2890 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2891 goto lend;
2893 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2894 if (nResCode)
2896 if (nResCode == 2)
2897 bSuccess = TRUE;
2898 else
2899 FTP_SetResponseError(nResCode);
2902 lend:
2903 return bSuccess;
2907 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2908 /***********************************************************************
2909 * FTP_GetFileSize (internal)
2911 * Retrieves from the server the size of the given file
2913 * RETURNS
2914 * TRUE on success
2915 * FALSE on failure
2918 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2920 INT nResCode;
2921 BOOL bSuccess = FALSE;
2923 TRACE("\n");
2925 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2926 goto lend;
2928 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2929 if (nResCode)
2931 if (nResCode == 213) {
2932 /* Now parses the output to get the actual file size */
2933 int i;
2934 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2936 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2937 if (lpszResponseBuffer[i] == '\0') return FALSE;
2938 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2940 bSuccess = TRUE;
2941 } else {
2942 FTP_SetResponseError(nResCode);
2946 lend:
2947 return bSuccess;
2949 #endif
2952 /***********************************************************************
2953 * FTP_SendPort (internal)
2955 * Tell server which port to use
2957 * RETURNS
2958 * TRUE on success
2959 * FALSE on failure
2962 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
2964 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2965 INT nResCode;
2966 WCHAR szIPAddress[64];
2967 BOOL bSuccess = FALSE;
2968 TRACE("\n");
2970 sprintfW(szIPAddress, szIPFormat,
2971 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2972 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2973 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2974 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2975 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2976 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2978 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2979 goto lend;
2981 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2982 if (nResCode)
2984 if (nResCode == 200)
2985 bSuccess = TRUE;
2986 else
2987 FTP_SetResponseError(nResCode);
2990 lend:
2991 return bSuccess;
2995 /***********************************************************************
2996 * FTP_DoPassive (internal)
2998 * Tell server that we want to do passive transfers
2999 * and connect data socket
3001 * RETURNS
3002 * TRUE on success
3003 * FALSE on failure
3006 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3008 INT nResCode;
3009 BOOL bSuccess = FALSE;
3011 TRACE("\n");
3012 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3013 goto lend;
3015 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3016 if (nResCode)
3018 if (nResCode == 227)
3020 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3021 LPSTR p;
3022 int f[6];
3023 int i;
3024 char *pAddr, *pPort;
3025 INT nsocket = -1;
3026 struct sockaddr_in dataSocketAddress;
3028 p = lpszResponseBuffer+4; /* skip status code */
3029 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3031 if (*p == '\0')
3033 ERR("no address found in response, aborting\n");
3034 goto lend;
3037 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3038 &f[4], &f[5]) != 6)
3040 ERR("unknown response address format '%s', aborting\n", p);
3041 goto lend;
3043 for (i=0; i < 6; i++)
3044 f[i] = f[i] & 0xff;
3046 dataSocketAddress = lpwfs->socketAddress;
3047 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3048 pPort = (char *)&(dataSocketAddress.sin_port);
3049 pAddr[0] = f[0];
3050 pAddr[1] = f[1];
3051 pAddr[2] = f[2];
3052 pAddr[3] = f[3];
3053 pPort[0] = f[4];
3054 pPort[1] = f[5];
3056 nsocket = socket(AF_INET,SOCK_STREAM,0);
3057 if (nsocket == -1)
3058 goto lend;
3060 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3062 ERR("can't connect passive FTP data port.\n");
3063 closesocket(nsocket);
3064 goto lend;
3066 lpwfs->pasvSocket = nsocket;
3067 bSuccess = TRUE;
3069 else
3070 FTP_SetResponseError(nResCode);
3073 lend:
3074 return bSuccess;
3078 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3080 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3082 if (!FTP_DoPassive(lpwfs))
3083 return FALSE;
3085 else
3087 if (!FTP_SendPort(lpwfs))
3088 return FALSE;
3090 return TRUE;
3094 /***********************************************************************
3095 * FTP_GetDataSocket (internal)
3097 * Either accepts an incoming data socket connection from the server
3098 * or just returns the already opened socket after a PASV command
3099 * in case of passive FTP.
3102 * RETURNS
3103 * TRUE on success
3104 * FALSE on failure
3107 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3109 struct sockaddr_in saddr;
3110 socklen_t addrlen = sizeof(struct sockaddr);
3112 TRACE("\n");
3113 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3115 *nDataSocket = lpwfs->pasvSocket;
3116 lpwfs->pasvSocket = -1;
3118 else
3120 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3121 closesocket(lpwfs->lstnSocket);
3122 lpwfs->lstnSocket = -1;
3124 return *nDataSocket != -1;
3128 /***********************************************************************
3129 * FTP_SendData (internal)
3131 * Send data to the server
3133 * RETURNS
3134 * TRUE on success
3135 * FALSE on failure
3138 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3140 BY_HANDLE_FILE_INFORMATION fi;
3141 DWORD nBytesRead = 0;
3142 DWORD nBytesSent = 0;
3143 DWORD nTotalSent = 0;
3144 DWORD nBytesToSend, nLen;
3145 int nRC = 1;
3146 time_t s_long_time, e_long_time;
3147 LONG nSeconds;
3148 CHAR *lpszBuffer;
3150 TRACE("\n");
3151 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3153 /* Get the size of the file. */
3154 GetFileInformationByHandle(hFile, &fi);
3155 time(&s_long_time);
3159 nBytesToSend = nBytesRead - nBytesSent;
3161 if (nBytesToSend <= 0)
3163 /* Read data from file. */
3164 nBytesSent = 0;
3165 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3166 ERR("Failed reading from file\n");
3168 if (nBytesRead > 0)
3169 nBytesToSend = nBytesRead;
3170 else
3171 break;
3174 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3175 DATA_PACKET_SIZE : nBytesToSend;
3176 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3178 if (nRC != -1)
3180 nBytesSent += nRC;
3181 nTotalSent += nRC;
3184 /* Do some computation to display the status. */
3185 time(&e_long_time);
3186 nSeconds = e_long_time - s_long_time;
3187 if( nSeconds / 60 > 0 )
3189 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3190 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3191 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3193 else
3195 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3196 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3197 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3199 } while (nRC != -1);
3201 TRACE("file transfer complete!\n");
3203 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3205 return nTotalSent;
3209 /***********************************************************************
3210 * FTP_SendRetrieve (internal)
3212 * Send request to retrieve a file
3214 * RETURNS
3215 * Number of bytes to be received on success
3216 * 0 on failure
3219 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3221 INT nResCode;
3222 BOOL ret;
3224 TRACE("\n");
3225 if (!(ret = FTP_InitListenSocket(lpwfs)))
3226 goto lend;
3228 if (!(ret = FTP_SendType(lpwfs, dwType)))
3229 goto lend;
3231 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3232 goto lend;
3234 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3235 goto lend;
3237 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3238 if ((nResCode != 125) && (nResCode != 150)) {
3239 /* That means that we got an error getting the file. */
3240 FTP_SetResponseError(nResCode);
3241 ret = FALSE;
3244 lend:
3245 if (!ret && lpwfs->lstnSocket != -1)
3247 closesocket(lpwfs->lstnSocket);
3248 lpwfs->lstnSocket = -1;
3251 return ret;
3255 /***********************************************************************
3256 * FTP_RetrieveData (internal)
3258 * Retrieve data from server
3260 * RETURNS
3261 * TRUE on success
3262 * FALSE on failure
3265 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3267 DWORD nBytesWritten;
3268 INT nRC = 0;
3269 CHAR *lpszBuffer;
3271 TRACE("\n");
3273 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3274 if (NULL == lpszBuffer)
3276 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3277 return FALSE;
3280 while (nRC != -1)
3282 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3283 if (nRC != -1)
3285 /* other side closed socket. */
3286 if (nRC == 0)
3287 goto recv_end;
3288 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3292 TRACE("Data transfer complete\n");
3294 recv_end:
3295 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3297 return (nRC != -1);
3300 /***********************************************************************
3301 * FTPFINDNEXT_Destroy (internal)
3303 * Deallocate session handle
3305 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3307 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3308 DWORD i;
3310 TRACE("\n");
3312 WININET_Release(&lpwfn->lpFtpSession->hdr);
3314 for (i = 0; i < lpwfn->size; i++)
3316 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3319 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3320 HeapFree(GetProcessHeap(), 0, lpwfn);
3323 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3325 WIN32_FIND_DATAW *find_data = data;
3326 DWORD res = ERROR_SUCCESS;
3328 TRACE("index(%d) size(%d)\n", find->index, find->size);
3330 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3332 if (find->index < find->size) {
3333 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3334 find->index++;
3336 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3337 }else {
3338 res = ERROR_NO_MORE_FILES;
3341 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3343 INTERNET_ASYNC_RESULT iar;
3345 iar.dwResult = (res == ERROR_SUCCESS);
3346 iar.dwError = res;
3348 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3349 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3350 sizeof(INTERNET_ASYNC_RESULT));
3353 return res;
3356 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3358 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3360 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3363 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3365 switch(option) {
3366 case INTERNET_OPTION_HANDLE_TYPE:
3367 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3369 if (*size < sizeof(ULONG))
3370 return ERROR_INSUFFICIENT_BUFFER;
3372 *size = sizeof(DWORD);
3373 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3374 return ERROR_SUCCESS;
3377 return INET_QueryOption(option, buffer, size, unicode);
3380 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3382 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3384 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3386 WORKREQUEST workRequest;
3387 struct WORKREQ_FTPFINDNEXTW *req;
3389 workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3390 workRequest.hdr = WININET_AddRef( &find->hdr );
3391 req = &workRequest.u.FtpFindNextW;
3392 req->lpFindFileData = data;
3394 INTERNET_AsyncCall(&workRequest);
3396 return ERROR_SUCCESS;
3399 return FTPFINDNEXT_FindNextFileProc(find, data);
3402 static const object_vtbl_t FTPFINDNEXTVtbl = {
3403 FTPFINDNEXT_Destroy,
3404 NULL,
3405 FTPFINDNEXT_QueryOption,
3406 NULL,
3407 NULL,
3408 NULL,
3409 NULL,
3410 NULL,
3411 NULL,
3412 FTPFINDNEXT_FindNextFileW
3415 /***********************************************************************
3416 * FTP_ReceiveFileList (internal)
3418 * Read file list from server
3420 * RETURNS
3421 * Handle to file list on success
3422 * NULL on failure
3425 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3426 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3428 DWORD dwSize = 0;
3429 LPFILEPROPERTIESW lpafp = NULL;
3430 LPWININETFTPFINDNEXTW lpwfn = NULL;
3431 HINTERNET handle = 0;
3433 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3435 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3437 if(lpFindFileData)
3438 FTP_ConvertFileProp(lpafp, lpFindFileData);
3440 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3441 if (lpwfn)
3443 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3444 lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3445 lpwfn->hdr.dwContext = dwContext;
3446 lpwfn->hdr.refs = 1;
3447 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3448 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3449 lpwfn->size = dwSize;
3450 lpwfn->lpafp = lpafp;
3452 WININET_AddRef( &lpwfs->hdr );
3453 lpwfn->lpFtpSession = lpwfs;
3454 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3456 handle = WININET_AllocHandle( &lpwfn->hdr );
3460 if( lpwfn )
3461 WININET_Release( &lpwfn->hdr );
3463 TRACE("Matched %d files\n", dwSize);
3464 return handle;
3468 /***********************************************************************
3469 * FTP_ConvertFileProp (internal)
3471 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3473 * RETURNS
3474 * TRUE on success
3475 * FALSE on failure
3478 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3480 BOOL bSuccess = FALSE;
3482 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3484 if (lpafp)
3486 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3487 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3488 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3490 /* Not all fields are filled in */
3491 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3492 lpFindFileData->nFileSizeLow = lpafp->nSize;
3494 if (lpafp->bIsDirectory)
3495 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3497 if (lpafp->lpszName)
3498 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3500 bSuccess = TRUE;
3503 return bSuccess;
3506 /***********************************************************************
3507 * FTP_ParseNextFile (internal)
3509 * Parse the next line in file listing
3511 * RETURNS
3512 * TRUE on success
3513 * FALSE on failure
3515 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3517 static const char szSpace[] = " \t";
3518 DWORD nBufLen;
3519 char *pszLine;
3520 char *pszToken;
3521 char *pszTmp;
3522 BOOL found = FALSE;
3523 int i;
3525 lpfp->lpszName = NULL;
3526 do {
3527 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3528 return FALSE;
3530 pszToken = strtok(pszLine, szSpace);
3531 /* ls format
3532 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3534 * For instance:
3535 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3537 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3538 if(!FTP_ParsePermission(pszToken, lpfp))
3539 lpfp->bIsDirectory = FALSE;
3540 for(i=0; i<=3; i++) {
3541 if(!(pszToken = strtok(NULL, szSpace)))
3542 break;
3544 if(!pszToken) continue;
3545 if(lpfp->bIsDirectory) {
3546 TRACE("Is directory\n");
3547 lpfp->nSize = 0;
3549 else {
3550 TRACE("Size: %s\n", pszToken);
3551 lpfp->nSize = atol(pszToken);
3554 lpfp->tmLastModified.wSecond = 0;
3555 lpfp->tmLastModified.wMinute = 0;
3556 lpfp->tmLastModified.wHour = 0;
3557 lpfp->tmLastModified.wDay = 0;
3558 lpfp->tmLastModified.wMonth = 0;
3559 lpfp->tmLastModified.wYear = 0;
3561 /* Determine month */
3562 pszToken = strtok(NULL, szSpace);
3563 if(!pszToken) continue;
3564 if(strlen(pszToken) >= 3) {
3565 pszToken[3] = 0;
3566 if((pszTmp = StrStrIA(szMonths, pszToken)))
3567 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3569 /* Determine day */
3570 pszToken = strtok(NULL, szSpace);
3571 if(!pszToken) continue;
3572 lpfp->tmLastModified.wDay = atoi(pszToken);
3573 /* Determine time or year */
3574 pszToken = strtok(NULL, szSpace);
3575 if(!pszToken) continue;
3576 if((pszTmp = strchr(pszToken, ':'))) {
3577 SYSTEMTIME curr_time;
3578 *pszTmp = 0;
3579 pszTmp++;
3580 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3581 lpfp->tmLastModified.wHour = atoi(pszToken);
3582 GetLocalTime( &curr_time );
3583 lpfp->tmLastModified.wYear = curr_time.wYear;
3585 else {
3586 lpfp->tmLastModified.wYear = atoi(pszToken);
3587 lpfp->tmLastModified.wHour = 12;
3589 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3590 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3591 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3593 pszToken = strtok(NULL, szSpace);
3594 if(!pszToken) continue;
3595 lpfp->lpszName = heap_strdupAtoW(pszToken);
3596 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3598 /* NT way of parsing ... :
3600 07-13-03 08:55PM <DIR> sakpatch
3601 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3603 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3604 int mon, mday, year, hour, min;
3605 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3607 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3608 lpfp->tmLastModified.wDay = mday;
3609 lpfp->tmLastModified.wMonth = mon;
3610 lpfp->tmLastModified.wYear = year;
3612 /* Hacky and bad Y2K protection :-) */
3613 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3615 pszToken = strtok(NULL, szSpace);
3616 if(!pszToken) continue;
3617 sscanf(pszToken, "%d:%d", &hour, &min);
3618 lpfp->tmLastModified.wHour = hour;
3619 lpfp->tmLastModified.wMinute = min;
3620 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3621 lpfp->tmLastModified.wHour += 12;
3623 lpfp->tmLastModified.wSecond = 0;
3625 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3626 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3627 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3629 pszToken = strtok(NULL, szSpace);
3630 if(!pszToken) continue;
3631 if(!strcasecmp(pszToken, "<DIR>")) {
3632 lpfp->bIsDirectory = TRUE;
3633 lpfp->nSize = 0;
3634 TRACE("Is directory\n");
3636 else {
3637 lpfp->bIsDirectory = FALSE;
3638 lpfp->nSize = atol(pszToken);
3639 TRACE("Size: %d\n", lpfp->nSize);
3642 pszToken = strtok(NULL, szSpace);
3643 if(!pszToken) continue;
3644 lpfp->lpszName = heap_strdupAtoW(pszToken);
3645 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3647 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3648 else if(pszToken[0] == '+') {
3649 FIXME("EPLF Format not implemented\n");
3652 if(lpfp->lpszName) {
3653 if((lpszSearchFile == NULL) ||
3654 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3655 found = TRUE;
3656 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3658 else {
3659 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3660 lpfp->lpszName = NULL;
3663 } while(!found);
3664 return TRUE;
3667 /***********************************************************************
3668 * FTP_ParseDirectory (internal)
3670 * Parse string of directory information
3672 * RETURNS
3673 * TRUE on success
3674 * FALSE on failure
3676 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3677 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3679 BOOL bSuccess = TRUE;
3680 INT sizeFilePropArray = 500;/*20; */
3681 INT indexFilePropArray = -1;
3683 TRACE("\n");
3685 /* Allocate initial file properties array */
3686 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3687 if (!*lpafp)
3688 return FALSE;
3690 do {
3691 if (indexFilePropArray+1 >= sizeFilePropArray)
3693 LPFILEPROPERTIESW tmpafp;
3695 sizeFilePropArray *= 2;
3696 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3697 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3698 if (NULL == tmpafp)
3700 bSuccess = FALSE;
3701 break;
3704 *lpafp = tmpafp;
3706 indexFilePropArray++;
3707 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3709 if (bSuccess && indexFilePropArray)
3711 if (indexFilePropArray < sizeFilePropArray - 1)
3713 LPFILEPROPERTIESW tmpafp;
3715 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3716 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3717 if (NULL != tmpafp)
3718 *lpafp = tmpafp;
3720 *dwfp = indexFilePropArray;
3722 else
3724 HeapFree(GetProcessHeap(), 0, *lpafp);
3725 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3726 bSuccess = FALSE;
3729 return bSuccess;
3733 /***********************************************************************
3734 * FTP_ParsePermission (internal)
3736 * Parse permission string of directory information
3738 * RETURNS
3739 * TRUE on success
3740 * FALSE on failure
3743 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3745 BOOL bSuccess = TRUE;
3746 unsigned short nPermission = 0;
3747 INT nPos = 1;
3748 INT nLast = 9;
3750 TRACE("\n");
3751 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3753 bSuccess = FALSE;
3754 return bSuccess;
3757 lpfp->bIsDirectory = (*lpszPermission == 'd');
3760 switch (nPos)
3762 case 1:
3763 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3764 break;
3765 case 2:
3766 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3767 break;
3768 case 3:
3769 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3770 break;
3771 case 4:
3772 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3773 break;
3774 case 5:
3775 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3776 break;
3777 case 6:
3778 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3779 break;
3780 case 7:
3781 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3782 break;
3783 case 8:
3784 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3785 break;
3786 case 9:
3787 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3788 break;
3790 nPos++;
3791 }while (nPos <= nLast);
3793 lpfp->permissions = nPermission;
3794 return bSuccess;
3798 /***********************************************************************
3799 * FTP_SetResponseError (internal)
3801 * Set the appropriate error code for a given response from the server
3803 * RETURNS
3806 static DWORD FTP_SetResponseError(DWORD dwResponse)
3808 DWORD dwCode = 0;
3810 switch(dwResponse)
3812 case 425: /* Cannot open data connection. */
3813 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3814 break;
3816 case 426: /* Connection closed, transer aborted. */
3817 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3818 break;
3820 case 530: /* Not logged in. Login incorrect. */
3821 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3822 break;
3824 case 421: /* Service not available - Server may be shutting down. */
3825 case 450: /* File action not taken. File may be busy. */
3826 case 451: /* Action aborted. Server error. */
3827 case 452: /* Action not taken. Insufficient storage space on server. */
3828 case 500: /* Syntax error. Command unrecognized. */
3829 case 501: /* Syntax error. Error in parameters or arguments. */
3830 case 502: /* Command not implemented. */
3831 case 503: /* Bad sequence of commands. */
3832 case 504: /* Command not implemented for that parameter. */
3833 case 532: /* Need account for storing files */
3834 case 550: /* File action not taken. File not found or no access. */
3835 case 551: /* Requested action aborted. Page type unknown */
3836 case 552: /* Action aborted. Exceeded storage allocation */
3837 case 553: /* Action not taken. File name not allowed. */
3839 default:
3840 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3841 break;
3844 INTERNET_SetLastError(dwCode);
3845 return dwCode;