qmgr: Implement IEnumBackgroundCopyFiles_Next.
[wine.git] / dlls / wininet / ftp.c
blob76801c82c16050743282972cca64a5f5ed485fbd
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 #include <errno.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #ifdef HAVE_SYS_SOCKET_H
40 # include <sys/socket.h>
41 #endif
42 #ifdef HAVE_UNISTD_H
43 # include <unistd.h>
44 #endif
45 #include <time.h>
46 #include <assert.h>
48 #include "windef.h"
49 #include "winbase.h"
50 #include "wingdi.h"
51 #include "winuser.h"
52 #include "wininet.h"
53 #include "winnls.h"
54 #include "winerror.h"
55 #include "winreg.h"
56 #include "winternl.h"
57 #include "shlwapi.h"
59 #include "wine/debug.h"
60 #include "internet.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
64 typedef struct
66 BOOL bIsDirectory;
67 LPWSTR lpszName;
68 DWORD nSize;
69 struct tm tmLastModified;
70 unsigned short permissions;
71 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
73 typedef struct
75 WININETHANDLEHEADER hdr;
76 WININETFTPSESSIONW *lpFtpSession;
77 DWORD index;
78 DWORD size;
79 LPFILEPROPERTIESW lpafp;
80 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
82 #define DATA_PACKET_SIZE 0x2000
83 #define szCRLF "\r\n"
84 #define MAX_BACKLOG 5
86 /* Testing shows that Windows only accepts dwFlags where the last
87 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
89 #define FTP_CONDITION_MASK 0x0007
91 typedef enum {
92 /* FTP commands with arguments. */
93 FTP_CMD_ACCT,
94 FTP_CMD_CWD,
95 FTP_CMD_DELE,
96 FTP_CMD_MKD,
97 FTP_CMD_PASS,
98 FTP_CMD_PORT,
99 FTP_CMD_RETR,
100 FTP_CMD_RMD,
101 FTP_CMD_RNFR,
102 FTP_CMD_RNTO,
103 FTP_CMD_STOR,
104 FTP_CMD_TYPE,
105 FTP_CMD_USER,
106 FTP_CMD_SIZE,
108 /* FTP commands without arguments. */
109 FTP_CMD_ABOR,
110 FTP_CMD_LIST,
111 FTP_CMD_NLST,
112 FTP_CMD_PASV,
113 FTP_CMD_PWD,
114 FTP_CMD_QUIT,
115 } FTP_COMMAND;
117 static const CHAR *const szFtpCommands[] = {
118 "ACCT",
119 "CWD",
120 "DELE",
121 "MKD",
122 "PASS",
123 "PORT",
124 "RETR",
125 "RMD",
126 "RNFR",
127 "RNTO",
128 "STOR",
129 "TYPE",
130 "USER",
131 "SIZE",
132 "ABOR",
133 "LIST",
134 "NLST",
135 "PASV",
136 "PWD",
137 "QUIT",
140 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
141 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
143 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
144 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext);
145 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
146 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket);
147 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile);
148 static INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD_PTR dwContext);
149 static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
150 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile);
151 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs);
152 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs);
153 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs);
154 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs);
155 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType);
156 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs);
157 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs);
158 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs);
159 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
160 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
161 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
162 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
163 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
164 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
165 static DWORD FTP_SetResponseError(DWORD dwResponse);
166 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
168 /***********************************************************************
169 * FtpPutFileA (WININET.@)
171 * Uploads a file to the FTP server
173 * RETURNS
174 * TRUE on success
175 * FALSE on failure
178 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
179 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
181 LPWSTR lpwzLocalFile;
182 LPWSTR lpwzNewRemoteFile;
183 BOOL ret;
185 lpwzLocalFile = lpszLocalFile?WININET_strdup_AtoW(lpszLocalFile):NULL;
186 lpwzNewRemoteFile = lpszNewRemoteFile?WININET_strdup_AtoW(lpszNewRemoteFile):NULL;
187 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
188 dwFlags, dwContext);
189 HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
190 HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
191 return ret;
194 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
196 struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
197 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
199 TRACE("%p\n", lpwfs);
201 FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
202 req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
204 HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
205 HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
208 /***********************************************************************
209 * FtpPutFileW (WININET.@)
211 * Uploads a file to the FTP server
213 * RETURNS
214 * TRUE on success
215 * FALSE on failure
218 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
219 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
221 LPWININETFTPSESSIONW lpwfs;
222 LPWININETAPPINFOW hIC = NULL;
223 BOOL r = FALSE;
225 if (!lpszLocalFile || !lpszNewRemoteFile)
227 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
228 return FALSE;
231 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
232 if (!lpwfs)
234 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
235 return FALSE;
238 if (WH_HFTPSESSION != lpwfs->hdr.htype)
240 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
241 goto lend;
244 if (lpwfs->download_in_progress != NULL)
246 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
247 goto lend;
250 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
252 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
253 goto lend;
256 hIC = lpwfs->lpAppInfo;
257 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
259 WORKREQUEST workRequest;
260 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
262 workRequest.asyncproc = AsyncFtpPutFileProc;
263 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
264 req->lpszLocalFile = WININET_strdupW(lpszLocalFile);
265 req->lpszNewRemoteFile = WININET_strdupW(lpszNewRemoteFile);
266 req->dwFlags = dwFlags;
267 req->dwContext = dwContext;
269 r = INTERNET_AsyncCall(&workRequest);
271 else
273 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
274 lpszNewRemoteFile, dwFlags, dwContext);
277 lend:
278 WININET_Release( &lpwfs->hdr );
280 return r;
283 /***********************************************************************
284 * FTP_FtpPutFileW (Internal)
286 * Uploads a file to the FTP server
288 * RETURNS
289 * TRUE on success
290 * FALSE on failure
293 BOOL WINAPI FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
294 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
296 HANDLE hFile;
297 BOOL bSuccess = FALSE;
298 LPWININETAPPINFOW hIC = NULL;
299 INT nResCode;
301 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
303 /* Clear any error information */
304 INTERNET_SetLastError(0);
306 /* Open file to be uploaded */
307 if (INVALID_HANDLE_VALUE ==
308 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
309 /* Let CreateFile set the appropriate error */
310 return FALSE;
312 hIC = lpwfs->lpAppInfo;
314 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
316 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
318 INT nDataSocket;
320 /* Get data socket to server */
321 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
323 FTP_SendData(lpwfs, nDataSocket, hFile);
324 closesocket(nDataSocket);
325 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
326 if (nResCode)
328 if (nResCode == 226)
329 bSuccess = TRUE;
330 else
331 FTP_SetResponseError(nResCode);
336 if (lpwfs->lstnSocket != -1)
337 closesocket(lpwfs->lstnSocket);
339 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
341 INTERNET_ASYNC_RESULT iar;
343 iar.dwResult = (DWORD)bSuccess;
344 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
345 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
346 &iar, sizeof(INTERNET_ASYNC_RESULT));
349 CloseHandle(hFile);
351 return bSuccess;
355 /***********************************************************************
356 * FtpSetCurrentDirectoryA (WININET.@)
358 * Change the working directory on the FTP server
360 * RETURNS
361 * TRUE on success
362 * FALSE on failure
365 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
367 LPWSTR lpwzDirectory;
368 BOOL ret;
370 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
371 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
372 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
373 return ret;
377 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
379 struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
380 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
382 TRACE("%p\n", lpwfs);
384 FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
385 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
388 /***********************************************************************
389 * FtpSetCurrentDirectoryW (WININET.@)
391 * Change the working directory on the FTP server
393 * RETURNS
394 * TRUE on success
395 * FALSE on failure
398 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
400 LPWININETFTPSESSIONW lpwfs = NULL;
401 LPWININETAPPINFOW hIC = NULL;
402 BOOL r = FALSE;
404 if (!lpszDirectory)
406 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
407 goto lend;
410 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
411 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
413 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
414 goto lend;
417 if (lpwfs->download_in_progress != NULL)
419 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
420 goto lend;
423 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
425 hIC = lpwfs->lpAppInfo;
426 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
428 WORKREQUEST workRequest;
429 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
431 workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
432 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
433 req = &workRequest.u.FtpSetCurrentDirectoryW;
434 req->lpszDirectory = WININET_strdupW(lpszDirectory);
436 r = INTERNET_AsyncCall(&workRequest);
438 else
440 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
443 lend:
444 if( lpwfs )
445 WININET_Release( &lpwfs->hdr );
447 return r;
451 /***********************************************************************
452 * FTP_FtpSetCurrentDirectoryW (Internal)
454 * Change the working directory on the FTP server
456 * RETURNS
457 * TRUE on success
458 * FALSE on failure
461 BOOL WINAPI FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
463 INT nResCode;
464 LPWININETAPPINFOW hIC = NULL;
465 DWORD bSuccess = FALSE;
467 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
469 /* Clear any error information */
470 INTERNET_SetLastError(0);
472 hIC = lpwfs->lpAppInfo;
473 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
474 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
475 goto lend;
477 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
479 if (nResCode)
481 if (nResCode == 250)
482 bSuccess = TRUE;
483 else
484 FTP_SetResponseError(nResCode);
487 lend:
488 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
490 INTERNET_ASYNC_RESULT iar;
492 iar.dwResult = bSuccess;
493 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
494 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
495 &iar, sizeof(INTERNET_ASYNC_RESULT));
497 return bSuccess;
501 /***********************************************************************
502 * FtpCreateDirectoryA (WININET.@)
504 * Create new directory on the FTP server
506 * RETURNS
507 * TRUE on success
508 * FALSE on failure
511 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
513 LPWSTR lpwzDirectory;
514 BOOL ret;
516 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
517 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
518 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
519 return ret;
523 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
525 struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
526 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
528 TRACE(" %p\n", lpwfs);
530 FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
531 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
534 /***********************************************************************
535 * FtpCreateDirectoryW (WININET.@)
537 * Create new directory on the FTP server
539 * RETURNS
540 * TRUE on success
541 * FALSE on failure
544 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
546 LPWININETFTPSESSIONW lpwfs;
547 LPWININETAPPINFOW hIC = NULL;
548 BOOL r = FALSE;
550 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
551 if (!lpwfs)
553 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
554 return FALSE;
557 if (WH_HFTPSESSION != lpwfs->hdr.htype)
559 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
560 goto lend;
563 if (lpwfs->download_in_progress != NULL)
565 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
566 goto lend;
569 if (!lpszDirectory)
571 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
572 goto lend;
575 hIC = lpwfs->lpAppInfo;
576 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
578 WORKREQUEST workRequest;
579 struct WORKREQ_FTPCREATEDIRECTORYW *req;
581 workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
582 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
583 req = &workRequest.u.FtpCreateDirectoryW;
584 req->lpszDirectory = WININET_strdupW(lpszDirectory);
586 r = INTERNET_AsyncCall(&workRequest);
588 else
590 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
592 lend:
593 WININET_Release( &lpwfs->hdr );
595 return r;
599 /***********************************************************************
600 * FTP_FtpCreateDirectoryW (Internal)
602 * Create new directory on the FTP server
604 * RETURNS
605 * TRUE on success
606 * FALSE on failure
609 BOOL WINAPI FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
611 INT nResCode;
612 BOOL bSuccess = FALSE;
613 LPWININETAPPINFOW hIC = NULL;
615 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
617 /* Clear any error information */
618 INTERNET_SetLastError(0);
620 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
621 goto lend;
623 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
624 if (nResCode)
626 if (nResCode == 257)
627 bSuccess = TRUE;
628 else
629 FTP_SetResponseError(nResCode);
632 lend:
633 hIC = lpwfs->lpAppInfo;
634 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
636 INTERNET_ASYNC_RESULT iar;
638 iar.dwResult = (DWORD)bSuccess;
639 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
640 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
641 &iar, sizeof(INTERNET_ASYNC_RESULT));
644 return bSuccess;
647 /***********************************************************************
648 * FtpFindFirstFileA (WININET.@)
650 * Search the specified directory
652 * RETURNS
653 * HINTERNET on success
654 * NULL on failure
657 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
658 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
660 LPWSTR lpwzSearchFile;
661 WIN32_FIND_DATAW wfd;
662 LPWIN32_FIND_DATAW lpFindFileDataW;
663 HINTERNET ret;
665 lpwzSearchFile = lpszSearchFile?WININET_strdup_AtoW(lpszSearchFile):NULL;
666 lpFindFileDataW = lpFindFileData?&wfd:NULL;
667 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
668 HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
670 if(lpFindFileData) {
671 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
673 return ret;
677 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
679 struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
680 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
682 TRACE("%p\n", lpwfs);
684 FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
685 req->lpFindFileData, req->dwFlags, req->dwContext);
686 HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
689 /***********************************************************************
690 * FtpFindFirstFileW (WININET.@)
692 * Search the specified directory
694 * RETURNS
695 * HINTERNET on success
696 * NULL on failure
699 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
700 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
702 LPWININETFTPSESSIONW lpwfs;
703 LPWININETAPPINFOW hIC = NULL;
704 HINTERNET r = NULL;
706 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
707 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
709 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
710 goto lend;
713 if (lpwfs->download_in_progress != NULL)
715 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
716 goto lend;
719 hIC = lpwfs->lpAppInfo;
720 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
722 WORKREQUEST workRequest;
723 struct WORKREQ_FTPFINDFIRSTFILEW *req;
725 workRequest.asyncproc = AsyncFtpFindFirstFileProc;
726 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
727 req = &workRequest.u.FtpFindFirstFileW;
728 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : WININET_strdupW(lpszSearchFile);
729 req->lpFindFileData = lpFindFileData;
730 req->dwFlags = dwFlags;
731 req->dwContext= dwContext;
733 INTERNET_AsyncCall(&workRequest);
734 r = NULL;
736 else
738 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
739 dwFlags, dwContext);
741 lend:
742 if( lpwfs )
743 WININET_Release( &lpwfs->hdr );
745 return r;
749 /***********************************************************************
750 * FTP_FtpFindFirstFileW (Internal)
752 * Search the specified directory
754 * RETURNS
755 * HINTERNET on success
756 * NULL on failure
759 HINTERNET WINAPI FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
760 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
762 INT nResCode;
763 LPWININETAPPINFOW hIC = NULL;
764 HINTERNET hFindNext = NULL;
766 TRACE("\n");
768 /* Clear any error information */
769 INTERNET_SetLastError(0);
771 if (!FTP_InitListenSocket(lpwfs))
772 goto lend;
774 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
775 goto lend;
777 if (!FTP_SendPortOrPasv(lpwfs))
778 goto lend;
780 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
781 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
782 goto lend;
784 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
785 if (nResCode)
787 if (nResCode == 125 || nResCode == 150)
789 INT nDataSocket;
791 /* Get data socket to server */
792 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
794 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
795 closesocket(nDataSocket);
796 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
797 if (nResCode != 226 && nResCode != 250)
798 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
801 else
802 FTP_SetResponseError(nResCode);
805 lend:
806 if (lpwfs->lstnSocket != -1)
807 closesocket(lpwfs->lstnSocket);
809 hIC = lpwfs->lpAppInfo;
810 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
812 INTERNET_ASYNC_RESULT iar;
814 if (hFindNext)
816 iar.dwResult = (DWORD)hFindNext;
817 iar.dwError = ERROR_SUCCESS;
818 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
819 &iar, sizeof(INTERNET_ASYNC_RESULT));
822 iar.dwResult = (DWORD)hFindNext;
823 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
824 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
825 &iar, sizeof(INTERNET_ASYNC_RESULT));
828 return hFindNext;
832 /***********************************************************************
833 * FtpGetCurrentDirectoryA (WININET.@)
835 * Retrieves the current directory
837 * RETURNS
838 * TRUE on success
839 * FALSE on failure
842 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
843 LPDWORD lpdwCurrentDirectory)
845 WCHAR *dir = NULL;
846 DWORD len;
847 BOOL ret;
849 if(lpdwCurrentDirectory) {
850 len = *lpdwCurrentDirectory;
851 if(lpszCurrentDirectory)
853 dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
854 if (NULL == dir)
856 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
857 return FALSE;
861 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
863 if (ret && lpszCurrentDirectory)
864 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
866 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
867 HeapFree(GetProcessHeap(), 0, dir);
868 return ret;
872 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
874 struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
875 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
877 TRACE("%p\n", lpwfs);
879 FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
882 /***********************************************************************
883 * FtpGetCurrentDirectoryW (WININET.@)
885 * Retrieves the current directory
887 * RETURNS
888 * TRUE on success
889 * FALSE on failure
892 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
893 LPDWORD lpdwCurrentDirectory)
895 LPWININETFTPSESSIONW lpwfs;
896 LPWININETAPPINFOW hIC = NULL;
897 BOOL r = FALSE;
899 TRACE("len(%d)\n", *lpdwCurrentDirectory);
901 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
902 if (NULL == lpwfs)
904 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
905 goto lend;
908 if (WH_HFTPSESSION != lpwfs->hdr.htype)
910 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
911 goto lend;
914 if (!lpdwCurrentDirectory)
916 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
917 goto lend;
920 if (lpszCurrentDirectory == NULL)
922 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
923 goto lend;
926 if (lpwfs->download_in_progress != NULL)
928 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
929 goto lend;
932 hIC = lpwfs->lpAppInfo;
933 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
935 WORKREQUEST workRequest;
936 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
938 workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
939 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
940 req = &workRequest.u.FtpGetCurrentDirectoryW;
941 req->lpszDirectory = lpszCurrentDirectory;
942 req->lpdwDirectory = lpdwCurrentDirectory;
944 r = INTERNET_AsyncCall(&workRequest);
946 else
948 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
949 lpdwCurrentDirectory);
952 lend:
953 if( lpwfs )
954 WININET_Release( &lpwfs->hdr );
956 return r;
960 /***********************************************************************
961 * FTP_FtpGetCurrentDirectoryW (Internal)
963 * Retrieves the current directory
965 * RETURNS
966 * TRUE on success
967 * FALSE on failure
970 BOOL WINAPI FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
971 LPDWORD lpdwCurrentDirectory)
973 INT nResCode;
974 LPWININETAPPINFOW hIC = NULL;
975 DWORD bSuccess = FALSE;
977 TRACE("len(%d)\n", *lpdwCurrentDirectory);
979 /* Clear any error information */
980 INTERNET_SetLastError(0);
982 hIC = lpwfs->lpAppInfo;
983 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
984 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
985 goto lend;
987 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
988 if (nResCode)
990 if (nResCode == 257) /* Extract directory name */
992 DWORD firstpos, lastpos, len;
993 LPWSTR lpszResponseBuffer = WININET_strdup_AtoW(INTERNET_GetResponseBuffer());
995 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
997 if ('"' == lpszResponseBuffer[lastpos])
999 if (!firstpos)
1000 firstpos = lastpos;
1001 else
1002 break;
1005 len = lastpos - firstpos;
1006 if (*lpdwCurrentDirectory >= len)
1008 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1009 lpszCurrentDirectory[len - 1] = 0;
1010 *lpdwCurrentDirectory = len;
1011 bSuccess = TRUE;
1013 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1015 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
1017 else
1018 FTP_SetResponseError(nResCode);
1021 lend:
1022 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1024 INTERNET_ASYNC_RESULT iar;
1026 iar.dwResult = bSuccess;
1027 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1028 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1029 &iar, sizeof(INTERNET_ASYNC_RESULT));
1032 return bSuccess;
1035 /***********************************************************************
1036 * FtpOpenFileA (WININET.@)
1038 * Open a remote file for writing or reading
1040 * RETURNS
1041 * HINTERNET handle on success
1042 * NULL on failure
1045 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1046 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1047 DWORD_PTR dwContext)
1049 LPWSTR lpwzFileName;
1050 HINTERNET ret;
1052 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1053 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1054 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1055 return ret;
1059 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1061 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1062 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1064 TRACE("%p\n", lpwfs);
1066 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1067 req->dwAccess, req->dwFlags, req->dwContext);
1068 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1071 /***********************************************************************
1072 * FtpOpenFileW (WININET.@)
1074 * Open a remote file for writing or reading
1076 * RETURNS
1077 * HINTERNET handle on success
1078 * NULL on failure
1081 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1082 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1083 DWORD_PTR dwContext)
1085 LPWININETFTPSESSIONW lpwfs;
1086 LPWININETAPPINFOW hIC = NULL;
1087 HINTERNET r = NULL;
1089 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1090 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1092 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1093 if (!lpwfs)
1095 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1096 return FALSE;
1099 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1101 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1102 goto lend;
1105 if ((!lpszFileName) ||
1106 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1107 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1109 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1110 goto lend;
1113 if (lpwfs->download_in_progress != NULL)
1115 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1116 goto lend;
1119 hIC = lpwfs->lpAppInfo;
1120 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1122 WORKREQUEST workRequest;
1123 struct WORKREQ_FTPOPENFILEW *req;
1125 workRequest.asyncproc = AsyncFtpOpenFileProc;
1126 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1127 req = &workRequest.u.FtpOpenFileW;
1128 req->lpszFilename = WININET_strdupW(lpszFileName);
1129 req->dwAccess = fdwAccess;
1130 req->dwFlags = dwFlags;
1131 req->dwContext = dwContext;
1133 INTERNET_AsyncCall(&workRequest);
1134 r = NULL;
1136 else
1138 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1141 lend:
1142 WININET_Release( &lpwfs->hdr );
1144 return r;
1148 /***********************************************************************
1149 * FTPFILE_Destroy(internal)
1151 * Closes the file transfer handle. This also 'cleans' the data queue of
1152 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1155 static void FTPFILE_Destroy(WININETHANDLEHEADER *hdr)
1157 LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
1158 LPWININETFTPSESSIONW lpwfs = lpwh->lpFtpSession;
1159 INT nResCode;
1161 TRACE("\n");
1163 WININET_Release(&lpwh->lpFtpSession->hdr);
1165 if (!lpwh->session_deleted)
1166 lpwfs->download_in_progress = NULL;
1168 if (lpwh->nDataSocket != -1)
1169 closesocket(lpwh->nDataSocket);
1171 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1172 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1174 HeapFree(GetProcessHeap(), 0, lpwh);
1177 static BOOL FTPFILE_WriteFile(WININETHANDLEHEADER *hdr, const void *buffer, DWORD size, DWORD *written)
1179 LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
1180 int res;
1182 res = send(lpwh->nDataSocket, buffer, size, 0);
1184 *written = res>0 ? res : 0;
1185 return res >= 0;
1188 static const HANDLEHEADERVtbl FTPFILEVtbl = {
1189 FTPFILE_Destroy,
1190 NULL,
1191 NULL,
1192 FTPFILE_WriteFile,
1193 NULL
1196 /***********************************************************************
1197 * FTP_FtpOpenFileW (Internal)
1199 * Open a remote file for writing or reading
1201 * RETURNS
1202 * HINTERNET handle on success
1203 * NULL on failure
1206 HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
1207 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1208 DWORD_PTR dwContext)
1210 INT nDataSocket;
1211 BOOL bSuccess = FALSE;
1212 LPWININETFTPFILE lpwh = NULL;
1213 LPWININETAPPINFOW hIC = NULL;
1214 HINTERNET handle = NULL;
1216 TRACE("\n");
1218 /* Clear any error information */
1219 INTERNET_SetLastError(0);
1221 if (GENERIC_READ == fdwAccess)
1223 /* Set up socket to retrieve data */
1224 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1226 else if (GENERIC_WRITE == fdwAccess)
1228 /* Set up socket to send data */
1229 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1232 /* Get data socket to server */
1233 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1235 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPFILE));
1236 lpwh->hdr.htype = WH_HFILE;
1237 lpwh->hdr.vtbl = &FTPFILEVtbl;
1238 lpwh->hdr.dwFlags = dwFlags;
1239 lpwh->hdr.dwContext = dwContext;
1240 lpwh->hdr.dwRefCount = 1;
1241 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1242 lpwh->nDataSocket = nDataSocket;
1243 lpwh->session_deleted = FALSE;
1245 WININET_AddRef( &lpwfs->hdr );
1246 lpwh->lpFtpSession = lpwfs;
1247 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1249 handle = WININET_AllocHandle( &lpwh->hdr );
1250 if( !handle )
1251 goto lend;
1253 /* Indicate that a download is currently in progress */
1254 lpwfs->download_in_progress = lpwh;
1257 if (lpwfs->lstnSocket != -1)
1258 closesocket(lpwfs->lstnSocket);
1260 hIC = lpwfs->lpAppInfo;
1261 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1263 INTERNET_ASYNC_RESULT iar;
1265 if (lpwh)
1267 iar.dwResult = (DWORD)handle;
1268 iar.dwError = ERROR_SUCCESS;
1269 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1270 &iar, sizeof(INTERNET_ASYNC_RESULT));
1273 iar.dwResult = (DWORD)bSuccess;
1274 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1275 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1276 &iar, sizeof(INTERNET_ASYNC_RESULT));
1279 lend:
1280 if( lpwh )
1281 WININET_Release( &lpwh->hdr );
1283 return handle;
1287 /***********************************************************************
1288 * FtpGetFileA (WININET.@)
1290 * Retrieve file from the FTP server
1292 * RETURNS
1293 * TRUE on success
1294 * FALSE on failure
1297 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1298 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1299 DWORD_PTR dwContext)
1301 LPWSTR lpwzRemoteFile;
1302 LPWSTR lpwzNewFile;
1303 BOOL ret;
1305 lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
1306 lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
1307 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1308 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1309 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1310 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1311 return ret;
1315 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1317 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1318 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1320 TRACE("%p\n", lpwfs);
1322 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1323 req->lpszNewFile, req->fFailIfExists,
1324 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1325 HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1326 HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1330 /***********************************************************************
1331 * FtpGetFileW (WININET.@)
1333 * Retrieve file from the FTP server
1335 * RETURNS
1336 * TRUE on success
1337 * FALSE on failure
1340 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1341 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1342 DWORD_PTR dwContext)
1344 LPWININETFTPSESSIONW lpwfs;
1345 LPWININETAPPINFOW hIC = NULL;
1346 BOOL r = FALSE;
1348 if (!lpszRemoteFile || !lpszNewFile)
1350 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1351 return FALSE;
1354 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
1355 if (!lpwfs)
1357 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1358 return FALSE;
1361 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1363 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1364 goto lend;
1367 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1369 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1370 goto lend;
1373 if (lpwfs->download_in_progress != NULL)
1375 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1376 goto lend;
1379 hIC = lpwfs->lpAppInfo;
1380 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1382 WORKREQUEST workRequest;
1383 struct WORKREQ_FTPGETFILEW *req;
1385 workRequest.asyncproc = AsyncFtpGetFileProc;
1386 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1387 req = &workRequest.u.FtpGetFileW;
1388 req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
1389 req->lpszNewFile = WININET_strdupW(lpszNewFile);
1390 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1391 req->fFailIfExists = fFailIfExists;
1392 req->dwFlags = dwInternetFlags;
1393 req->dwContext = dwContext;
1395 r = INTERNET_AsyncCall(&workRequest);
1397 else
1399 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1400 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1403 lend:
1404 WININET_Release( &lpwfs->hdr );
1406 return r;
1410 /***********************************************************************
1411 * FTP_FtpGetFileW (Internal)
1413 * Retrieve file from the FTP server
1415 * RETURNS
1416 * TRUE on success
1417 * FALSE on failure
1420 BOOL WINAPI FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1421 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1422 DWORD_PTR dwContext)
1424 BOOL bSuccess = FALSE;
1425 HANDLE hFile;
1426 LPWININETAPPINFOW hIC = NULL;
1428 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1430 /* Clear any error information */
1431 INTERNET_SetLastError(0);
1433 /* Ensure we can write to lpszNewfile by opening it */
1434 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1435 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1436 if (INVALID_HANDLE_VALUE == hFile)
1437 return FALSE;
1439 /* Set up socket to retrieve data */
1440 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1442 INT nDataSocket;
1444 /* Get data socket to server */
1445 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1447 INT nResCode;
1449 /* Receive data */
1450 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1451 closesocket(nDataSocket);
1453 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1454 if (nResCode)
1456 if (nResCode == 226)
1457 bSuccess = TRUE;
1458 else
1459 FTP_SetResponseError(nResCode);
1464 if (lpwfs->lstnSocket != -1)
1465 closesocket(lpwfs->lstnSocket);
1467 CloseHandle(hFile);
1469 hIC = lpwfs->lpAppInfo;
1470 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1472 INTERNET_ASYNC_RESULT iar;
1474 iar.dwResult = (DWORD)bSuccess;
1475 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1476 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1477 &iar, sizeof(INTERNET_ASYNC_RESULT));
1480 return bSuccess;
1483 /***********************************************************************
1484 * FtpGetFileSize (WININET.@)
1486 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1488 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1490 if (lpdwFileSizeHigh)
1491 *lpdwFileSizeHigh = 0;
1493 return 0;
1496 /***********************************************************************
1497 * FtpDeleteFileA (WININET.@)
1499 * Delete a file on the ftp server
1501 * RETURNS
1502 * TRUE on success
1503 * FALSE on failure
1506 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1508 LPWSTR lpwzFileName;
1509 BOOL ret;
1511 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1512 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1513 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1514 return ret;
1517 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1519 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1520 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1522 TRACE("%p\n", lpwfs);
1524 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1525 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1528 /***********************************************************************
1529 * FtpDeleteFileW (WININET.@)
1531 * Delete a file on the ftp server
1533 * RETURNS
1534 * TRUE on success
1535 * FALSE on failure
1538 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1540 LPWININETFTPSESSIONW lpwfs;
1541 LPWININETAPPINFOW hIC = NULL;
1542 BOOL r = FALSE;
1544 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1545 if (!lpwfs)
1547 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1548 return FALSE;
1551 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1553 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1554 goto lend;
1557 if (lpwfs->download_in_progress != NULL)
1559 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1560 goto lend;
1563 if (!lpszFileName)
1565 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1566 goto lend;
1569 hIC = lpwfs->lpAppInfo;
1570 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1572 WORKREQUEST workRequest;
1573 struct WORKREQ_FTPDELETEFILEW *req;
1575 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1576 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1577 req = &workRequest.u.FtpDeleteFileW;
1578 req->lpszFilename = WININET_strdupW(lpszFileName);
1580 r = INTERNET_AsyncCall(&workRequest);
1582 else
1584 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1587 lend:
1588 WININET_Release( &lpwfs->hdr );
1590 return r;
1593 /***********************************************************************
1594 * FTP_FtpDeleteFileW (Internal)
1596 * Delete a file on the ftp server
1598 * RETURNS
1599 * TRUE on success
1600 * FALSE on failure
1603 BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName)
1605 INT nResCode;
1606 BOOL bSuccess = FALSE;
1607 LPWININETAPPINFOW hIC = NULL;
1609 TRACE("%p\n", lpwfs);
1611 /* Clear any error information */
1612 INTERNET_SetLastError(0);
1614 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1615 goto lend;
1617 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1618 if (nResCode)
1620 if (nResCode == 250)
1621 bSuccess = TRUE;
1622 else
1623 FTP_SetResponseError(nResCode);
1625 lend:
1626 hIC = lpwfs->lpAppInfo;
1627 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1629 INTERNET_ASYNC_RESULT iar;
1631 iar.dwResult = (DWORD)bSuccess;
1632 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1633 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1634 &iar, sizeof(INTERNET_ASYNC_RESULT));
1637 return bSuccess;
1641 /***********************************************************************
1642 * FtpRemoveDirectoryA (WININET.@)
1644 * Remove a directory on the ftp server
1646 * RETURNS
1647 * TRUE on success
1648 * FALSE on failure
1651 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1653 LPWSTR lpwzDirectory;
1654 BOOL ret;
1656 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
1657 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1658 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1659 return ret;
1662 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1664 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1665 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1667 TRACE("%p\n", lpwfs);
1669 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1670 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1673 /***********************************************************************
1674 * FtpRemoveDirectoryW (WININET.@)
1676 * Remove a directory on the ftp server
1678 * RETURNS
1679 * TRUE on success
1680 * FALSE on failure
1683 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1685 LPWININETFTPSESSIONW lpwfs;
1686 LPWININETAPPINFOW hIC = NULL;
1687 BOOL r = FALSE;
1689 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1690 if (!lpwfs)
1692 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1693 return FALSE;
1696 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1698 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1699 goto lend;
1702 if (lpwfs->download_in_progress != NULL)
1704 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1705 goto lend;
1708 if (!lpszDirectory)
1710 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1711 goto lend;
1714 hIC = lpwfs->lpAppInfo;
1715 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1717 WORKREQUEST workRequest;
1718 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1720 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1721 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1722 req = &workRequest.u.FtpRemoveDirectoryW;
1723 req->lpszDirectory = WININET_strdupW(lpszDirectory);
1725 r = INTERNET_AsyncCall(&workRequest);
1727 else
1729 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1732 lend:
1733 WININET_Release( &lpwfs->hdr );
1735 return r;
1738 /***********************************************************************
1739 * FTP_FtpRemoveDirectoryW (Internal)
1741 * Remove a directory on the ftp server
1743 * RETURNS
1744 * TRUE on success
1745 * FALSE on failure
1748 BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
1750 INT nResCode;
1751 BOOL bSuccess = FALSE;
1752 LPWININETAPPINFOW hIC = NULL;
1754 TRACE("\n");
1756 /* Clear any error information */
1757 INTERNET_SetLastError(0);
1759 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1760 goto lend;
1762 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1763 if (nResCode)
1765 if (nResCode == 250)
1766 bSuccess = TRUE;
1767 else
1768 FTP_SetResponseError(nResCode);
1771 lend:
1772 hIC = lpwfs->lpAppInfo;
1773 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1775 INTERNET_ASYNC_RESULT iar;
1777 iar.dwResult = (DWORD)bSuccess;
1778 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1779 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1780 &iar, sizeof(INTERNET_ASYNC_RESULT));
1783 return bSuccess;
1787 /***********************************************************************
1788 * FtpRenameFileA (WININET.@)
1790 * Rename a file on the ftp server
1792 * RETURNS
1793 * TRUE on success
1794 * FALSE on failure
1797 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1799 LPWSTR lpwzSrc;
1800 LPWSTR lpwzDest;
1801 BOOL ret;
1803 lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL;
1804 lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL;
1805 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1806 HeapFree(GetProcessHeap(), 0, lpwzSrc);
1807 HeapFree(GetProcessHeap(), 0, lpwzDest);
1808 return ret;
1811 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
1813 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
1814 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1816 TRACE("%p\n", lpwfs);
1818 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
1819 HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
1820 HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
1823 /***********************************************************************
1824 * FtpRenameFileW (WININET.@)
1826 * Rename a file on the ftp server
1828 * RETURNS
1829 * TRUE on success
1830 * FALSE on failure
1833 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
1835 LPWININETFTPSESSIONW lpwfs;
1836 LPWININETAPPINFOW hIC = NULL;
1837 BOOL r = FALSE;
1839 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1840 if (!lpwfs)
1842 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1843 return FALSE;
1846 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1848 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1849 goto lend;
1852 if (lpwfs->download_in_progress != NULL)
1854 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1855 goto lend;
1858 if (!lpszSrc || !lpszDest)
1860 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1861 goto lend;
1864 hIC = lpwfs->lpAppInfo;
1865 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1867 WORKREQUEST workRequest;
1868 struct WORKREQ_FTPRENAMEFILEW *req;
1870 workRequest.asyncproc = AsyncFtpRenameFileProc;
1871 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1872 req = &workRequest.u.FtpRenameFileW;
1873 req->lpszSrcFile = WININET_strdupW(lpszSrc);
1874 req->lpszDestFile = WININET_strdupW(lpszDest);
1876 r = INTERNET_AsyncCall(&workRequest);
1878 else
1880 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
1883 lend:
1884 WININET_Release( &lpwfs->hdr );
1886 return r;
1889 /***********************************************************************
1890 * FTP_FtpRenameFileW (Internal)
1892 * Rename a file on the ftp server
1894 * RETURNS
1895 * TRUE on success
1896 * FALSE on failure
1899 BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs,
1900 LPCWSTR lpszSrc, LPCWSTR lpszDest)
1902 INT nResCode;
1903 BOOL bSuccess = FALSE;
1904 LPWININETAPPINFOW hIC = NULL;
1906 TRACE("\n");
1908 /* Clear any error information */
1909 INTERNET_SetLastError(0);
1911 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
1912 goto lend;
1914 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1915 if (nResCode == 350)
1917 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
1918 goto lend;
1920 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1923 if (nResCode == 250)
1924 bSuccess = TRUE;
1925 else
1926 FTP_SetResponseError(nResCode);
1928 lend:
1929 hIC = lpwfs->lpAppInfo;
1930 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1932 INTERNET_ASYNC_RESULT iar;
1934 iar.dwResult = (DWORD)bSuccess;
1935 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1936 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1937 &iar, sizeof(INTERNET_ASYNC_RESULT));
1940 return bSuccess;
1943 /***********************************************************************
1944 * FtpCommandA (WININET.@)
1946 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1947 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1949 BOOL r;
1950 WCHAR *cmdW;
1952 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1953 debugstr_a(lpszCommand), dwContext, phFtpCommand);
1955 if (fExpectResponse)
1957 FIXME("data connection not supported\n");
1958 return FALSE;
1961 if (!lpszCommand || !lpszCommand[0])
1963 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1964 return FALSE;
1967 if (!(cmdW = WININET_strdup_AtoW(lpszCommand)))
1969 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1970 return FALSE;
1973 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
1975 HeapFree(GetProcessHeap(), 0, cmdW);
1976 return r;
1979 /***********************************************************************
1980 * FtpCommandW (WININET.@)
1982 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1983 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1985 BOOL r = FALSE;
1986 LPWININETFTPSESSIONW lpwfs;
1987 LPSTR cmd = NULL;
1988 DWORD len, nBytesSent= 0;
1989 INT nResCode, nRC = 0;
1991 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1992 debugstr_w(lpszCommand), dwContext, phFtpCommand);
1994 if (!lpszCommand || !lpszCommand[0])
1996 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1997 return FALSE;
2000 if (fExpectResponse)
2002 FIXME("data connection not supported\n");
2003 return FALSE;
2006 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
2007 if (!lpwfs)
2009 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2010 return FALSE;
2013 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2015 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2016 goto lend;
2019 if (lpwfs->download_in_progress != NULL)
2021 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2022 goto lend;
2025 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2026 if ((cmd = HeapAlloc(GetProcessHeap(), 0, len )))
2027 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2028 else
2030 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2031 goto lend;
2034 strcat(cmd, szCRLF);
2035 len--;
2037 TRACE("Sending (%s) len(%d)\n", cmd, len);
2038 while ((nBytesSent < len) && (nRC != -1))
2040 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2041 if (nRC != -1)
2043 nBytesSent += nRC;
2044 TRACE("Sent %d bytes\n", nRC);
2048 if (nBytesSent)
2050 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2051 if (nResCode > 0 && nResCode < 400)
2052 r = TRUE;
2053 else
2054 FTP_SetResponseError(nResCode);
2057 lend:
2058 WININET_Release( &lpwfs->hdr );
2059 HeapFree(GetProcessHeap(), 0, cmd);
2060 return r;
2064 /***********************************************************************
2065 * FTPSESSION_Destroy (internal)
2067 * Deallocate session handle
2069 static void FTPSESSION_Destroy(WININETHANDLEHEADER *hdr)
2071 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2073 TRACE("\n");
2075 WININET_Release(&lpwfs->lpAppInfo->hdr);
2077 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2078 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2079 HeapFree(GetProcessHeap(), 0, lpwfs);
2082 static void FTPSESSION_CloseConnection(WININETHANDLEHEADER *hdr)
2084 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2086 TRACE("\n");
2088 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2089 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2091 if (lpwfs->download_in_progress != NULL)
2092 lpwfs->download_in_progress->session_deleted = TRUE;
2094 if (lpwfs->sndSocket != -1)
2095 closesocket(lpwfs->sndSocket);
2097 if (lpwfs->lstnSocket != -1)
2098 closesocket(lpwfs->lstnSocket);
2100 if (lpwfs->pasvSocket != -1)
2101 closesocket(lpwfs->pasvSocket);
2103 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2104 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2107 static const HANDLEHEADERVtbl FTPSESSIONVtbl = {
2108 FTPSESSION_Destroy,
2109 FTPSESSION_CloseConnection,
2110 NULL,
2111 NULL,
2112 NULL
2116 /***********************************************************************
2117 * FTP_Connect (internal)
2119 * Connect to a ftp server
2121 * RETURNS
2122 * HINTERNET a session handle on success
2123 * NULL on failure
2125 * NOTES:
2127 * Windows uses 'anonymous' as the username, when given a NULL username
2128 * and a NULL password. The password is first looked up in:
2130 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2132 * If this entry is not present it uses the current username as the password.
2136 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
2137 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2138 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2139 DWORD dwInternalFlags)
2141 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2142 'M','i','c','r','o','s','o','f','t','\\',
2143 'W','i','n','d','o','w','s','\\',
2144 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2145 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2146 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2147 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2148 static const WCHAR szEmpty[] = {'\0'};
2149 struct sockaddr_in socketAddr;
2150 INT nsocket = -1;
2151 UINT sock_namelen;
2152 BOOL bSuccess = FALSE;
2153 LPWININETFTPSESSIONW lpwfs = NULL;
2154 HINTERNET handle = NULL;
2156 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2157 hIC, debugstr_w(lpszServerName),
2158 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2160 assert( hIC->hdr.htype == WH_HINIT );
2162 if (NULL == lpszUserName && NULL != lpszPassword)
2164 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2165 goto lerror;
2168 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
2169 if (NULL == lpwfs)
2171 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2172 goto lerror;
2175 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2176 nServerPort = INTERNET_DEFAULT_FTP_PORT;
2178 lpwfs->hdr.htype = WH_HFTPSESSION;
2179 lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
2180 lpwfs->hdr.dwFlags = dwFlags;
2181 lpwfs->hdr.dwContext = dwContext;
2182 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2183 lpwfs->hdr.dwRefCount = 1;
2184 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2185 lpwfs->download_in_progress = NULL;
2186 lpwfs->sndSocket = -1;
2187 lpwfs->lstnSocket = -1;
2188 lpwfs->pasvSocket = -1;
2190 WININET_AddRef( &hIC->hdr );
2191 lpwfs->lpAppInfo = hIC;
2192 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2194 handle = WININET_AllocHandle( &lpwfs->hdr );
2195 if( !handle )
2197 ERR("Failed to alloc handle\n");
2198 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2199 goto lerror;
2202 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2203 if(strchrW(hIC->lpszProxy, ' '))
2204 FIXME("Several proxies not implemented.\n");
2205 if(hIC->lpszProxyBypass)
2206 FIXME("Proxy bypass is ignored.\n");
2208 if ( !lpszUserName) {
2209 HKEY key;
2210 WCHAR szPassword[MAX_PATH];
2211 DWORD len = sizeof(szPassword);
2213 lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
2215 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2216 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2217 /* Nothing in the registry, get the username and use that as the password */
2218 if (!GetUserNameW(szPassword, &len)) {
2219 /* Should never get here, but use an empty password as failsafe */
2220 strcpyW(szPassword, szEmpty);
2223 RegCloseKey(key);
2225 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2226 lpwfs->lpszPassword = WININET_strdupW(szPassword);
2228 else {
2229 lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
2231 if (lpszPassword)
2232 lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
2233 else
2234 lpwfs->lpszPassword = WININET_strdupW(szEmpty);
2237 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2238 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2240 INTERNET_ASYNC_RESULT iar;
2242 iar.dwResult = (DWORD)handle;
2243 iar.dwError = ERROR_SUCCESS;
2245 SendAsyncCallback(&hIC->hdr, dwContext,
2246 INTERNET_STATUS_HANDLE_CREATED, &iar,
2247 sizeof(INTERNET_ASYNC_RESULT));
2250 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2251 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2253 if (!GetAddress(lpszServerName, nServerPort, &socketAddr))
2255 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2256 goto lerror;
2259 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2260 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2262 nsocket = socket(AF_INET,SOCK_STREAM,0);
2263 if (nsocket == -1)
2265 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2266 goto lerror;
2269 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2270 &socketAddr, sizeof(struct sockaddr_in));
2272 if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
2274 ERR("Unable to connect (%s)\n", strerror(errno));
2275 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2277 else
2279 TRACE("Connected to server\n");
2280 lpwfs->sndSocket = nsocket;
2281 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2282 &socketAddr, sizeof(struct sockaddr_in));
2284 sock_namelen = sizeof(lpwfs->socketAddress);
2285 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2287 if (FTP_ConnectToHost(lpwfs))
2289 TRACE("Successfully logged into server\n");
2290 bSuccess = TRUE;
2294 lerror:
2295 if (lpwfs) WININET_Release( &lpwfs->hdr );
2297 if (!bSuccess && handle)
2299 WININET_FreeHandle( handle );
2300 handle = NULL;
2303 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2305 INTERNET_ASYNC_RESULT iar;
2307 iar.dwResult = bSuccess ? (DWORD_PTR)lpwfs : 0;
2308 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2309 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2310 &iar, sizeof(INTERNET_ASYNC_RESULT));
2313 return handle;
2317 /***********************************************************************
2318 * FTP_ConnectToHost (internal)
2320 * Connect to a ftp server
2322 * RETURNS
2323 * TRUE on success
2324 * NULL on failure
2327 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
2329 INT nResCode;
2330 BOOL bSuccess = FALSE;
2332 TRACE("\n");
2333 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2335 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2336 goto lend;
2338 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2339 if (nResCode)
2341 /* Login successful... */
2342 if (nResCode == 230)
2343 bSuccess = TRUE;
2344 /* User name okay, need password... */
2345 else if (nResCode == 331)
2346 bSuccess = FTP_SendPassword(lpwfs);
2347 /* Need account for login... */
2348 else if (nResCode == 332)
2349 bSuccess = FTP_SendAccount(lpwfs);
2350 else
2351 FTP_SetResponseError(nResCode);
2354 TRACE("Returning %d\n", bSuccess);
2355 lend:
2356 return bSuccess;
2360 /***********************************************************************
2361 * FTP_SendCommandA (internal)
2363 * Send command to server
2365 * RETURNS
2366 * TRUE on success
2367 * NULL on failure
2370 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2371 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
2373 DWORD len;
2374 CHAR *buf;
2375 DWORD nBytesSent = 0;
2376 int nRC = 0;
2377 DWORD dwParamLen;
2379 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2381 if (lpfnStatusCB)
2383 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2386 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2387 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2388 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2390 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2391 return FALSE;
2393 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2394 dwParamLen ? lpszParam : "", szCRLF);
2396 TRACE("Sending (%s) len(%d)\n", buf, len);
2397 while((nBytesSent < len) && (nRC != -1))
2399 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2400 nBytesSent += nRC;
2403 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2405 if (lpfnStatusCB)
2407 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2408 &nBytesSent, sizeof(DWORD));
2411 TRACE("Sent %d bytes\n", nBytesSent);
2412 return (nRC != -1);
2415 /***********************************************************************
2416 * FTP_SendCommand (internal)
2418 * Send command to server
2420 * RETURNS
2421 * TRUE on success
2422 * NULL on failure
2425 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2426 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
2428 BOOL ret;
2429 LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
2430 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2431 HeapFree(GetProcessHeap(), 0, lpszParamA);
2432 return ret;
2435 /***********************************************************************
2436 * FTP_ReceiveResponse (internal)
2438 * Receive response from server
2440 * RETURNS
2441 * Reply code on success
2442 * 0 on failure
2445 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD_PTR dwContext)
2447 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2448 DWORD nRecv;
2449 INT rc = 0;
2450 char firstprefix[5];
2451 BOOL multiline = FALSE;
2452 LPWININETAPPINFOW hIC = NULL;
2454 TRACE("socket(%d)\n", lpwfs->sndSocket);
2456 hIC = lpwfs->lpAppInfo;
2457 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2459 while(1)
2461 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2462 goto lerror;
2464 if (nRecv >= 3)
2466 if(!multiline)
2468 if(lpszResponse[3] != '-')
2469 break;
2470 else
2471 { /* Start of multiline response. Loop until we get "nnn " */
2472 multiline = TRUE;
2473 memcpy(firstprefix, lpszResponse, 3);
2474 firstprefix[3] = ' ';
2475 firstprefix[4] = '\0';
2478 else
2480 if(!memcmp(firstprefix, lpszResponse, 4))
2481 break;
2486 if (nRecv >= 3)
2488 rc = atoi(lpszResponse);
2490 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2491 &nRecv, sizeof(DWORD));
2494 lerror:
2495 TRACE("return %d\n", rc);
2496 return rc;
2500 /***********************************************************************
2501 * FTP_SendPassword (internal)
2503 * Send password to ftp server
2505 * RETURNS
2506 * TRUE on success
2507 * NULL on failure
2510 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2512 INT nResCode;
2513 BOOL bSuccess = FALSE;
2515 TRACE("\n");
2516 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2517 goto lend;
2519 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2520 if (nResCode)
2522 TRACE("Received reply code %d\n", nResCode);
2523 /* Login successful... */
2524 if (nResCode == 230)
2525 bSuccess = TRUE;
2526 /* Command not implemented, superfluous at the server site... */
2527 /* Need account for login... */
2528 else if (nResCode == 332)
2529 bSuccess = FTP_SendAccount(lpwfs);
2530 else
2531 FTP_SetResponseError(nResCode);
2534 lend:
2535 TRACE("Returning %d\n", bSuccess);
2536 return bSuccess;
2540 /***********************************************************************
2541 * FTP_SendAccount (internal)
2545 * RETURNS
2546 * TRUE on success
2547 * FALSE on failure
2550 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2552 INT nResCode;
2553 BOOL bSuccess = FALSE;
2555 TRACE("\n");
2556 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2557 goto lend;
2559 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2560 if (nResCode)
2561 bSuccess = TRUE;
2562 else
2563 FTP_SetResponseError(nResCode);
2565 lend:
2566 return bSuccess;
2570 /***********************************************************************
2571 * FTP_SendStore (internal)
2573 * Send request to upload file to ftp server
2575 * RETURNS
2576 * TRUE on success
2577 * FALSE on failure
2580 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2582 INT nResCode;
2583 BOOL bSuccess = FALSE;
2585 TRACE("\n");
2586 if (!FTP_InitListenSocket(lpwfs))
2587 goto lend;
2589 if (!FTP_SendType(lpwfs, dwType))
2590 goto lend;
2592 if (!FTP_SendPortOrPasv(lpwfs))
2593 goto lend;
2595 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2596 goto lend;
2597 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2598 if (nResCode)
2600 if (nResCode == 150 || nResCode == 125)
2601 bSuccess = TRUE;
2602 else
2603 FTP_SetResponseError(nResCode);
2606 lend:
2607 if (!bSuccess && lpwfs->lstnSocket != -1)
2609 closesocket(lpwfs->lstnSocket);
2610 lpwfs->lstnSocket = -1;
2613 return bSuccess;
2617 /***********************************************************************
2618 * FTP_InitListenSocket (internal)
2620 * Create a socket to listen for server response
2622 * RETURNS
2623 * TRUE on success
2624 * FALSE on failure
2627 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2629 BOOL bSuccess = FALSE;
2630 socklen_t namelen = sizeof(struct sockaddr_in);
2632 TRACE("\n");
2634 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2635 if (lpwfs->lstnSocket == -1)
2637 TRACE("Unable to create listening socket\n");
2638 goto lend;
2641 /* We obtain our ip addr from the name of the command channel socket */
2642 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2644 /* and get the system to assign us a port */
2645 lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
2647 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2649 TRACE("Unable to bind socket\n");
2650 goto lend;
2653 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2655 TRACE("listen failed\n");
2656 goto lend;
2659 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2660 bSuccess = TRUE;
2662 lend:
2663 if (!bSuccess && lpwfs->lstnSocket != -1)
2665 closesocket(lpwfs->lstnSocket);
2666 lpwfs->lstnSocket = -1;
2669 return bSuccess;
2673 /***********************************************************************
2674 * FTP_SendType (internal)
2676 * Tell server type of data being transferred
2678 * RETURNS
2679 * TRUE on success
2680 * FALSE on failure
2682 * W98SE doesn't cache the type that's currently set
2683 * (i.e. it sends it always),
2684 * so we probably don't want to do that either.
2686 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2688 INT nResCode;
2689 WCHAR type[] = { 'I','\0' };
2690 BOOL bSuccess = FALSE;
2692 TRACE("\n");
2693 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2694 type[0] = 'A';
2696 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2697 goto lend;
2699 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2700 if (nResCode)
2702 if (nResCode == 2)
2703 bSuccess = TRUE;
2704 else
2705 FTP_SetResponseError(nResCode);
2708 lend:
2709 return bSuccess;
2713 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2714 /***********************************************************************
2715 * FTP_GetFileSize (internal)
2717 * Retrieves from the server the size of the given file
2719 * RETURNS
2720 * TRUE on success
2721 * FALSE on failure
2724 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2726 INT nResCode;
2727 BOOL bSuccess = FALSE;
2729 TRACE("\n");
2731 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2732 goto lend;
2734 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2735 if (nResCode)
2737 if (nResCode == 213) {
2738 /* Now parses the output to get the actual file size */
2739 int i;
2740 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2742 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2743 if (lpszResponseBuffer[i] == '\0') return FALSE;
2744 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2746 bSuccess = TRUE;
2747 } else {
2748 FTP_SetResponseError(nResCode);
2752 lend:
2753 return bSuccess;
2755 #endif
2758 /***********************************************************************
2759 * FTP_SendPort (internal)
2761 * Tell server which port to use
2763 * RETURNS
2764 * TRUE on success
2765 * FALSE on failure
2768 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2770 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2771 INT nResCode;
2772 WCHAR szIPAddress[64];
2773 BOOL bSuccess = FALSE;
2774 TRACE("\n");
2776 sprintfW(szIPAddress, szIPFormat,
2777 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2778 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2779 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2780 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2781 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2782 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2784 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2785 goto lend;
2787 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2788 if (nResCode)
2790 if (nResCode == 200)
2791 bSuccess = TRUE;
2792 else
2793 FTP_SetResponseError(nResCode);
2796 lend:
2797 return bSuccess;
2801 /***********************************************************************
2802 * FTP_DoPassive (internal)
2804 * Tell server that we want to do passive transfers
2805 * and connect data socket
2807 * RETURNS
2808 * TRUE on success
2809 * FALSE on failure
2812 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2814 INT nResCode;
2815 BOOL bSuccess = FALSE;
2817 TRACE("\n");
2818 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2819 goto lend;
2821 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2822 if (nResCode)
2824 if (nResCode == 227)
2826 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2827 LPSTR p;
2828 int f[6];
2829 int i;
2830 char *pAddr, *pPort;
2831 INT nsocket = -1;
2832 struct sockaddr_in dataSocketAddress;
2834 p = lpszResponseBuffer+4; /* skip status code */
2835 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
2837 if (*p == '\0')
2839 ERR("no address found in response, aborting\n");
2840 goto lend;
2843 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
2844 &f[4], &f[5]) != 6)
2846 ERR("unknown response address format '%s', aborting\n", p);
2847 goto lend;
2849 for (i=0; i < 6; i++)
2850 f[i] = f[i] & 0xff;
2852 dataSocketAddress = lpwfs->socketAddress;
2853 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2854 pPort = (char *)&(dataSocketAddress.sin_port);
2855 pAddr[0] = f[0];
2856 pAddr[1] = f[1];
2857 pAddr[2] = f[2];
2858 pAddr[3] = f[3];
2859 pPort[0] = f[4];
2860 pPort[1] = f[5];
2862 nsocket = socket(AF_INET,SOCK_STREAM,0);
2863 if (nsocket == -1)
2864 goto lend;
2866 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2868 ERR("can't connect passive FTP data port.\n");
2869 closesocket(nsocket);
2870 goto lend;
2872 lpwfs->pasvSocket = nsocket;
2873 bSuccess = TRUE;
2875 else
2876 FTP_SetResponseError(nResCode);
2879 lend:
2880 return bSuccess;
2884 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2886 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2888 if (!FTP_DoPassive(lpwfs))
2889 return FALSE;
2891 else
2893 if (!FTP_SendPort(lpwfs))
2894 return FALSE;
2896 return TRUE;
2900 /***********************************************************************
2901 * FTP_GetDataSocket (internal)
2903 * Either accepts an incoming data socket connection from the server
2904 * or just returns the already opened socket after a PASV command
2905 * in case of passive FTP.
2908 * RETURNS
2909 * TRUE on success
2910 * FALSE on failure
2913 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
2915 struct sockaddr_in saddr;
2916 socklen_t addrlen = sizeof(struct sockaddr);
2918 TRACE("\n");
2919 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2921 *nDataSocket = lpwfs->pasvSocket;
2923 else
2925 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
2926 closesocket(lpwfs->lstnSocket);
2927 lpwfs->lstnSocket = -1;
2929 return *nDataSocket != -1;
2933 /***********************************************************************
2934 * FTP_SendData (internal)
2936 * Send data to the server
2938 * RETURNS
2939 * TRUE on success
2940 * FALSE on failure
2943 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
2945 BY_HANDLE_FILE_INFORMATION fi;
2946 DWORD nBytesRead = 0;
2947 DWORD nBytesSent = 0;
2948 DWORD nTotalSent = 0;
2949 DWORD nBytesToSend, nLen;
2950 int nRC = 1;
2951 time_t s_long_time, e_long_time;
2952 LONG nSeconds;
2953 CHAR *lpszBuffer;
2955 TRACE("\n");
2956 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2958 /* Get the size of the file. */
2959 GetFileInformationByHandle(hFile, &fi);
2960 time(&s_long_time);
2964 nBytesToSend = nBytesRead - nBytesSent;
2966 if (nBytesToSend <= 0)
2968 /* Read data from file. */
2969 nBytesSent = 0;
2970 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
2971 ERR("Failed reading from file\n");
2973 if (nBytesRead > 0)
2974 nBytesToSend = nBytesRead;
2975 else
2976 break;
2979 nLen = DATA_PACKET_SIZE < nBytesToSend ?
2980 DATA_PACKET_SIZE : nBytesToSend;
2981 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
2983 if (nRC != -1)
2985 nBytesSent += nRC;
2986 nTotalSent += nRC;
2989 /* Do some computation to display the status. */
2990 time(&e_long_time);
2991 nSeconds = e_long_time - s_long_time;
2992 if( nSeconds / 60 > 0 )
2994 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
2995 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
2996 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
2998 else
3000 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3001 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3002 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3004 } while (nRC != -1);
3006 TRACE("file transfer complete!\n");
3008 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3010 return nTotalSent;
3014 /***********************************************************************
3015 * FTP_SendRetrieve (internal)
3017 * Send request to retrieve a file
3019 * RETURNS
3020 * Number of bytes to be received on success
3021 * 0 on failure
3024 static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3026 INT nResCode;
3027 BOOL ret;
3029 TRACE("\n");
3030 if (!(ret = FTP_InitListenSocket(lpwfs)))
3031 goto lend;
3033 if (!(ret = FTP_SendType(lpwfs, dwType)))
3034 goto lend;
3036 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3037 goto lend;
3039 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3040 goto lend;
3042 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3043 if ((nResCode != 125) && (nResCode != 150)) {
3044 /* That means that we got an error getting the file. */
3045 FTP_SetResponseError(nResCode);
3046 ret = FALSE;
3049 lend:
3050 if (!ret && lpwfs->lstnSocket != -1)
3052 closesocket(lpwfs->lstnSocket);
3053 lpwfs->lstnSocket = -1;
3056 return ret;
3060 /***********************************************************************
3061 * FTP_RetrieveData (internal)
3063 * Retrieve data from server
3065 * RETURNS
3066 * TRUE on success
3067 * FALSE on failure
3070 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
3072 DWORD nBytesWritten;
3073 DWORD nBytesReceived = 0;
3074 INT nRC = 0;
3075 CHAR *lpszBuffer;
3077 TRACE("\n");
3079 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3080 if (NULL == lpszBuffer)
3082 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3083 return FALSE;
3086 while (nRC != -1)
3088 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3089 if (nRC != -1)
3091 /* other side closed socket. */
3092 if (nRC == 0)
3093 goto recv_end;
3094 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3095 nBytesReceived += nRC;
3099 TRACE("Data transfer complete\n");
3101 recv_end:
3102 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3104 return (nRC != -1);
3107 /***********************************************************************
3108 * FTPFINDNEXT_Destroy (internal)
3110 * Deallocate session handle
3112 static void FTPFINDNEXT_Destroy(WININETHANDLEHEADER *hdr)
3114 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3115 DWORD i;
3117 TRACE("\n");
3119 WININET_Release(&lpwfn->lpFtpSession->hdr);
3121 for (i = 0; i < lpwfn->size; i++)
3123 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3126 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3127 HeapFree(GetProcessHeap(), 0, lpwfn);
3130 static DWORD WINAPI FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3132 WIN32_FIND_DATAW *find_data = data;
3133 DWORD res = ERROR_SUCCESS;
3135 TRACE("index(%d) size(%d)\n", find->index, find->size);
3137 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3139 if (find->index < find->size) {
3140 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3141 find->index++;
3143 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3144 }else {
3145 res = ERROR_NO_MORE_FILES;
3148 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3150 INTERNET_ASYNC_RESULT iar;
3152 iar.dwResult = (res == ERROR_SUCCESS);
3153 iar.dwError = res;
3155 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3156 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3157 sizeof(INTERNET_ASYNC_RESULT));
3160 return res;
3163 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3165 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3167 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3170 static DWORD FTPFINDNEXT_FindNextFileW(WININETHANDLEHEADER *hdr, void *data)
3172 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3174 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3176 WORKREQUEST workRequest;
3177 struct WORKREQ_FTPFINDNEXTW *req;
3179 workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3180 workRequest.hdr = WININET_AddRef( &find->hdr );
3181 req = &workRequest.u.FtpFindNextW;
3182 req->lpFindFileData = data;
3184 INTERNET_AsyncCall(&workRequest);
3186 return ERROR_SUCCESS;
3189 return FTPFINDNEXT_FindNextFileProc(find, data);
3192 static const HANDLEHEADERVtbl FTPFINDNEXTVtbl = {
3193 FTPFINDNEXT_Destroy,
3194 NULL,
3195 NULL,
3196 NULL,
3197 FTPFINDNEXT_FindNextFileW
3200 /***********************************************************************
3201 * FTP_ReceiveFileList (internal)
3203 * Read file list from server
3205 * RETURNS
3206 * Handle to file list on success
3207 * NULL on failure
3210 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3211 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3213 DWORD dwSize = 0;
3214 LPFILEPROPERTIESW lpafp = NULL;
3215 LPWININETFTPFINDNEXTW lpwfn = NULL;
3216 HINTERNET handle = 0;
3218 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3220 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3222 if(lpFindFileData)
3223 FTP_ConvertFileProp(lpafp, lpFindFileData);
3225 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3226 if (lpwfn)
3228 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3229 lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3230 lpwfn->hdr.dwContext = dwContext;
3231 lpwfn->hdr.dwRefCount = 1;
3232 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3233 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3234 lpwfn->size = dwSize;
3235 lpwfn->lpafp = lpafp;
3237 WININET_AddRef( &lpwfs->hdr );
3238 lpwfn->lpFtpSession = lpwfs;
3239 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3241 handle = WININET_AllocHandle( &lpwfn->hdr );
3245 if( lpwfn )
3246 WININET_Release( &lpwfn->hdr );
3248 TRACE("Matched %d files\n", dwSize);
3249 return handle;
3253 /***********************************************************************
3254 * FTP_ConvertFileProp (internal)
3256 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3258 * RETURNS
3259 * TRUE on success
3260 * FALSE on failure
3263 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3265 BOOL bSuccess = FALSE;
3267 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3269 if (lpafp)
3271 /* Convert 'Unix' time to Windows time */
3272 RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
3273 (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
3274 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3275 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3277 /* Not all fields are filled in */
3278 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3279 lpFindFileData->nFileSizeLow = lpafp->nSize;
3281 if (lpafp->bIsDirectory)
3282 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3284 if (lpafp->lpszName)
3285 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3287 bSuccess = TRUE;
3290 return bSuccess;
3293 /***********************************************************************
3294 * FTP_ParseNextFile (internal)
3296 * Parse the next line in file listing
3298 * RETURNS
3299 * TRUE on success
3300 * FALSE on failure
3302 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3304 static const char szSpace[] = " \t";
3305 DWORD nBufLen;
3306 char *pszLine;
3307 char *pszToken;
3308 char *pszTmp;
3309 BOOL found = FALSE;
3310 int i;
3312 lpfp->lpszName = NULL;
3313 do {
3314 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3315 return FALSE;
3317 pszToken = strtok(pszLine, szSpace);
3318 /* ls format
3319 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3321 * For instance:
3322 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3324 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3325 if(!FTP_ParsePermission(pszToken, lpfp))
3326 lpfp->bIsDirectory = FALSE;
3327 for(i=0; i<=3; i++) {
3328 if(!(pszToken = strtok(NULL, szSpace)))
3329 break;
3331 if(!pszToken) continue;
3332 if(lpfp->bIsDirectory) {
3333 TRACE("Is directory\n");
3334 lpfp->nSize = 0;
3336 else {
3337 TRACE("Size: %s\n", pszToken);
3338 lpfp->nSize = atol(pszToken);
3341 lpfp->tmLastModified.tm_sec = 0;
3342 lpfp->tmLastModified.tm_min = 0;
3343 lpfp->tmLastModified.tm_hour = 0;
3344 lpfp->tmLastModified.tm_mday = 0;
3345 lpfp->tmLastModified.tm_mon = 0;
3346 lpfp->tmLastModified.tm_year = 0;
3348 /* Determine month */
3349 pszToken = strtok(NULL, szSpace);
3350 if(!pszToken) continue;
3351 if(strlen(pszToken) >= 3) {
3352 pszToken[3] = 0;
3353 if((pszTmp = StrStrIA(szMonths, pszToken)))
3354 lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
3356 /* Determine day */
3357 pszToken = strtok(NULL, szSpace);
3358 if(!pszToken) continue;
3359 lpfp->tmLastModified.tm_mday = atoi(pszToken);
3360 /* Determine time or year */
3361 pszToken = strtok(NULL, szSpace);
3362 if(!pszToken) continue;
3363 if((pszTmp = strchr(pszToken, ':'))) {
3364 struct tm* apTM;
3365 time_t aTime;
3366 *pszTmp = 0;
3367 pszTmp++;
3368 lpfp->tmLastModified.tm_min = atoi(pszTmp);
3369 lpfp->tmLastModified.tm_hour = atoi(pszToken);
3370 time(&aTime);
3371 apTM = localtime(&aTime);
3372 lpfp->tmLastModified.tm_year = apTM->tm_year;
3374 else {
3375 lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
3376 lpfp->tmLastModified.tm_hour = 12;
3378 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3379 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3380 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3381 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3383 pszToken = strtok(NULL, szSpace);
3384 if(!pszToken) continue;
3385 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3386 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3388 /* NT way of parsing ... :
3390 07-13-03 08:55PM <DIR> sakpatch
3391 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3393 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3394 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3396 sscanf(pszToken, "%d-%d-%d",
3397 &lpfp->tmLastModified.tm_mon,
3398 &lpfp->tmLastModified.tm_mday,
3399 &lpfp->tmLastModified.tm_year);
3401 /* Hacky and bad Y2K protection :-) */
3402 if (lpfp->tmLastModified.tm_year < 70)
3403 lpfp->tmLastModified.tm_year += 100;
3405 pszToken = strtok(NULL, szSpace);
3406 if(!pszToken) continue;
3407 sscanf(pszToken, "%d:%d",
3408 &lpfp->tmLastModified.tm_hour,
3409 &lpfp->tmLastModified.tm_min);
3410 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3411 lpfp->tmLastModified.tm_hour += 12;
3413 lpfp->tmLastModified.tm_sec = 0;
3415 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3416 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3417 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3418 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3420 pszToken = strtok(NULL, szSpace);
3421 if(!pszToken) continue;
3422 if(!strcasecmp(pszToken, "<DIR>")) {
3423 lpfp->bIsDirectory = TRUE;
3424 lpfp->nSize = 0;
3425 TRACE("Is directory\n");
3427 else {
3428 lpfp->bIsDirectory = FALSE;
3429 lpfp->nSize = atol(pszToken);
3430 TRACE("Size: %d\n", lpfp->nSize);
3433 pszToken = strtok(NULL, szSpace);
3434 if(!pszToken) continue;
3435 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3436 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3438 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3439 else if(pszToken[0] == '+') {
3440 FIXME("EPLF Format not implemented\n");
3443 if(lpfp->lpszName) {
3444 if((lpszSearchFile == NULL) ||
3445 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3446 found = TRUE;
3447 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3449 else {
3450 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3451 lpfp->lpszName = NULL;
3454 } while(!found);
3455 return TRUE;
3458 /***********************************************************************
3459 * FTP_ParseDirectory (internal)
3461 * Parse string of directory information
3463 * RETURNS
3464 * TRUE on success
3465 * FALSE on failure
3467 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3468 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3470 BOOL bSuccess = TRUE;
3471 INT sizeFilePropArray = 500;/*20; */
3472 INT indexFilePropArray = -1;
3474 TRACE("\n");
3476 /* Allocate initial file properties array */
3477 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3478 if (!*lpafp)
3479 return FALSE;
3481 do {
3482 if (indexFilePropArray+1 >= sizeFilePropArray)
3484 LPFILEPROPERTIESW tmpafp;
3486 sizeFilePropArray *= 2;
3487 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3488 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3489 if (NULL == tmpafp)
3491 bSuccess = FALSE;
3492 break;
3495 *lpafp = tmpafp;
3497 indexFilePropArray++;
3498 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3500 if (bSuccess && indexFilePropArray)
3502 if (indexFilePropArray < sizeFilePropArray - 1)
3504 LPFILEPROPERTIESW tmpafp;
3506 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3507 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3508 if (NULL != tmpafp)
3509 *lpafp = tmpafp;
3511 *dwfp = indexFilePropArray;
3513 else
3515 HeapFree(GetProcessHeap(), 0, *lpafp);
3516 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3517 bSuccess = FALSE;
3520 return bSuccess;
3524 /***********************************************************************
3525 * FTP_ParsePermission (internal)
3527 * Parse permission string of directory information
3529 * RETURNS
3530 * TRUE on success
3531 * FALSE on failure
3534 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3536 BOOL bSuccess = TRUE;
3537 unsigned short nPermission = 0;
3538 INT nPos = 1;
3539 INT nLast = 9;
3541 TRACE("\n");
3542 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3544 bSuccess = FALSE;
3545 return bSuccess;
3548 lpfp->bIsDirectory = (*lpszPermission == 'd');
3551 switch (nPos)
3553 case 1:
3554 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3555 break;
3556 case 2:
3557 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3558 break;
3559 case 3:
3560 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3561 break;
3562 case 4:
3563 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3564 break;
3565 case 5:
3566 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3567 break;
3568 case 6:
3569 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3570 break;
3571 case 7:
3572 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3573 break;
3574 case 8:
3575 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3576 break;
3577 case 9:
3578 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3579 break;
3581 nPos++;
3582 }while (nPos <= nLast);
3584 lpfp->permissions = nPermission;
3585 return bSuccess;
3589 /***********************************************************************
3590 * FTP_SetResponseError (internal)
3592 * Set the appropriate error code for a given response from the server
3594 * RETURNS
3597 static DWORD FTP_SetResponseError(DWORD dwResponse)
3599 DWORD dwCode = 0;
3601 switch(dwResponse)
3603 case 425: /* Cannot open data connection. */
3604 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3605 break;
3607 case 426: /* Connection closed, transer aborted. */
3608 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3609 break;
3611 case 530: /* Not logged in. Login incorrect. */
3612 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3613 break;
3615 case 421: /* Service not available - Server may be shutting down. */
3616 case 450: /* File action not taken. File may be busy. */
3617 case 451: /* Action aborted. Server error. */
3618 case 452: /* Action not taken. Insufficient storage space on server. */
3619 case 500: /* Syntax error. Command unrecognized. */
3620 case 501: /* Syntax error. Error in parameters or arguments. */
3621 case 502: /* Command not implemented. */
3622 case 503: /* Bad sequence of commands. */
3623 case 504: /* Command not implemented for that parameter. */
3624 case 532: /* Need account for storing files */
3625 case 550: /* File action not taken. File not found or no access. */
3626 case 551: /* Requested action aborted. Page type unknown */
3627 case 552: /* Action aborted. Exceeded storage allocation */
3628 case 553: /* Action not taken. File name not allowed. */
3630 default:
3631 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3632 break;
3635 INTERNET_SetLastError(dwCode);
3636 return dwCode;