push 6c2bed22d4a356b01aae243fbf554b5dba1af534
[wine/hacks.git] / dlls / wininet / ftp.c
blob8894add0500013cfa8d597d17a9f79032d8fc22b
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(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.refs = 1;
1297 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1298 lpwh->nDataSocket = nDataSocket;
1299 lpwh->session_deleted = FALSE;
1301 WININET_AddRef( &lpwfs->hdr );
1302 lpwh->lpFtpSession = lpwfs;
1303 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1305 handle = WININET_AllocHandle( &lpwh->hdr );
1306 if( !handle )
1307 goto lend;
1309 /* Indicate that a download is currently in progress */
1310 lpwfs->download_in_progress = lpwh;
1313 if (lpwfs->lstnSocket != -1)
1315 closesocket(lpwfs->lstnSocket);
1316 lpwfs->lstnSocket = -1;
1319 hIC = lpwfs->lpAppInfo;
1320 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1322 INTERNET_ASYNC_RESULT iar;
1324 if (lpwh)
1326 iar.dwResult = (DWORD_PTR)handle;
1327 iar.dwError = ERROR_SUCCESS;
1328 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1329 &iar, sizeof(INTERNET_ASYNC_RESULT));
1332 if(bSuccess) {
1333 FTP_ReceiveRequestData(lpwh, TRUE);
1334 }else {
1335 iar.dwResult = 0;
1336 iar.dwError = INTERNET_GetLastError();
1337 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1338 &iar, sizeof(INTERNET_ASYNC_RESULT));
1342 lend:
1343 if( lpwh )
1344 WININET_Release( &lpwh->hdr );
1346 return handle;
1350 /***********************************************************************
1351 * FtpOpenFileA (WININET.@)
1353 * Open a remote file for writing or reading
1355 * RETURNS
1356 * HINTERNET handle on success
1357 * NULL on failure
1360 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1361 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1362 DWORD_PTR dwContext)
1364 LPWSTR lpwzFileName;
1365 HINTERNET ret;
1367 lpwzFileName = heap_strdupAtoW(lpszFileName);
1368 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1369 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1370 return ret;
1374 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1376 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1377 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1379 TRACE("%p\n", lpwfs);
1381 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1382 req->dwAccess, req->dwFlags, req->dwContext);
1383 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1386 /***********************************************************************
1387 * FtpOpenFileW (WININET.@)
1389 * Open a remote file for writing or reading
1391 * RETURNS
1392 * HINTERNET handle on success
1393 * NULL on failure
1396 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1397 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1398 DWORD_PTR dwContext)
1400 ftp_session_t *lpwfs;
1401 appinfo_t *hIC = NULL;
1402 HINTERNET r = NULL;
1404 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1405 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1407 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1408 if (!lpwfs)
1410 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1411 return FALSE;
1414 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1416 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1417 goto lend;
1420 if ((!lpszFileName) ||
1421 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1422 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1424 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1425 goto lend;
1428 if (lpwfs->download_in_progress != NULL)
1430 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1431 goto lend;
1434 hIC = lpwfs->lpAppInfo;
1435 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1437 WORKREQUEST workRequest;
1438 struct WORKREQ_FTPOPENFILEW *req;
1440 workRequest.asyncproc = AsyncFtpOpenFileProc;
1441 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1442 req = &workRequest.u.FtpOpenFileW;
1443 req->lpszFilename = heap_strdupW(lpszFileName);
1444 req->dwAccess = fdwAccess;
1445 req->dwFlags = dwFlags;
1446 req->dwContext = dwContext;
1448 INTERNET_AsyncCall(&workRequest);
1449 r = NULL;
1451 else
1453 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1456 lend:
1457 WININET_Release( &lpwfs->hdr );
1459 return r;
1463 /***********************************************************************
1464 * FtpGetFileA (WININET.@)
1466 * Retrieve file from the FTP server
1468 * RETURNS
1469 * TRUE on success
1470 * FALSE on failure
1473 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1474 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1475 DWORD_PTR dwContext)
1477 LPWSTR lpwzRemoteFile;
1478 LPWSTR lpwzNewFile;
1479 BOOL ret;
1481 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1482 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1483 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1484 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1485 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1486 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1487 return ret;
1491 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1493 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1494 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1496 TRACE("%p\n", lpwfs);
1498 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1499 req->lpszNewFile, req->fFailIfExists,
1500 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1501 HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1502 HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1506 /***********************************************************************
1507 * FtpGetFileW (WININET.@)
1509 * Retrieve file from the FTP server
1511 * RETURNS
1512 * TRUE on success
1513 * FALSE on failure
1516 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1517 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1518 DWORD_PTR dwContext)
1520 ftp_session_t *lpwfs;
1521 appinfo_t *hIC = NULL;
1522 BOOL r = FALSE;
1524 if (!lpszRemoteFile || !lpszNewFile)
1526 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1527 return FALSE;
1530 lpwfs = (ftp_session_t*) WININET_GetObject( hInternet );
1531 if (!lpwfs)
1533 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1534 return FALSE;
1537 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1539 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1540 goto lend;
1543 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1545 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1546 goto lend;
1549 if (lpwfs->download_in_progress != NULL)
1551 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1552 goto lend;
1555 hIC = lpwfs->lpAppInfo;
1556 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1558 WORKREQUEST workRequest;
1559 struct WORKREQ_FTPGETFILEW *req;
1561 workRequest.asyncproc = AsyncFtpGetFileProc;
1562 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1563 req = &workRequest.u.FtpGetFileW;
1564 req->lpszRemoteFile = heap_strdupW(lpszRemoteFile);
1565 req->lpszNewFile = heap_strdupW(lpszNewFile);
1566 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1567 req->fFailIfExists = fFailIfExists;
1568 req->dwFlags = dwInternetFlags;
1569 req->dwContext = dwContext;
1571 r = INTERNET_AsyncCall(&workRequest);
1573 else
1575 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1576 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1579 lend:
1580 WININET_Release( &lpwfs->hdr );
1582 return r;
1586 /***********************************************************************
1587 * FTP_FtpGetFileW (Internal)
1589 * Retrieve file from the FTP server
1591 * RETURNS
1592 * TRUE on success
1593 * FALSE on failure
1596 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1597 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1598 DWORD_PTR dwContext)
1600 BOOL bSuccess = FALSE;
1601 HANDLE hFile;
1602 appinfo_t *hIC = NULL;
1604 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1606 /* Clear any error information */
1607 INTERNET_SetLastError(0);
1609 /* Ensure we can write to lpszNewfile by opening it */
1610 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1611 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1612 if (INVALID_HANDLE_VALUE == hFile)
1613 return FALSE;
1615 /* Set up socket to retrieve data */
1616 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1618 INT nDataSocket;
1620 /* Get data socket to server */
1621 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1623 INT nResCode;
1625 /* Receive data */
1626 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1627 closesocket(nDataSocket);
1629 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1630 if (nResCode)
1632 if (nResCode == 226)
1633 bSuccess = TRUE;
1634 else
1635 FTP_SetResponseError(nResCode);
1640 if (lpwfs->lstnSocket != -1)
1642 closesocket(lpwfs->lstnSocket);
1643 lpwfs->lstnSocket = -1;
1646 CloseHandle(hFile);
1648 hIC = lpwfs->lpAppInfo;
1649 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1651 INTERNET_ASYNC_RESULT iar;
1653 iar.dwResult = (DWORD)bSuccess;
1654 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1655 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1656 &iar, sizeof(INTERNET_ASYNC_RESULT));
1659 if (!bSuccess) DeleteFileW(lpszNewFile);
1660 return bSuccess;
1663 /***********************************************************************
1664 * FtpGetFileSize (WININET.@)
1666 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1668 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1670 if (lpdwFileSizeHigh)
1671 *lpdwFileSizeHigh = 0;
1673 return 0;
1676 /***********************************************************************
1677 * FtpDeleteFileA (WININET.@)
1679 * Delete a file on the ftp server
1681 * RETURNS
1682 * TRUE on success
1683 * FALSE on failure
1686 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1688 LPWSTR lpwzFileName;
1689 BOOL ret;
1691 lpwzFileName = heap_strdupAtoW(lpszFileName);
1692 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1693 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1694 return ret;
1697 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1699 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1700 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1702 TRACE("%p\n", lpwfs);
1704 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1705 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1708 /***********************************************************************
1709 * FtpDeleteFileW (WININET.@)
1711 * Delete a file on the ftp server
1713 * RETURNS
1714 * TRUE on success
1715 * FALSE on failure
1718 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1720 ftp_session_t *lpwfs;
1721 appinfo_t *hIC = NULL;
1722 BOOL r = FALSE;
1724 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1725 if (!lpwfs)
1727 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1728 return FALSE;
1731 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1733 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1734 goto lend;
1737 if (lpwfs->download_in_progress != NULL)
1739 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1740 goto lend;
1743 if (!lpszFileName)
1745 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1746 goto lend;
1749 hIC = lpwfs->lpAppInfo;
1750 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1752 WORKREQUEST workRequest;
1753 struct WORKREQ_FTPDELETEFILEW *req;
1755 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1756 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1757 req = &workRequest.u.FtpDeleteFileW;
1758 req->lpszFilename = heap_strdupW(lpszFileName);
1760 r = INTERNET_AsyncCall(&workRequest);
1762 else
1764 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1767 lend:
1768 WININET_Release( &lpwfs->hdr );
1770 return r;
1773 /***********************************************************************
1774 * FTP_FtpDeleteFileW (Internal)
1776 * Delete a file on the ftp server
1778 * RETURNS
1779 * TRUE on success
1780 * FALSE on failure
1783 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1785 INT nResCode;
1786 BOOL bSuccess = FALSE;
1787 appinfo_t *hIC = NULL;
1789 TRACE("%p\n", lpwfs);
1791 /* Clear any error information */
1792 INTERNET_SetLastError(0);
1794 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1795 goto lend;
1797 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1798 if (nResCode)
1800 if (nResCode == 250)
1801 bSuccess = TRUE;
1802 else
1803 FTP_SetResponseError(nResCode);
1805 lend:
1806 hIC = lpwfs->lpAppInfo;
1807 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1809 INTERNET_ASYNC_RESULT iar;
1811 iar.dwResult = (DWORD)bSuccess;
1812 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1813 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1814 &iar, sizeof(INTERNET_ASYNC_RESULT));
1817 return bSuccess;
1821 /***********************************************************************
1822 * FtpRemoveDirectoryA (WININET.@)
1824 * Remove a directory on the ftp server
1826 * RETURNS
1827 * TRUE on success
1828 * FALSE on failure
1831 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1833 LPWSTR lpwzDirectory;
1834 BOOL ret;
1836 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1837 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1838 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1839 return ret;
1842 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1844 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1845 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1847 TRACE("%p\n", lpwfs);
1849 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1850 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1853 /***********************************************************************
1854 * FtpRemoveDirectoryW (WININET.@)
1856 * Remove a directory on the ftp server
1858 * RETURNS
1859 * TRUE on success
1860 * FALSE on failure
1863 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1865 ftp_session_t *lpwfs;
1866 appinfo_t *hIC = NULL;
1867 BOOL r = FALSE;
1869 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1870 if (!lpwfs)
1872 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1873 return FALSE;
1876 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1878 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1879 goto lend;
1882 if (lpwfs->download_in_progress != NULL)
1884 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1885 goto lend;
1888 if (!lpszDirectory)
1890 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1891 goto lend;
1894 hIC = lpwfs->lpAppInfo;
1895 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1897 WORKREQUEST workRequest;
1898 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1900 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1901 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1902 req = &workRequest.u.FtpRemoveDirectoryW;
1903 req->lpszDirectory = heap_strdupW(lpszDirectory);
1905 r = INTERNET_AsyncCall(&workRequest);
1907 else
1909 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1912 lend:
1913 WININET_Release( &lpwfs->hdr );
1915 return r;
1918 /***********************************************************************
1919 * FTP_FtpRemoveDirectoryW (Internal)
1921 * Remove a directory on the ftp server
1923 * RETURNS
1924 * TRUE on success
1925 * FALSE on failure
1928 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
1930 INT nResCode;
1931 BOOL bSuccess = FALSE;
1932 appinfo_t *hIC = NULL;
1934 TRACE("\n");
1936 /* Clear any error information */
1937 INTERNET_SetLastError(0);
1939 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1940 goto lend;
1942 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1943 if (nResCode)
1945 if (nResCode == 250)
1946 bSuccess = TRUE;
1947 else
1948 FTP_SetResponseError(nResCode);
1951 lend:
1952 hIC = lpwfs->lpAppInfo;
1953 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1955 INTERNET_ASYNC_RESULT iar;
1957 iar.dwResult = (DWORD)bSuccess;
1958 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1959 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1960 &iar, sizeof(INTERNET_ASYNC_RESULT));
1963 return bSuccess;
1967 /***********************************************************************
1968 * FtpRenameFileA (WININET.@)
1970 * Rename a file on the ftp server
1972 * RETURNS
1973 * TRUE on success
1974 * FALSE on failure
1977 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1979 LPWSTR lpwzSrc;
1980 LPWSTR lpwzDest;
1981 BOOL ret;
1983 lpwzSrc = heap_strdupAtoW(lpszSrc);
1984 lpwzDest = heap_strdupAtoW(lpszDest);
1985 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1986 HeapFree(GetProcessHeap(), 0, lpwzSrc);
1987 HeapFree(GetProcessHeap(), 0, lpwzDest);
1988 return ret;
1991 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
1993 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
1994 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1996 TRACE("%p\n", lpwfs);
1998 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
1999 HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
2000 HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
2003 /***********************************************************************
2004 * FtpRenameFileW (WININET.@)
2006 * Rename a file on the ftp server
2008 * RETURNS
2009 * TRUE on success
2010 * FALSE on failure
2013 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2015 ftp_session_t *lpwfs;
2016 appinfo_t *hIC = NULL;
2017 BOOL r = FALSE;
2019 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
2020 if (!lpwfs)
2022 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2023 return FALSE;
2026 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2028 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2029 goto lend;
2032 if (lpwfs->download_in_progress != NULL)
2034 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2035 goto lend;
2038 if (!lpszSrc || !lpszDest)
2040 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2041 goto lend;
2044 hIC = lpwfs->lpAppInfo;
2045 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2047 WORKREQUEST workRequest;
2048 struct WORKREQ_FTPRENAMEFILEW *req;
2050 workRequest.asyncproc = AsyncFtpRenameFileProc;
2051 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
2052 req = &workRequest.u.FtpRenameFileW;
2053 req->lpszSrcFile = heap_strdupW(lpszSrc);
2054 req->lpszDestFile = heap_strdupW(lpszDest);
2056 r = INTERNET_AsyncCall(&workRequest);
2058 else
2060 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2063 lend:
2064 WININET_Release( &lpwfs->hdr );
2066 return r;
2069 /***********************************************************************
2070 * FTP_FtpRenameFileW (Internal)
2072 * Rename a file on the ftp server
2074 * RETURNS
2075 * TRUE on success
2076 * FALSE on failure
2079 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2081 INT nResCode;
2082 BOOL bSuccess = FALSE;
2083 appinfo_t *hIC = NULL;
2085 TRACE("\n");
2087 /* Clear any error information */
2088 INTERNET_SetLastError(0);
2090 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2091 goto lend;
2093 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2094 if (nResCode == 350)
2096 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2097 goto lend;
2099 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2102 if (nResCode == 250)
2103 bSuccess = TRUE;
2104 else
2105 FTP_SetResponseError(nResCode);
2107 lend:
2108 hIC = lpwfs->lpAppInfo;
2109 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2111 INTERNET_ASYNC_RESULT iar;
2113 iar.dwResult = (DWORD)bSuccess;
2114 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2115 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2116 &iar, sizeof(INTERNET_ASYNC_RESULT));
2119 return bSuccess;
2122 /***********************************************************************
2123 * FtpCommandA (WININET.@)
2125 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2126 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2128 BOOL r;
2129 WCHAR *cmdW;
2131 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2132 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2134 if (fExpectResponse)
2136 FIXME("data connection not supported\n");
2137 return FALSE;
2140 if (!lpszCommand || !lpszCommand[0])
2142 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2143 return FALSE;
2146 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2148 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2149 return FALSE;
2152 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2154 HeapFree(GetProcessHeap(), 0, cmdW);
2155 return r;
2158 /***********************************************************************
2159 * FtpCommandW (WININET.@)
2161 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2162 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2164 BOOL r = FALSE;
2165 ftp_session_t *lpwfs;
2166 LPSTR cmd = NULL;
2167 DWORD len, nBytesSent= 0;
2168 INT nResCode, nRC = 0;
2170 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2171 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2173 if (!lpszCommand || !lpszCommand[0])
2175 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2176 return FALSE;
2179 if (fExpectResponse)
2181 FIXME("data connection not supported\n");
2182 return FALSE;
2185 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
2186 if (!lpwfs)
2188 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2189 return FALSE;
2192 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2194 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2195 goto lend;
2198 if (lpwfs->download_in_progress != NULL)
2200 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2201 goto lend;
2204 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2205 if ((cmd = HeapAlloc(GetProcessHeap(), 0, len )))
2206 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2207 else
2209 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2210 goto lend;
2213 strcat(cmd, szCRLF);
2214 len--;
2216 TRACE("Sending (%s) len(%d)\n", cmd, len);
2217 while ((nBytesSent < len) && (nRC != -1))
2219 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2220 if (nRC != -1)
2222 nBytesSent += nRC;
2223 TRACE("Sent %d bytes\n", nRC);
2227 if (nBytesSent)
2229 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2230 if (nResCode > 0 && nResCode < 400)
2231 r = TRUE;
2232 else
2233 FTP_SetResponseError(nResCode);
2236 lend:
2237 WININET_Release( &lpwfs->hdr );
2238 HeapFree(GetProcessHeap(), 0, cmd);
2239 return r;
2243 /***********************************************************************
2244 * FTPSESSION_Destroy (internal)
2246 * Deallocate session handle
2248 static void FTPSESSION_Destroy(object_header_t *hdr)
2250 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2252 TRACE("\n");
2254 WININET_Release(&lpwfs->lpAppInfo->hdr);
2256 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2257 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2258 HeapFree(GetProcessHeap(), 0, lpwfs);
2261 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2263 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2265 TRACE("\n");
2267 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2268 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2270 if (lpwfs->download_in_progress != NULL)
2271 lpwfs->download_in_progress->session_deleted = TRUE;
2273 if (lpwfs->sndSocket != -1)
2274 closesocket(lpwfs->sndSocket);
2276 if (lpwfs->lstnSocket != -1)
2277 closesocket(lpwfs->lstnSocket);
2279 if (lpwfs->pasvSocket != -1)
2280 closesocket(lpwfs->pasvSocket);
2282 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2283 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2286 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2288 switch(option) {
2289 case INTERNET_OPTION_HANDLE_TYPE:
2290 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2292 if (*size < sizeof(ULONG))
2293 return ERROR_INSUFFICIENT_BUFFER;
2295 *size = sizeof(DWORD);
2296 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2297 return ERROR_SUCCESS;
2300 return INET_QueryOption(option, buffer, size, unicode);
2303 static const object_vtbl_t FTPSESSIONVtbl = {
2304 FTPSESSION_Destroy,
2305 FTPSESSION_CloseConnection,
2306 FTPSESSION_QueryOption,
2307 NULL,
2308 NULL,
2309 NULL,
2310 NULL,
2311 NULL,
2312 NULL
2316 /***********************************************************************
2317 * FTP_Connect (internal)
2319 * Connect to a ftp server
2321 * RETURNS
2322 * HINTERNET a session handle on success
2323 * NULL on failure
2325 * NOTES:
2327 * Windows uses 'anonymous' as the username, when given a NULL username
2328 * and a NULL password. The password is first looked up in:
2330 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2332 * If this entry is not present it uses the current username as the password.
2336 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2337 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2338 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2339 DWORD dwInternalFlags)
2341 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2342 'M','i','c','r','o','s','o','f','t','\\',
2343 'W','i','n','d','o','w','s','\\',
2344 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2345 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2346 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2347 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2348 static const WCHAR szEmpty[] = {'\0'};
2349 struct sockaddr_in socketAddr;
2350 INT nsocket = -1;
2351 UINT sock_namelen;
2352 BOOL bSuccess = FALSE;
2353 ftp_session_t *lpwfs = NULL;
2354 HINTERNET handle = NULL;
2356 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2357 hIC, debugstr_w(lpszServerName),
2358 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2360 assert( hIC->hdr.htype == WH_HINIT );
2362 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2364 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2365 goto lerror;
2368 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(ftp_session_t));
2369 if (NULL == lpwfs)
2371 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2372 goto lerror;
2375 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2376 nServerPort = INTERNET_DEFAULT_FTP_PORT;
2378 lpwfs->hdr.htype = WH_HFTPSESSION;
2379 lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
2380 lpwfs->hdr.dwFlags = dwFlags;
2381 lpwfs->hdr.dwContext = dwContext;
2382 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2383 lpwfs->hdr.refs = 1;
2384 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2385 lpwfs->download_in_progress = NULL;
2386 lpwfs->sndSocket = -1;
2387 lpwfs->lstnSocket = -1;
2388 lpwfs->pasvSocket = -1;
2390 WININET_AddRef( &hIC->hdr );
2391 lpwfs->lpAppInfo = hIC;
2392 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2394 handle = WININET_AllocHandle( &lpwfs->hdr );
2395 if( !handle )
2397 ERR("Failed to alloc handle\n");
2398 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2399 goto lerror;
2402 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2403 if(strchrW(hIC->lpszProxy, ' '))
2404 FIXME("Several proxies not implemented.\n");
2405 if(hIC->lpszProxyBypass)
2406 FIXME("Proxy bypass is ignored.\n");
2408 if (!lpszUserName || !strlenW(lpszUserName)) {
2409 HKEY key;
2410 WCHAR szPassword[MAX_PATH];
2411 DWORD len = sizeof(szPassword);
2413 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2415 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2416 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2417 /* Nothing in the registry, get the username and use that as the password */
2418 if (!GetUserNameW(szPassword, &len)) {
2419 /* Should never get here, but use an empty password as failsafe */
2420 strcpyW(szPassword, szEmpty);
2423 RegCloseKey(key);
2425 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2426 lpwfs->lpszPassword = heap_strdupW(szPassword);
2428 else {
2429 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2430 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2433 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2434 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2436 INTERNET_ASYNC_RESULT iar;
2438 iar.dwResult = (DWORD_PTR)handle;
2439 iar.dwError = ERROR_SUCCESS;
2441 SendAsyncCallback(&hIC->hdr, dwContext,
2442 INTERNET_STATUS_HANDLE_CREATED, &iar,
2443 sizeof(INTERNET_ASYNC_RESULT));
2446 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2447 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2449 sock_namelen = sizeof(socketAddr);
2450 if (!GetAddress(lpszServerName, nServerPort,
2451 (struct sockaddr *)&socketAddr, &sock_namelen))
2453 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2454 goto lerror;
2457 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2458 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2460 if (socketAddr.sin_family != AF_INET)
2462 WARN("unsupported address family %d\n", socketAddr.sin_family);
2463 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2464 goto lerror;
2466 nsocket = socket(AF_INET,SOCK_STREAM,0);
2467 if (nsocket == -1)
2469 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2470 goto lerror;
2473 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2474 &socketAddr, sock_namelen);
2476 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2478 ERR("Unable to connect (%s)\n", strerror(errno));
2479 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2480 closesocket(nsocket);
2482 else
2484 TRACE("Connected to server\n");
2485 lpwfs->sndSocket = nsocket;
2486 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2487 &socketAddr, sock_namelen);
2489 sock_namelen = sizeof(lpwfs->socketAddress);
2490 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2492 if (FTP_ConnectToHost(lpwfs))
2494 TRACE("Successfully logged into server\n");
2495 bSuccess = TRUE;
2499 lerror:
2500 if (lpwfs) WININET_Release( &lpwfs->hdr );
2502 if (!bSuccess && handle)
2504 WININET_FreeHandle( handle );
2505 handle = NULL;
2508 return handle;
2512 /***********************************************************************
2513 * FTP_ConnectToHost (internal)
2515 * Connect to a ftp server
2517 * RETURNS
2518 * TRUE on success
2519 * NULL on failure
2522 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2524 INT nResCode;
2525 BOOL bSuccess = FALSE;
2527 TRACE("\n");
2528 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2530 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2531 goto lend;
2533 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2534 if (nResCode)
2536 /* Login successful... */
2537 if (nResCode == 230)
2538 bSuccess = TRUE;
2539 /* User name okay, need password... */
2540 else if (nResCode == 331)
2541 bSuccess = FTP_SendPassword(lpwfs);
2542 /* Need account for login... */
2543 else if (nResCode == 332)
2544 bSuccess = FTP_SendAccount(lpwfs);
2545 else
2546 FTP_SetResponseError(nResCode);
2549 TRACE("Returning %d\n", bSuccess);
2550 lend:
2551 return bSuccess;
2555 /***********************************************************************
2556 * FTP_SendCommandA (internal)
2558 * Send command to server
2560 * RETURNS
2561 * TRUE on success
2562 * NULL on failure
2565 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2566 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2568 DWORD len;
2569 CHAR *buf;
2570 DWORD nBytesSent = 0;
2571 int nRC = 0;
2572 DWORD dwParamLen;
2574 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2576 if (lpfnStatusCB)
2578 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2581 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2582 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2583 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2585 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2586 return FALSE;
2588 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2589 dwParamLen ? lpszParam : "", szCRLF);
2591 TRACE("Sending (%s) len(%d)\n", buf, len);
2592 while((nBytesSent < len) && (nRC != -1))
2594 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2595 nBytesSent += nRC;
2598 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2600 if (lpfnStatusCB)
2602 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2603 &nBytesSent, sizeof(DWORD));
2606 TRACE("Sent %d bytes\n", nBytesSent);
2607 return (nRC != -1);
2610 /***********************************************************************
2611 * FTP_SendCommand (internal)
2613 * Send command to server
2615 * RETURNS
2616 * TRUE on success
2617 * NULL on failure
2620 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2621 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2623 BOOL ret;
2624 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2625 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2626 HeapFree(GetProcessHeap(), 0, lpszParamA);
2627 return ret;
2630 /***********************************************************************
2631 * FTP_ReceiveResponse (internal)
2633 * Receive response from server
2635 * RETURNS
2636 * Reply code on success
2637 * 0 on failure
2640 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2642 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2643 DWORD nRecv;
2644 INT rc = 0;
2645 char firstprefix[5];
2646 BOOL multiline = FALSE;
2648 TRACE("socket(%d)\n", lpwfs->sndSocket);
2650 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2652 while(1)
2654 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2655 goto lerror;
2657 if (nRecv >= 3)
2659 if(!multiline)
2661 if(lpszResponse[3] != '-')
2662 break;
2663 else
2664 { /* Start of multiline response. Loop until we get "nnn " */
2665 multiline = TRUE;
2666 memcpy(firstprefix, lpszResponse, 3);
2667 firstprefix[3] = ' ';
2668 firstprefix[4] = '\0';
2671 else
2673 if(!memcmp(firstprefix, lpszResponse, 4))
2674 break;
2679 if (nRecv >= 3)
2681 rc = atoi(lpszResponse);
2683 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2684 &nRecv, sizeof(DWORD));
2687 lerror:
2688 TRACE("return %d\n", rc);
2689 return rc;
2693 /***********************************************************************
2694 * FTP_SendPassword (internal)
2696 * Send password to ftp server
2698 * RETURNS
2699 * TRUE on success
2700 * NULL on failure
2703 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2705 INT nResCode;
2706 BOOL bSuccess = FALSE;
2708 TRACE("\n");
2709 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2710 goto lend;
2712 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2713 if (nResCode)
2715 TRACE("Received reply code %d\n", nResCode);
2716 /* Login successful... */
2717 if (nResCode == 230)
2718 bSuccess = TRUE;
2719 /* Command not implemented, superfluous at the server site... */
2720 /* Need account for login... */
2721 else if (nResCode == 332)
2722 bSuccess = FTP_SendAccount(lpwfs);
2723 else
2724 FTP_SetResponseError(nResCode);
2727 lend:
2728 TRACE("Returning %d\n", bSuccess);
2729 return bSuccess;
2733 /***********************************************************************
2734 * FTP_SendAccount (internal)
2738 * RETURNS
2739 * TRUE on success
2740 * FALSE on failure
2743 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2745 INT nResCode;
2746 BOOL bSuccess = FALSE;
2748 TRACE("\n");
2749 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2750 goto lend;
2752 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2753 if (nResCode)
2754 bSuccess = TRUE;
2755 else
2756 FTP_SetResponseError(nResCode);
2758 lend:
2759 return bSuccess;
2763 /***********************************************************************
2764 * FTP_SendStore (internal)
2766 * Send request to upload file to ftp server
2768 * RETURNS
2769 * TRUE on success
2770 * FALSE on failure
2773 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2775 INT nResCode;
2776 BOOL bSuccess = FALSE;
2778 TRACE("\n");
2779 if (!FTP_InitListenSocket(lpwfs))
2780 goto lend;
2782 if (!FTP_SendType(lpwfs, dwType))
2783 goto lend;
2785 if (!FTP_SendPortOrPasv(lpwfs))
2786 goto lend;
2788 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2789 goto lend;
2790 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2791 if (nResCode)
2793 if (nResCode == 150 || nResCode == 125)
2794 bSuccess = TRUE;
2795 else
2796 FTP_SetResponseError(nResCode);
2799 lend:
2800 if (!bSuccess && lpwfs->lstnSocket != -1)
2802 closesocket(lpwfs->lstnSocket);
2803 lpwfs->lstnSocket = -1;
2806 return bSuccess;
2810 /***********************************************************************
2811 * FTP_InitListenSocket (internal)
2813 * Create a socket to listen for server response
2815 * RETURNS
2816 * TRUE on success
2817 * FALSE on failure
2820 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2822 BOOL bSuccess = FALSE;
2823 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2825 TRACE("\n");
2827 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2828 if (lpwfs->lstnSocket == -1)
2830 TRACE("Unable to create listening socket\n");
2831 goto lend;
2834 /* We obtain our ip addr from the name of the command channel socket */
2835 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2837 /* and get the system to assign us a port */
2838 lpwfs->lstnSocketAddress.sin_port = htons(0);
2840 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2842 TRACE("Unable to bind socket\n");
2843 goto lend;
2846 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2848 TRACE("listen failed\n");
2849 goto lend;
2852 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2853 bSuccess = TRUE;
2855 lend:
2856 if (!bSuccess && lpwfs->lstnSocket != -1)
2858 closesocket(lpwfs->lstnSocket);
2859 lpwfs->lstnSocket = -1;
2862 return bSuccess;
2866 /***********************************************************************
2867 * FTP_SendType (internal)
2869 * Tell server type of data being transferred
2871 * RETURNS
2872 * TRUE on success
2873 * FALSE on failure
2875 * W98SE doesn't cache the type that's currently set
2876 * (i.e. it sends it always),
2877 * so we probably don't want to do that either.
2879 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2881 INT nResCode;
2882 WCHAR type[] = { 'I','\0' };
2883 BOOL bSuccess = FALSE;
2885 TRACE("\n");
2886 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2887 type[0] = 'A';
2889 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2890 goto lend;
2892 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2893 if (nResCode)
2895 if (nResCode == 2)
2896 bSuccess = TRUE;
2897 else
2898 FTP_SetResponseError(nResCode);
2901 lend:
2902 return bSuccess;
2906 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2907 /***********************************************************************
2908 * FTP_GetFileSize (internal)
2910 * Retrieves from the server the size of the given file
2912 * RETURNS
2913 * TRUE on success
2914 * FALSE on failure
2917 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2919 INT nResCode;
2920 BOOL bSuccess = FALSE;
2922 TRACE("\n");
2924 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2925 goto lend;
2927 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2928 if (nResCode)
2930 if (nResCode == 213) {
2931 /* Now parses the output to get the actual file size */
2932 int i;
2933 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2935 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2936 if (lpszResponseBuffer[i] == '\0') return FALSE;
2937 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2939 bSuccess = TRUE;
2940 } else {
2941 FTP_SetResponseError(nResCode);
2945 lend:
2946 return bSuccess;
2948 #endif
2951 /***********************************************************************
2952 * FTP_SendPort (internal)
2954 * Tell server which port to use
2956 * RETURNS
2957 * TRUE on success
2958 * FALSE on failure
2961 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
2963 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2964 INT nResCode;
2965 WCHAR szIPAddress[64];
2966 BOOL bSuccess = FALSE;
2967 TRACE("\n");
2969 sprintfW(szIPAddress, szIPFormat,
2970 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2971 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2972 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2973 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2974 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2975 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2977 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2978 goto lend;
2980 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2981 if (nResCode)
2983 if (nResCode == 200)
2984 bSuccess = TRUE;
2985 else
2986 FTP_SetResponseError(nResCode);
2989 lend:
2990 return bSuccess;
2994 /***********************************************************************
2995 * FTP_DoPassive (internal)
2997 * Tell server that we want to do passive transfers
2998 * and connect data socket
3000 * RETURNS
3001 * TRUE on success
3002 * FALSE on failure
3005 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3007 INT nResCode;
3008 BOOL bSuccess = FALSE;
3010 TRACE("\n");
3011 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3012 goto lend;
3014 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3015 if (nResCode)
3017 if (nResCode == 227)
3019 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3020 LPSTR p;
3021 int f[6];
3022 int i;
3023 char *pAddr, *pPort;
3024 INT nsocket = -1;
3025 struct sockaddr_in dataSocketAddress;
3027 p = lpszResponseBuffer+4; /* skip status code */
3028 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3030 if (*p == '\0')
3032 ERR("no address found in response, aborting\n");
3033 goto lend;
3036 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3037 &f[4], &f[5]) != 6)
3039 ERR("unknown response address format '%s', aborting\n", p);
3040 goto lend;
3042 for (i=0; i < 6; i++)
3043 f[i] = f[i] & 0xff;
3045 dataSocketAddress = lpwfs->socketAddress;
3046 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3047 pPort = (char *)&(dataSocketAddress.sin_port);
3048 pAddr[0] = f[0];
3049 pAddr[1] = f[1];
3050 pAddr[2] = f[2];
3051 pAddr[3] = f[3];
3052 pPort[0] = f[4];
3053 pPort[1] = f[5];
3055 nsocket = socket(AF_INET,SOCK_STREAM,0);
3056 if (nsocket == -1)
3057 goto lend;
3059 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3061 ERR("can't connect passive FTP data port.\n");
3062 closesocket(nsocket);
3063 goto lend;
3065 lpwfs->pasvSocket = nsocket;
3066 bSuccess = TRUE;
3068 else
3069 FTP_SetResponseError(nResCode);
3072 lend:
3073 return bSuccess;
3077 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3079 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3081 if (!FTP_DoPassive(lpwfs))
3082 return FALSE;
3084 else
3086 if (!FTP_SendPort(lpwfs))
3087 return FALSE;
3089 return TRUE;
3093 /***********************************************************************
3094 * FTP_GetDataSocket (internal)
3096 * Either accepts an incoming data socket connection from the server
3097 * or just returns the already opened socket after a PASV command
3098 * in case of passive FTP.
3101 * RETURNS
3102 * TRUE on success
3103 * FALSE on failure
3106 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3108 struct sockaddr_in saddr;
3109 socklen_t addrlen = sizeof(struct sockaddr);
3111 TRACE("\n");
3112 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3114 *nDataSocket = lpwfs->pasvSocket;
3115 lpwfs->pasvSocket = -1;
3117 else
3119 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3120 closesocket(lpwfs->lstnSocket);
3121 lpwfs->lstnSocket = -1;
3123 return *nDataSocket != -1;
3127 /***********************************************************************
3128 * FTP_SendData (internal)
3130 * Send data to the server
3132 * RETURNS
3133 * TRUE on success
3134 * FALSE on failure
3137 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3139 BY_HANDLE_FILE_INFORMATION fi;
3140 DWORD nBytesRead = 0;
3141 DWORD nBytesSent = 0;
3142 DWORD nTotalSent = 0;
3143 DWORD nBytesToSend, nLen;
3144 int nRC = 1;
3145 time_t s_long_time, e_long_time;
3146 LONG nSeconds;
3147 CHAR *lpszBuffer;
3149 TRACE("\n");
3150 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3152 /* Get the size of the file. */
3153 GetFileInformationByHandle(hFile, &fi);
3154 time(&s_long_time);
3158 nBytesToSend = nBytesRead - nBytesSent;
3160 if (nBytesToSend <= 0)
3162 /* Read data from file. */
3163 nBytesSent = 0;
3164 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3165 ERR("Failed reading from file\n");
3167 if (nBytesRead > 0)
3168 nBytesToSend = nBytesRead;
3169 else
3170 break;
3173 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3174 DATA_PACKET_SIZE : nBytesToSend;
3175 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3177 if (nRC != -1)
3179 nBytesSent += nRC;
3180 nTotalSent += nRC;
3183 /* Do some computation to display the status. */
3184 time(&e_long_time);
3185 nSeconds = e_long_time - s_long_time;
3186 if( nSeconds / 60 > 0 )
3188 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3189 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3190 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3192 else
3194 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3195 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3196 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3198 } while (nRC != -1);
3200 TRACE("file transfer complete!\n");
3202 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3204 return nTotalSent;
3208 /***********************************************************************
3209 * FTP_SendRetrieve (internal)
3211 * Send request to retrieve a file
3213 * RETURNS
3214 * Number of bytes to be received on success
3215 * 0 on failure
3218 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3220 INT nResCode;
3221 BOOL ret;
3223 TRACE("\n");
3224 if (!(ret = FTP_InitListenSocket(lpwfs)))
3225 goto lend;
3227 if (!(ret = FTP_SendType(lpwfs, dwType)))
3228 goto lend;
3230 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3231 goto lend;
3233 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3234 goto lend;
3236 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3237 if ((nResCode != 125) && (nResCode != 150)) {
3238 /* That means that we got an error getting the file. */
3239 FTP_SetResponseError(nResCode);
3240 ret = FALSE;
3243 lend:
3244 if (!ret && lpwfs->lstnSocket != -1)
3246 closesocket(lpwfs->lstnSocket);
3247 lpwfs->lstnSocket = -1;
3250 return ret;
3254 /***********************************************************************
3255 * FTP_RetrieveData (internal)
3257 * Retrieve data from server
3259 * RETURNS
3260 * TRUE on success
3261 * FALSE on failure
3264 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3266 DWORD nBytesWritten;
3267 INT nRC = 0;
3268 CHAR *lpszBuffer;
3270 TRACE("\n");
3272 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3273 if (NULL == lpszBuffer)
3275 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3276 return FALSE;
3279 while (nRC != -1)
3281 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3282 if (nRC != -1)
3284 /* other side closed socket. */
3285 if (nRC == 0)
3286 goto recv_end;
3287 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3291 TRACE("Data transfer complete\n");
3293 recv_end:
3294 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3296 return (nRC != -1);
3299 /***********************************************************************
3300 * FTPFINDNEXT_Destroy (internal)
3302 * Deallocate session handle
3304 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3306 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3307 DWORD i;
3309 TRACE("\n");
3311 WININET_Release(&lpwfn->lpFtpSession->hdr);
3313 for (i = 0; i < lpwfn->size; i++)
3315 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3318 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3319 HeapFree(GetProcessHeap(), 0, lpwfn);
3322 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3324 WIN32_FIND_DATAW *find_data = data;
3325 DWORD res = ERROR_SUCCESS;
3327 TRACE("index(%d) size(%d)\n", find->index, find->size);
3329 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3331 if (find->index < find->size) {
3332 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3333 find->index++;
3335 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3336 }else {
3337 res = ERROR_NO_MORE_FILES;
3340 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3342 INTERNET_ASYNC_RESULT iar;
3344 iar.dwResult = (res == ERROR_SUCCESS);
3345 iar.dwError = res;
3347 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3348 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3349 sizeof(INTERNET_ASYNC_RESULT));
3352 return res;
3355 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3357 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3359 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3362 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3364 switch(option) {
3365 case INTERNET_OPTION_HANDLE_TYPE:
3366 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3368 if (*size < sizeof(ULONG))
3369 return ERROR_INSUFFICIENT_BUFFER;
3371 *size = sizeof(DWORD);
3372 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3373 return ERROR_SUCCESS;
3376 return INET_QueryOption(option, buffer, size, unicode);
3379 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3381 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3383 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3385 WORKREQUEST workRequest;
3386 struct WORKREQ_FTPFINDNEXTW *req;
3388 workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3389 workRequest.hdr = WININET_AddRef( &find->hdr );
3390 req = &workRequest.u.FtpFindNextW;
3391 req->lpFindFileData = data;
3393 INTERNET_AsyncCall(&workRequest);
3395 return ERROR_SUCCESS;
3398 return FTPFINDNEXT_FindNextFileProc(find, data);
3401 static const object_vtbl_t FTPFINDNEXTVtbl = {
3402 FTPFINDNEXT_Destroy,
3403 NULL,
3404 FTPFINDNEXT_QueryOption,
3405 NULL,
3406 NULL,
3407 NULL,
3408 NULL,
3409 NULL,
3410 NULL,
3411 FTPFINDNEXT_FindNextFileW
3414 /***********************************************************************
3415 * FTP_ReceiveFileList (internal)
3417 * Read file list from server
3419 * RETURNS
3420 * Handle to file list on success
3421 * NULL on failure
3424 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3425 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3427 DWORD dwSize = 0;
3428 LPFILEPROPERTIESW lpafp = NULL;
3429 LPWININETFTPFINDNEXTW lpwfn = NULL;
3430 HINTERNET handle = 0;
3432 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3434 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3436 if(lpFindFileData)
3437 FTP_ConvertFileProp(lpafp, lpFindFileData);
3439 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3440 if (lpwfn)
3442 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3443 lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3444 lpwfn->hdr.dwContext = dwContext;
3445 lpwfn->hdr.refs = 1;
3446 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3447 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3448 lpwfn->size = dwSize;
3449 lpwfn->lpafp = lpafp;
3451 WININET_AddRef( &lpwfs->hdr );
3452 lpwfn->lpFtpSession = lpwfs;
3453 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3455 handle = WININET_AllocHandle( &lpwfn->hdr );
3459 if( lpwfn )
3460 WININET_Release( &lpwfn->hdr );
3462 TRACE("Matched %d files\n", dwSize);
3463 return handle;
3467 /***********************************************************************
3468 * FTP_ConvertFileProp (internal)
3470 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3472 * RETURNS
3473 * TRUE on success
3474 * FALSE on failure
3477 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3479 BOOL bSuccess = FALSE;
3481 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3483 if (lpafp)
3485 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3486 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3487 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3489 /* Not all fields are filled in */
3490 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3491 lpFindFileData->nFileSizeLow = lpafp->nSize;
3493 if (lpafp->bIsDirectory)
3494 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3496 if (lpafp->lpszName)
3497 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3499 bSuccess = TRUE;
3502 return bSuccess;
3505 /***********************************************************************
3506 * FTP_ParseNextFile (internal)
3508 * Parse the next line in file listing
3510 * RETURNS
3511 * TRUE on success
3512 * FALSE on failure
3514 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3516 static const char szSpace[] = " \t";
3517 DWORD nBufLen;
3518 char *pszLine;
3519 char *pszToken;
3520 char *pszTmp;
3521 BOOL found = FALSE;
3522 int i;
3524 lpfp->lpszName = NULL;
3525 do {
3526 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3527 return FALSE;
3529 pszToken = strtok(pszLine, szSpace);
3530 /* ls format
3531 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3533 * For instance:
3534 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3536 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3537 if(!FTP_ParsePermission(pszToken, lpfp))
3538 lpfp->bIsDirectory = FALSE;
3539 for(i=0; i<=3; i++) {
3540 if(!(pszToken = strtok(NULL, szSpace)))
3541 break;
3543 if(!pszToken) continue;
3544 if(lpfp->bIsDirectory) {
3545 TRACE("Is directory\n");
3546 lpfp->nSize = 0;
3548 else {
3549 TRACE("Size: %s\n", pszToken);
3550 lpfp->nSize = atol(pszToken);
3553 lpfp->tmLastModified.wSecond = 0;
3554 lpfp->tmLastModified.wMinute = 0;
3555 lpfp->tmLastModified.wHour = 0;
3556 lpfp->tmLastModified.wDay = 0;
3557 lpfp->tmLastModified.wMonth = 0;
3558 lpfp->tmLastModified.wYear = 0;
3560 /* Determine month */
3561 pszToken = strtok(NULL, szSpace);
3562 if(!pszToken) continue;
3563 if(strlen(pszToken) >= 3) {
3564 pszToken[3] = 0;
3565 if((pszTmp = StrStrIA(szMonths, pszToken)))
3566 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3568 /* Determine day */
3569 pszToken = strtok(NULL, szSpace);
3570 if(!pszToken) continue;
3571 lpfp->tmLastModified.wDay = atoi(pszToken);
3572 /* Determine time or year */
3573 pszToken = strtok(NULL, szSpace);
3574 if(!pszToken) continue;
3575 if((pszTmp = strchr(pszToken, ':'))) {
3576 SYSTEMTIME curr_time;
3577 *pszTmp = 0;
3578 pszTmp++;
3579 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3580 lpfp->tmLastModified.wHour = atoi(pszToken);
3581 GetLocalTime( &curr_time );
3582 lpfp->tmLastModified.wYear = curr_time.wYear;
3584 else {
3585 lpfp->tmLastModified.wYear = atoi(pszToken);
3586 lpfp->tmLastModified.wHour = 12;
3588 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3589 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3590 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3592 pszToken = strtok(NULL, szSpace);
3593 if(!pszToken) continue;
3594 lpfp->lpszName = heap_strdupAtoW(pszToken);
3595 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3597 /* NT way of parsing ... :
3599 07-13-03 08:55PM <DIR> sakpatch
3600 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3602 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3603 int mon, mday, year, hour, min;
3604 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3606 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3607 lpfp->tmLastModified.wDay = mday;
3608 lpfp->tmLastModified.wMonth = mon;
3609 lpfp->tmLastModified.wYear = year;
3611 /* Hacky and bad Y2K protection :-) */
3612 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3614 pszToken = strtok(NULL, szSpace);
3615 if(!pszToken) continue;
3616 sscanf(pszToken, "%d:%d", &hour, &min);
3617 lpfp->tmLastModified.wHour = hour;
3618 lpfp->tmLastModified.wMinute = min;
3619 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3620 lpfp->tmLastModified.wHour += 12;
3622 lpfp->tmLastModified.wSecond = 0;
3624 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3625 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3626 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3628 pszToken = strtok(NULL, szSpace);
3629 if(!pszToken) continue;
3630 if(!strcasecmp(pszToken, "<DIR>")) {
3631 lpfp->bIsDirectory = TRUE;
3632 lpfp->nSize = 0;
3633 TRACE("Is directory\n");
3635 else {
3636 lpfp->bIsDirectory = FALSE;
3637 lpfp->nSize = atol(pszToken);
3638 TRACE("Size: %d\n", lpfp->nSize);
3641 pszToken = strtok(NULL, szSpace);
3642 if(!pszToken) continue;
3643 lpfp->lpszName = heap_strdupAtoW(pszToken);
3644 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3646 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3647 else if(pszToken[0] == '+') {
3648 FIXME("EPLF Format not implemented\n");
3651 if(lpfp->lpszName) {
3652 if((lpszSearchFile == NULL) ||
3653 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3654 found = TRUE;
3655 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3657 else {
3658 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3659 lpfp->lpszName = NULL;
3662 } while(!found);
3663 return TRUE;
3666 /***********************************************************************
3667 * FTP_ParseDirectory (internal)
3669 * Parse string of directory information
3671 * RETURNS
3672 * TRUE on success
3673 * FALSE on failure
3675 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3676 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3678 BOOL bSuccess = TRUE;
3679 INT sizeFilePropArray = 500;/*20; */
3680 INT indexFilePropArray = -1;
3682 TRACE("\n");
3684 /* Allocate initial file properties array */
3685 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3686 if (!*lpafp)
3687 return FALSE;
3689 do {
3690 if (indexFilePropArray+1 >= sizeFilePropArray)
3692 LPFILEPROPERTIESW tmpafp;
3694 sizeFilePropArray *= 2;
3695 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3696 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3697 if (NULL == tmpafp)
3699 bSuccess = FALSE;
3700 break;
3703 *lpafp = tmpafp;
3705 indexFilePropArray++;
3706 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3708 if (bSuccess && indexFilePropArray)
3710 if (indexFilePropArray < sizeFilePropArray - 1)
3712 LPFILEPROPERTIESW tmpafp;
3714 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3715 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3716 if (NULL != tmpafp)
3717 *lpafp = tmpafp;
3719 *dwfp = indexFilePropArray;
3721 else
3723 HeapFree(GetProcessHeap(), 0, *lpafp);
3724 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3725 bSuccess = FALSE;
3728 return bSuccess;
3732 /***********************************************************************
3733 * FTP_ParsePermission (internal)
3735 * Parse permission string of directory information
3737 * RETURNS
3738 * TRUE on success
3739 * FALSE on failure
3742 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3744 BOOL bSuccess = TRUE;
3745 unsigned short nPermission = 0;
3746 INT nPos = 1;
3747 INT nLast = 9;
3749 TRACE("\n");
3750 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3752 bSuccess = FALSE;
3753 return bSuccess;
3756 lpfp->bIsDirectory = (*lpszPermission == 'd');
3759 switch (nPos)
3761 case 1:
3762 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3763 break;
3764 case 2:
3765 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3766 break;
3767 case 3:
3768 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3769 break;
3770 case 4:
3771 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3772 break;
3773 case 5:
3774 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3775 break;
3776 case 6:
3777 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3778 break;
3779 case 7:
3780 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3781 break;
3782 case 8:
3783 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3784 break;
3785 case 9:
3786 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3787 break;
3789 nPos++;
3790 }while (nPos <= nLast);
3792 lpfp->permissions = nPermission;
3793 return bSuccess;
3797 /***********************************************************************
3798 * FTP_SetResponseError (internal)
3800 * Set the appropriate error code for a given response from the server
3802 * RETURNS
3805 static DWORD FTP_SetResponseError(DWORD dwResponse)
3807 DWORD dwCode = 0;
3809 switch(dwResponse)
3811 case 425: /* Cannot open data connection. */
3812 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3813 break;
3815 case 426: /* Connection closed, transer aborted. */
3816 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3817 break;
3819 case 530: /* Not logged in. Login incorrect. */
3820 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3821 break;
3823 case 421: /* Service not available - Server may be shutting down. */
3824 case 450: /* File action not taken. File may be busy. */
3825 case 451: /* Action aborted. Server error. */
3826 case 452: /* Action not taken. Insufficient storage space on server. */
3827 case 500: /* Syntax error. Command unrecognized. */
3828 case 501: /* Syntax error. Error in parameters or arguments. */
3829 case 502: /* Command not implemented. */
3830 case 503: /* Bad sequence of commands. */
3831 case 504: /* Command not implemented for that parameter. */
3832 case 532: /* Need account for storing files */
3833 case 550: /* File action not taken. File not found or no access. */
3834 case 551: /* Requested action aborted. Page type unknown */
3835 case 552: /* Action aborted. Exceeded storage allocation */
3836 case 553: /* Action not taken. File name not allowed. */
3838 default:
3839 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3840 break;
3843 INTERNET_SetLastError(dwCode);
3844 return dwCode;