wininet: Make the reference count of WININETHANDLEHEADER thread-safe by using Interlo...
[wine/wine64.git] / dlls / wininet / ftp.c
blobb9d305e71ec28ecd6056b4445e8408cdba662e27
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 _WININETFTPSESSIONW WININETFTPSESSIONW;
66 typedef struct
68 WININETHANDLEHEADER hdr;
69 WININETFTPSESSIONW *lpFtpSession;
70 BOOL session_deleted;
71 int nDataSocket;
72 } WININETFTPFILE, *LPWININETFTPFILE;
74 typedef struct _WININETFTPSESSIONW
76 WININETHANDLEHEADER hdr;
77 WININETAPPINFOW *lpAppInfo;
78 int sndSocket;
79 int lstnSocket;
80 int pasvSocket; /* data socket connected by us in case of passive FTP */
81 LPWININETFTPFILE download_in_progress;
82 struct sockaddr_in socketAddress;
83 struct sockaddr_in lstnSocketAddress;
84 LPWSTR lpszPassword;
85 LPWSTR lpszUserName;
86 } *LPWININETFTPSESSIONW;
88 typedef struct
90 BOOL bIsDirectory;
91 LPWSTR lpszName;
92 DWORD nSize;
93 struct tm tmLastModified;
94 unsigned short permissions;
95 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
97 typedef struct
99 WININETHANDLEHEADER hdr;
100 WININETFTPSESSIONW *lpFtpSession;
101 DWORD index;
102 DWORD size;
103 LPFILEPROPERTIESW lpafp;
104 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
106 #define DATA_PACKET_SIZE 0x2000
107 #define szCRLF "\r\n"
108 #define MAX_BACKLOG 5
110 /* Testing shows that Windows only accepts dwFlags where the last
111 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
113 #define FTP_CONDITION_MASK 0x0007
115 typedef enum {
116 /* FTP commands with arguments. */
117 FTP_CMD_ACCT,
118 FTP_CMD_CWD,
119 FTP_CMD_DELE,
120 FTP_CMD_MKD,
121 FTP_CMD_PASS,
122 FTP_CMD_PORT,
123 FTP_CMD_RETR,
124 FTP_CMD_RMD,
125 FTP_CMD_RNFR,
126 FTP_CMD_RNTO,
127 FTP_CMD_STOR,
128 FTP_CMD_TYPE,
129 FTP_CMD_USER,
130 FTP_CMD_SIZE,
132 /* FTP commands without arguments. */
133 FTP_CMD_ABOR,
134 FTP_CMD_LIST,
135 FTP_CMD_NLST,
136 FTP_CMD_PASV,
137 FTP_CMD_PWD,
138 FTP_CMD_QUIT,
139 } FTP_COMMAND;
141 static const CHAR *const szFtpCommands[] = {
142 "ACCT",
143 "CWD",
144 "DELE",
145 "MKD",
146 "PASS",
147 "PORT",
148 "RETR",
149 "RMD",
150 "RNFR",
151 "RNTO",
152 "STOR",
153 "TYPE",
154 "USER",
155 "SIZE",
156 "ABOR",
157 "LIST",
158 "NLST",
159 "PASV",
160 "PWD",
161 "QUIT",
164 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
165 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
167 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
168 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext);
169 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
170 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket);
171 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile);
172 static INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD_PTR dwContext);
173 static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
174 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile);
175 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs);
176 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs);
177 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs);
178 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs);
179 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType);
180 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs);
181 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs);
182 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs);
183 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
184 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
185 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
186 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
187 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
188 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
189 static DWORD FTP_SetResponseError(DWORD dwResponse);
190 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
191 static BOOL FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
192 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
193 static BOOL FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
194 static BOOL FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
195 static HINTERNET FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
196 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
197 static BOOL FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
198 LPDWORD lpdwCurrentDirectory);
199 static BOOL FTP_FtpRenameFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest);
200 static BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
201 static BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName);
202 static HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName,
203 DWORD fdwAccess, DWORD dwFlags, DWORD_PTR dwContext);
204 static BOOL FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
205 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
206 DWORD_PTR dwContext);
209 /***********************************************************************
210 * FtpPutFileA (WININET.@)
212 * Uploads a file to the FTP server
214 * RETURNS
215 * TRUE on success
216 * FALSE on failure
219 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
220 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
222 LPWSTR lpwzLocalFile;
223 LPWSTR lpwzNewRemoteFile;
224 BOOL ret;
226 lpwzLocalFile = lpszLocalFile?WININET_strdup_AtoW(lpszLocalFile):NULL;
227 lpwzNewRemoteFile = lpszNewRemoteFile?WININET_strdup_AtoW(lpszNewRemoteFile):NULL;
228 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
229 dwFlags, dwContext);
230 HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
231 HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
232 return ret;
235 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
237 struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
238 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
240 TRACE("%p\n", lpwfs);
242 FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
243 req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
245 HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
246 HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
249 /***********************************************************************
250 * FtpPutFileW (WININET.@)
252 * Uploads a file to the FTP server
254 * RETURNS
255 * TRUE on success
256 * FALSE on failure
259 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
260 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
262 LPWININETFTPSESSIONW lpwfs;
263 LPWININETAPPINFOW hIC = NULL;
264 BOOL r = FALSE;
266 if (!lpszLocalFile || !lpszNewRemoteFile)
268 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
269 return FALSE;
272 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
273 if (!lpwfs)
275 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
276 return FALSE;
279 if (WH_HFTPSESSION != lpwfs->hdr.htype)
281 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
282 goto lend;
285 if (lpwfs->download_in_progress != NULL)
287 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
288 goto lend;
291 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
293 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
294 goto lend;
297 hIC = lpwfs->lpAppInfo;
298 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
300 WORKREQUEST workRequest;
301 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
303 workRequest.asyncproc = AsyncFtpPutFileProc;
304 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
305 req->lpszLocalFile = WININET_strdupW(lpszLocalFile);
306 req->lpszNewRemoteFile = WININET_strdupW(lpszNewRemoteFile);
307 req->dwFlags = dwFlags;
308 req->dwContext = dwContext;
310 r = INTERNET_AsyncCall(&workRequest);
312 else
314 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
315 lpszNewRemoteFile, dwFlags, dwContext);
318 lend:
319 WININET_Release( &lpwfs->hdr );
321 return r;
324 /***********************************************************************
325 * FTP_FtpPutFileW (Internal)
327 * Uploads a file to the FTP server
329 * RETURNS
330 * TRUE on success
331 * FALSE on failure
334 static BOOL FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
335 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
337 HANDLE hFile;
338 BOOL bSuccess = FALSE;
339 LPWININETAPPINFOW hIC = NULL;
340 INT nResCode;
342 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
344 /* Clear any error information */
345 INTERNET_SetLastError(0);
347 /* Open file to be uploaded */
348 if (INVALID_HANDLE_VALUE ==
349 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
350 /* Let CreateFile set the appropriate error */
351 return FALSE;
353 hIC = lpwfs->lpAppInfo;
355 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
357 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
359 INT nDataSocket;
361 /* Get data socket to server */
362 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
364 FTP_SendData(lpwfs, nDataSocket, hFile);
365 closesocket(nDataSocket);
366 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
367 if (nResCode)
369 if (nResCode == 226)
370 bSuccess = TRUE;
371 else
372 FTP_SetResponseError(nResCode);
377 if (lpwfs->lstnSocket != -1)
378 closesocket(lpwfs->lstnSocket);
380 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
382 INTERNET_ASYNC_RESULT iar;
384 iar.dwResult = (DWORD)bSuccess;
385 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
386 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
387 &iar, sizeof(INTERNET_ASYNC_RESULT));
390 CloseHandle(hFile);
392 return bSuccess;
396 /***********************************************************************
397 * FtpSetCurrentDirectoryA (WININET.@)
399 * Change the working directory on the FTP server
401 * RETURNS
402 * TRUE on success
403 * FALSE on failure
406 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
408 LPWSTR lpwzDirectory;
409 BOOL ret;
411 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
412 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
413 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
414 return ret;
418 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
420 struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
421 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
423 TRACE("%p\n", lpwfs);
425 FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
426 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
429 /***********************************************************************
430 * FtpSetCurrentDirectoryW (WININET.@)
432 * Change the working directory on the FTP server
434 * RETURNS
435 * TRUE on success
436 * FALSE on failure
439 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
441 LPWININETFTPSESSIONW lpwfs = NULL;
442 LPWININETAPPINFOW hIC = NULL;
443 BOOL r = FALSE;
445 if (!lpszDirectory)
447 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
448 goto lend;
451 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
452 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
454 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
455 goto lend;
458 if (lpwfs->download_in_progress != NULL)
460 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
461 goto lend;
464 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
466 hIC = lpwfs->lpAppInfo;
467 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
469 WORKREQUEST workRequest;
470 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
472 workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
473 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
474 req = &workRequest.u.FtpSetCurrentDirectoryW;
475 req->lpszDirectory = WININET_strdupW(lpszDirectory);
477 r = INTERNET_AsyncCall(&workRequest);
479 else
481 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
484 lend:
485 if( lpwfs )
486 WININET_Release( &lpwfs->hdr );
488 return r;
492 /***********************************************************************
493 * FTP_FtpSetCurrentDirectoryW (Internal)
495 * Change the working directory on the FTP server
497 * RETURNS
498 * TRUE on success
499 * FALSE on failure
502 static BOOL FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
504 INT nResCode;
505 LPWININETAPPINFOW hIC = NULL;
506 DWORD bSuccess = FALSE;
508 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
510 /* Clear any error information */
511 INTERNET_SetLastError(0);
513 hIC = lpwfs->lpAppInfo;
514 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
515 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
516 goto lend;
518 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
520 if (nResCode)
522 if (nResCode == 250)
523 bSuccess = TRUE;
524 else
525 FTP_SetResponseError(nResCode);
528 lend:
529 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
531 INTERNET_ASYNC_RESULT iar;
533 iar.dwResult = bSuccess;
534 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
535 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
536 &iar, sizeof(INTERNET_ASYNC_RESULT));
538 return bSuccess;
542 /***********************************************************************
543 * FtpCreateDirectoryA (WININET.@)
545 * Create new directory on the FTP server
547 * RETURNS
548 * TRUE on success
549 * FALSE on failure
552 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
554 LPWSTR lpwzDirectory;
555 BOOL ret;
557 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
558 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
559 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
560 return ret;
564 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
566 struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
567 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
569 TRACE(" %p\n", lpwfs);
571 FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
572 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
575 /***********************************************************************
576 * FtpCreateDirectoryW (WININET.@)
578 * Create new directory on the FTP server
580 * RETURNS
581 * TRUE on success
582 * FALSE on failure
585 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
587 LPWININETFTPSESSIONW lpwfs;
588 LPWININETAPPINFOW hIC = NULL;
589 BOOL r = FALSE;
591 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
592 if (!lpwfs)
594 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
595 return FALSE;
598 if (WH_HFTPSESSION != lpwfs->hdr.htype)
600 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
601 goto lend;
604 if (lpwfs->download_in_progress != NULL)
606 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
607 goto lend;
610 if (!lpszDirectory)
612 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
613 goto lend;
616 hIC = lpwfs->lpAppInfo;
617 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
619 WORKREQUEST workRequest;
620 struct WORKREQ_FTPCREATEDIRECTORYW *req;
622 workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
623 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
624 req = &workRequest.u.FtpCreateDirectoryW;
625 req->lpszDirectory = WININET_strdupW(lpszDirectory);
627 r = INTERNET_AsyncCall(&workRequest);
629 else
631 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
633 lend:
634 WININET_Release( &lpwfs->hdr );
636 return r;
640 /***********************************************************************
641 * FTP_FtpCreateDirectoryW (Internal)
643 * Create new directory on the FTP server
645 * RETURNS
646 * TRUE on success
647 * FALSE on failure
650 static BOOL FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
652 INT nResCode;
653 BOOL bSuccess = FALSE;
654 LPWININETAPPINFOW hIC = NULL;
656 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
658 /* Clear any error information */
659 INTERNET_SetLastError(0);
661 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
662 goto lend;
664 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
665 if (nResCode)
667 if (nResCode == 257)
668 bSuccess = TRUE;
669 else
670 FTP_SetResponseError(nResCode);
673 lend:
674 hIC = lpwfs->lpAppInfo;
675 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
677 INTERNET_ASYNC_RESULT iar;
679 iar.dwResult = (DWORD)bSuccess;
680 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
681 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
682 &iar, sizeof(INTERNET_ASYNC_RESULT));
685 return bSuccess;
688 /***********************************************************************
689 * FtpFindFirstFileA (WININET.@)
691 * Search the specified directory
693 * RETURNS
694 * HINTERNET on success
695 * NULL on failure
698 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
699 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
701 LPWSTR lpwzSearchFile;
702 WIN32_FIND_DATAW wfd;
703 LPWIN32_FIND_DATAW lpFindFileDataW;
704 HINTERNET ret;
706 lpwzSearchFile = lpszSearchFile?WININET_strdup_AtoW(lpszSearchFile):NULL;
707 lpFindFileDataW = lpFindFileData?&wfd:NULL;
708 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
709 HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
711 if(lpFindFileData) {
712 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
714 return ret;
718 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
720 struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
721 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
723 TRACE("%p\n", lpwfs);
725 FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
726 req->lpFindFileData, req->dwFlags, req->dwContext);
727 HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
730 /***********************************************************************
731 * FtpFindFirstFileW (WININET.@)
733 * Search the specified directory
735 * RETURNS
736 * HINTERNET on success
737 * NULL on failure
740 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
741 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
743 LPWININETFTPSESSIONW lpwfs;
744 LPWININETAPPINFOW hIC = NULL;
745 HINTERNET r = NULL;
747 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
748 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
750 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
751 goto lend;
754 if (lpwfs->download_in_progress != NULL)
756 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
757 goto lend;
760 hIC = lpwfs->lpAppInfo;
761 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
763 WORKREQUEST workRequest;
764 struct WORKREQ_FTPFINDFIRSTFILEW *req;
766 workRequest.asyncproc = AsyncFtpFindFirstFileProc;
767 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
768 req = &workRequest.u.FtpFindFirstFileW;
769 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : WININET_strdupW(lpszSearchFile);
770 req->lpFindFileData = lpFindFileData;
771 req->dwFlags = dwFlags;
772 req->dwContext= dwContext;
774 INTERNET_AsyncCall(&workRequest);
775 r = NULL;
777 else
779 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
780 dwFlags, dwContext);
782 lend:
783 if( lpwfs )
784 WININET_Release( &lpwfs->hdr );
786 return r;
790 /***********************************************************************
791 * FTP_FtpFindFirstFileW (Internal)
793 * Search the specified directory
795 * RETURNS
796 * HINTERNET on success
797 * NULL on failure
800 static HINTERNET FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
801 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
803 INT nResCode;
804 LPWININETAPPINFOW hIC = NULL;
805 HINTERNET hFindNext = NULL;
807 TRACE("\n");
809 /* Clear any error information */
810 INTERNET_SetLastError(0);
812 if (!FTP_InitListenSocket(lpwfs))
813 goto lend;
815 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
816 goto lend;
818 if (!FTP_SendPortOrPasv(lpwfs))
819 goto lend;
821 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
822 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
823 goto lend;
825 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
826 if (nResCode)
828 if (nResCode == 125 || nResCode == 150)
830 INT nDataSocket;
832 /* Get data socket to server */
833 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
835 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
836 closesocket(nDataSocket);
837 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
838 if (nResCode != 226 && nResCode != 250)
839 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
842 else
843 FTP_SetResponseError(nResCode);
846 lend:
847 if (lpwfs->lstnSocket != -1)
848 closesocket(lpwfs->lstnSocket);
850 hIC = lpwfs->lpAppInfo;
851 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
853 INTERNET_ASYNC_RESULT iar;
855 if (hFindNext)
857 iar.dwResult = (DWORD)hFindNext;
858 iar.dwError = ERROR_SUCCESS;
859 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
860 &iar, sizeof(INTERNET_ASYNC_RESULT));
863 iar.dwResult = (DWORD)hFindNext;
864 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
865 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
866 &iar, sizeof(INTERNET_ASYNC_RESULT));
869 return hFindNext;
873 /***********************************************************************
874 * FtpGetCurrentDirectoryA (WININET.@)
876 * Retrieves the current directory
878 * RETURNS
879 * TRUE on success
880 * FALSE on failure
883 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
884 LPDWORD lpdwCurrentDirectory)
886 WCHAR *dir = NULL;
887 DWORD len;
888 BOOL ret;
890 if(lpdwCurrentDirectory) {
891 len = *lpdwCurrentDirectory;
892 if(lpszCurrentDirectory)
894 dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
895 if (NULL == dir)
897 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
898 return FALSE;
902 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
904 if (ret && lpszCurrentDirectory)
905 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
907 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
908 HeapFree(GetProcessHeap(), 0, dir);
909 return ret;
913 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
915 struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
916 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
918 TRACE("%p\n", lpwfs);
920 FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
923 /***********************************************************************
924 * FtpGetCurrentDirectoryW (WININET.@)
926 * Retrieves the current directory
928 * RETURNS
929 * TRUE on success
930 * FALSE on failure
933 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
934 LPDWORD lpdwCurrentDirectory)
936 LPWININETFTPSESSIONW lpwfs;
937 LPWININETAPPINFOW hIC = NULL;
938 BOOL r = FALSE;
940 TRACE("len(%d)\n", *lpdwCurrentDirectory);
942 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
943 if (NULL == lpwfs)
945 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
946 goto lend;
949 if (WH_HFTPSESSION != lpwfs->hdr.htype)
951 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
952 goto lend;
955 if (!lpdwCurrentDirectory)
957 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
958 goto lend;
961 if (lpszCurrentDirectory == NULL)
963 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
964 goto lend;
967 if (lpwfs->download_in_progress != NULL)
969 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
970 goto lend;
973 hIC = lpwfs->lpAppInfo;
974 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
976 WORKREQUEST workRequest;
977 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
979 workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
980 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
981 req = &workRequest.u.FtpGetCurrentDirectoryW;
982 req->lpszDirectory = lpszCurrentDirectory;
983 req->lpdwDirectory = lpdwCurrentDirectory;
985 r = INTERNET_AsyncCall(&workRequest);
987 else
989 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
990 lpdwCurrentDirectory);
993 lend:
994 if( lpwfs )
995 WININET_Release( &lpwfs->hdr );
997 return r;
1001 /***********************************************************************
1002 * FTP_FtpGetCurrentDirectoryW (Internal)
1004 * Retrieves the current directory
1006 * RETURNS
1007 * TRUE on success
1008 * FALSE on failure
1011 static BOOL FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
1012 LPDWORD lpdwCurrentDirectory)
1014 INT nResCode;
1015 LPWININETAPPINFOW hIC = NULL;
1016 DWORD bSuccess = FALSE;
1018 TRACE("len(%d)\n", *lpdwCurrentDirectory);
1020 /* Clear any error information */
1021 INTERNET_SetLastError(0);
1023 hIC = lpwfs->lpAppInfo;
1024 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1025 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1026 goto lend;
1028 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1029 if (nResCode)
1031 if (nResCode == 257) /* Extract directory name */
1033 DWORD firstpos, lastpos, len;
1034 LPWSTR lpszResponseBuffer = WININET_strdup_AtoW(INTERNET_GetResponseBuffer());
1036 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1038 if ('"' == lpszResponseBuffer[lastpos])
1040 if (!firstpos)
1041 firstpos = lastpos;
1042 else
1043 break;
1046 len = lastpos - firstpos;
1047 if (*lpdwCurrentDirectory >= len)
1049 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1050 lpszCurrentDirectory[len - 1] = 0;
1051 *lpdwCurrentDirectory = len;
1052 bSuccess = TRUE;
1054 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1056 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
1058 else
1059 FTP_SetResponseError(nResCode);
1062 lend:
1063 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1065 INTERNET_ASYNC_RESULT iar;
1067 iar.dwResult = bSuccess;
1068 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1069 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1070 &iar, sizeof(INTERNET_ASYNC_RESULT));
1073 return bSuccess;
1076 /***********************************************************************
1077 * FtpOpenFileA (WININET.@)
1079 * Open a remote file for writing or reading
1081 * RETURNS
1082 * HINTERNET handle on success
1083 * NULL on failure
1086 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1087 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1088 DWORD_PTR dwContext)
1090 LPWSTR lpwzFileName;
1091 HINTERNET ret;
1093 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1094 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1095 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1096 return ret;
1100 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1102 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1103 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1105 TRACE("%p\n", lpwfs);
1107 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1108 req->dwAccess, req->dwFlags, req->dwContext);
1109 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1112 /***********************************************************************
1113 * FtpOpenFileW (WININET.@)
1115 * Open a remote file for writing or reading
1117 * RETURNS
1118 * HINTERNET handle on success
1119 * NULL on failure
1122 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1123 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1124 DWORD_PTR dwContext)
1126 LPWININETFTPSESSIONW lpwfs;
1127 LPWININETAPPINFOW hIC = NULL;
1128 HINTERNET r = NULL;
1130 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1131 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1133 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1134 if (!lpwfs)
1136 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1137 return FALSE;
1140 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1142 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1143 goto lend;
1146 if ((!lpszFileName) ||
1147 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1148 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1150 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1151 goto lend;
1154 if (lpwfs->download_in_progress != NULL)
1156 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1157 goto lend;
1160 hIC = lpwfs->lpAppInfo;
1161 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1163 WORKREQUEST workRequest;
1164 struct WORKREQ_FTPOPENFILEW *req;
1166 workRequest.asyncproc = AsyncFtpOpenFileProc;
1167 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1168 req = &workRequest.u.FtpOpenFileW;
1169 req->lpszFilename = WININET_strdupW(lpszFileName);
1170 req->dwAccess = fdwAccess;
1171 req->dwFlags = dwFlags;
1172 req->dwContext = dwContext;
1174 INTERNET_AsyncCall(&workRequest);
1175 r = NULL;
1177 else
1179 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1182 lend:
1183 WININET_Release( &lpwfs->hdr );
1185 return r;
1189 /***********************************************************************
1190 * FTPFILE_Destroy(internal)
1192 * Closes the file transfer handle. This also 'cleans' the data queue of
1193 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1196 static void FTPFILE_Destroy(WININETHANDLEHEADER *hdr)
1198 LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
1199 LPWININETFTPSESSIONW lpwfs = lpwh->lpFtpSession;
1200 INT nResCode;
1202 TRACE("\n");
1204 WININET_Release(&lpwh->lpFtpSession->hdr);
1206 if (!lpwh->session_deleted)
1207 lpwfs->download_in_progress = NULL;
1209 if (lpwh->nDataSocket != -1)
1210 closesocket(lpwh->nDataSocket);
1212 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1213 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1215 HeapFree(GetProcessHeap(), 0, lpwh);
1218 static DWORD FTPFILE_ReadFile(WININETHANDLEHEADER *hdr, void *buffer, DWORD size, DWORD *read)
1220 WININETFTPFILE *file = (WININETFTPFILE*)hdr;
1221 int res;
1223 if (file->nDataSocket == -1)
1224 return ERROR_INTERNET_DISCONNECTED;
1226 /* FIXME: FTP should use NETCON_ stuff */
1227 res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1228 *read = res>0 ? res : 0;
1230 return res>=0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME*/
1233 static BOOL FTPFILE_WriteFile(WININETHANDLEHEADER *hdr, const void *buffer, DWORD size, DWORD *written)
1235 LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
1236 int res;
1238 res = send(lpwh->nDataSocket, buffer, size, 0);
1240 *written = res>0 ? res : 0;
1241 return res >= 0;
1244 static const HANDLEHEADERVtbl FTPFILEVtbl = {
1245 FTPFILE_Destroy,
1246 NULL,
1247 NULL,
1248 FTPFILE_ReadFile,
1249 NULL,
1250 FTPFILE_WriteFile,
1251 NULL,
1252 NULL
1255 /***********************************************************************
1256 * FTP_FtpOpenFileW (Internal)
1258 * Open a remote file for writing or reading
1260 * RETURNS
1261 * HINTERNET handle on success
1262 * NULL on failure
1265 HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
1266 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1267 DWORD_PTR dwContext)
1269 INT nDataSocket;
1270 BOOL bSuccess = FALSE;
1271 LPWININETFTPFILE lpwh = NULL;
1272 LPWININETAPPINFOW hIC = NULL;
1273 HINTERNET handle = NULL;
1275 TRACE("\n");
1277 /* Clear any error information */
1278 INTERNET_SetLastError(0);
1280 if (GENERIC_READ == fdwAccess)
1282 /* Set up socket to retrieve data */
1283 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1285 else if (GENERIC_WRITE == fdwAccess)
1287 /* Set up socket to send data */
1288 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1291 /* Get data socket to server */
1292 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1294 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPFILE));
1295 lpwh->hdr.htype = WH_HFILE;
1296 lpwh->hdr.vtbl = &FTPFILEVtbl;
1297 lpwh->hdr.dwFlags = dwFlags;
1298 lpwh->hdr.dwContext = dwContext;
1299 lpwh->hdr.refs = 1;
1300 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1301 lpwh->nDataSocket = nDataSocket;
1302 lpwh->session_deleted = FALSE;
1304 WININET_AddRef( &lpwfs->hdr );
1305 lpwh->lpFtpSession = lpwfs;
1306 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1308 handle = WININET_AllocHandle( &lpwh->hdr );
1309 if( !handle )
1310 goto lend;
1312 /* Indicate that a download is currently in progress */
1313 lpwfs->download_in_progress = lpwh;
1316 if (lpwfs->lstnSocket != -1)
1317 closesocket(lpwfs->lstnSocket);
1319 hIC = lpwfs->lpAppInfo;
1320 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1322 INTERNET_ASYNC_RESULT iar;
1324 if (lpwh)
1326 iar.dwResult = (DWORD)handle;
1327 iar.dwError = ERROR_SUCCESS;
1328 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1329 &iar, sizeof(INTERNET_ASYNC_RESULT));
1332 iar.dwResult = (DWORD)bSuccess;
1333 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1334 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1335 &iar, sizeof(INTERNET_ASYNC_RESULT));
1338 lend:
1339 if( lpwh )
1340 WININET_Release( &lpwh->hdr );
1342 return handle;
1346 /***********************************************************************
1347 * FtpGetFileA (WININET.@)
1349 * Retrieve file from the FTP server
1351 * RETURNS
1352 * TRUE on success
1353 * FALSE on failure
1356 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1357 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1358 DWORD_PTR dwContext)
1360 LPWSTR lpwzRemoteFile;
1361 LPWSTR lpwzNewFile;
1362 BOOL ret;
1364 lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
1365 lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
1366 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1367 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1368 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1369 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1370 return ret;
1374 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1376 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1377 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1379 TRACE("%p\n", lpwfs);
1381 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1382 req->lpszNewFile, req->fFailIfExists,
1383 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1384 HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1385 HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1389 /***********************************************************************
1390 * FtpGetFileW (WININET.@)
1392 * Retrieve file from the FTP server
1394 * RETURNS
1395 * TRUE on success
1396 * FALSE on failure
1399 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1400 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1401 DWORD_PTR dwContext)
1403 LPWININETFTPSESSIONW lpwfs;
1404 LPWININETAPPINFOW hIC = NULL;
1405 BOOL r = FALSE;
1407 if (!lpszRemoteFile || !lpszNewFile)
1409 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1410 return FALSE;
1413 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
1414 if (!lpwfs)
1416 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1417 return FALSE;
1420 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1422 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1423 goto lend;
1426 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1428 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1429 goto lend;
1432 if (lpwfs->download_in_progress != NULL)
1434 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1435 goto lend;
1438 hIC = lpwfs->lpAppInfo;
1439 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1441 WORKREQUEST workRequest;
1442 struct WORKREQ_FTPGETFILEW *req;
1444 workRequest.asyncproc = AsyncFtpGetFileProc;
1445 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1446 req = &workRequest.u.FtpGetFileW;
1447 req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
1448 req->lpszNewFile = WININET_strdupW(lpszNewFile);
1449 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1450 req->fFailIfExists = fFailIfExists;
1451 req->dwFlags = dwInternetFlags;
1452 req->dwContext = dwContext;
1454 r = INTERNET_AsyncCall(&workRequest);
1456 else
1458 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1459 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1462 lend:
1463 WININET_Release( &lpwfs->hdr );
1465 return r;
1469 /***********************************************************************
1470 * FTP_FtpGetFileW (Internal)
1472 * Retrieve file from the FTP server
1474 * RETURNS
1475 * TRUE on success
1476 * FALSE on failure
1479 static BOOL FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1480 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1481 DWORD_PTR dwContext)
1483 BOOL bSuccess = FALSE;
1484 HANDLE hFile;
1485 LPWININETAPPINFOW hIC = NULL;
1487 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1489 /* Clear any error information */
1490 INTERNET_SetLastError(0);
1492 /* Ensure we can write to lpszNewfile by opening it */
1493 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1494 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1495 if (INVALID_HANDLE_VALUE == hFile)
1496 return FALSE;
1498 /* Set up socket to retrieve data */
1499 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1501 INT nDataSocket;
1503 /* Get data socket to server */
1504 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1506 INT nResCode;
1508 /* Receive data */
1509 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1510 closesocket(nDataSocket);
1512 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1513 if (nResCode)
1515 if (nResCode == 226)
1516 bSuccess = TRUE;
1517 else
1518 FTP_SetResponseError(nResCode);
1523 if (lpwfs->lstnSocket != -1)
1524 closesocket(lpwfs->lstnSocket);
1526 CloseHandle(hFile);
1528 hIC = lpwfs->lpAppInfo;
1529 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1531 INTERNET_ASYNC_RESULT iar;
1533 iar.dwResult = (DWORD)bSuccess;
1534 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1535 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1536 &iar, sizeof(INTERNET_ASYNC_RESULT));
1539 return bSuccess;
1542 /***********************************************************************
1543 * FtpGetFileSize (WININET.@)
1545 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1547 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1549 if (lpdwFileSizeHigh)
1550 *lpdwFileSizeHigh = 0;
1552 return 0;
1555 /***********************************************************************
1556 * FtpDeleteFileA (WININET.@)
1558 * Delete a file on the ftp server
1560 * RETURNS
1561 * TRUE on success
1562 * FALSE on failure
1565 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1567 LPWSTR lpwzFileName;
1568 BOOL ret;
1570 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1571 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1572 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1573 return ret;
1576 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1578 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1579 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1581 TRACE("%p\n", lpwfs);
1583 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1584 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1587 /***********************************************************************
1588 * FtpDeleteFileW (WININET.@)
1590 * Delete a file on the ftp server
1592 * RETURNS
1593 * TRUE on success
1594 * FALSE on failure
1597 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1599 LPWININETFTPSESSIONW lpwfs;
1600 LPWININETAPPINFOW hIC = NULL;
1601 BOOL r = FALSE;
1603 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1604 if (!lpwfs)
1606 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1607 return FALSE;
1610 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1612 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1613 goto lend;
1616 if (lpwfs->download_in_progress != NULL)
1618 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1619 goto lend;
1622 if (!lpszFileName)
1624 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1625 goto lend;
1628 hIC = lpwfs->lpAppInfo;
1629 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1631 WORKREQUEST workRequest;
1632 struct WORKREQ_FTPDELETEFILEW *req;
1634 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1635 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1636 req = &workRequest.u.FtpDeleteFileW;
1637 req->lpszFilename = WININET_strdupW(lpszFileName);
1639 r = INTERNET_AsyncCall(&workRequest);
1641 else
1643 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1646 lend:
1647 WININET_Release( &lpwfs->hdr );
1649 return r;
1652 /***********************************************************************
1653 * FTP_FtpDeleteFileW (Internal)
1655 * Delete a file on the ftp server
1657 * RETURNS
1658 * TRUE on success
1659 * FALSE on failure
1662 BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName)
1664 INT nResCode;
1665 BOOL bSuccess = FALSE;
1666 LPWININETAPPINFOW hIC = NULL;
1668 TRACE("%p\n", lpwfs);
1670 /* Clear any error information */
1671 INTERNET_SetLastError(0);
1673 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1674 goto lend;
1676 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1677 if (nResCode)
1679 if (nResCode == 250)
1680 bSuccess = TRUE;
1681 else
1682 FTP_SetResponseError(nResCode);
1684 lend:
1685 hIC = lpwfs->lpAppInfo;
1686 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1688 INTERNET_ASYNC_RESULT iar;
1690 iar.dwResult = (DWORD)bSuccess;
1691 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1692 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1693 &iar, sizeof(INTERNET_ASYNC_RESULT));
1696 return bSuccess;
1700 /***********************************************************************
1701 * FtpRemoveDirectoryA (WININET.@)
1703 * Remove a directory on the ftp server
1705 * RETURNS
1706 * TRUE on success
1707 * FALSE on failure
1710 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1712 LPWSTR lpwzDirectory;
1713 BOOL ret;
1715 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
1716 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1717 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1718 return ret;
1721 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1723 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1724 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1726 TRACE("%p\n", lpwfs);
1728 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1729 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1732 /***********************************************************************
1733 * FtpRemoveDirectoryW (WININET.@)
1735 * Remove a directory on the ftp server
1737 * RETURNS
1738 * TRUE on success
1739 * FALSE on failure
1742 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1744 LPWININETFTPSESSIONW lpwfs;
1745 LPWININETAPPINFOW hIC = NULL;
1746 BOOL r = FALSE;
1748 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1749 if (!lpwfs)
1751 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1752 return FALSE;
1755 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1757 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1758 goto lend;
1761 if (lpwfs->download_in_progress != NULL)
1763 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1764 goto lend;
1767 if (!lpszDirectory)
1769 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1770 goto lend;
1773 hIC = lpwfs->lpAppInfo;
1774 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1776 WORKREQUEST workRequest;
1777 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1779 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1780 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1781 req = &workRequest.u.FtpRemoveDirectoryW;
1782 req->lpszDirectory = WININET_strdupW(lpszDirectory);
1784 r = INTERNET_AsyncCall(&workRequest);
1786 else
1788 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1791 lend:
1792 WININET_Release( &lpwfs->hdr );
1794 return r;
1797 /***********************************************************************
1798 * FTP_FtpRemoveDirectoryW (Internal)
1800 * Remove a directory on the ftp server
1802 * RETURNS
1803 * TRUE on success
1804 * FALSE on failure
1807 BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
1809 INT nResCode;
1810 BOOL bSuccess = FALSE;
1811 LPWININETAPPINFOW hIC = NULL;
1813 TRACE("\n");
1815 /* Clear any error information */
1816 INTERNET_SetLastError(0);
1818 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1819 goto lend;
1821 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1822 if (nResCode)
1824 if (nResCode == 250)
1825 bSuccess = TRUE;
1826 else
1827 FTP_SetResponseError(nResCode);
1830 lend:
1831 hIC = lpwfs->lpAppInfo;
1832 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1834 INTERNET_ASYNC_RESULT iar;
1836 iar.dwResult = (DWORD)bSuccess;
1837 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1838 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1839 &iar, sizeof(INTERNET_ASYNC_RESULT));
1842 return bSuccess;
1846 /***********************************************************************
1847 * FtpRenameFileA (WININET.@)
1849 * Rename a file on the ftp server
1851 * RETURNS
1852 * TRUE on success
1853 * FALSE on failure
1856 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1858 LPWSTR lpwzSrc;
1859 LPWSTR lpwzDest;
1860 BOOL ret;
1862 lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL;
1863 lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL;
1864 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1865 HeapFree(GetProcessHeap(), 0, lpwzSrc);
1866 HeapFree(GetProcessHeap(), 0, lpwzDest);
1867 return ret;
1870 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
1872 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
1873 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1875 TRACE("%p\n", lpwfs);
1877 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
1878 HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
1879 HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
1882 /***********************************************************************
1883 * FtpRenameFileW (WININET.@)
1885 * Rename a file on the ftp server
1887 * RETURNS
1888 * TRUE on success
1889 * FALSE on failure
1892 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
1894 LPWININETFTPSESSIONW lpwfs;
1895 LPWININETAPPINFOW hIC = NULL;
1896 BOOL r = FALSE;
1898 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1899 if (!lpwfs)
1901 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1902 return FALSE;
1905 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1907 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1908 goto lend;
1911 if (lpwfs->download_in_progress != NULL)
1913 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1914 goto lend;
1917 if (!lpszSrc || !lpszDest)
1919 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1920 goto lend;
1923 hIC = lpwfs->lpAppInfo;
1924 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1926 WORKREQUEST workRequest;
1927 struct WORKREQ_FTPRENAMEFILEW *req;
1929 workRequest.asyncproc = AsyncFtpRenameFileProc;
1930 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1931 req = &workRequest.u.FtpRenameFileW;
1932 req->lpszSrcFile = WININET_strdupW(lpszSrc);
1933 req->lpszDestFile = WININET_strdupW(lpszDest);
1935 r = INTERNET_AsyncCall(&workRequest);
1937 else
1939 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
1942 lend:
1943 WININET_Release( &lpwfs->hdr );
1945 return r;
1948 /***********************************************************************
1949 * FTP_FtpRenameFileW (Internal)
1951 * Rename a file on the ftp server
1953 * RETURNS
1954 * TRUE on success
1955 * FALSE on failure
1958 BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs,
1959 LPCWSTR lpszSrc, LPCWSTR lpszDest)
1961 INT nResCode;
1962 BOOL bSuccess = FALSE;
1963 LPWININETAPPINFOW hIC = NULL;
1965 TRACE("\n");
1967 /* Clear any error information */
1968 INTERNET_SetLastError(0);
1970 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
1971 goto lend;
1973 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1974 if (nResCode == 350)
1976 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
1977 goto lend;
1979 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1982 if (nResCode == 250)
1983 bSuccess = TRUE;
1984 else
1985 FTP_SetResponseError(nResCode);
1987 lend:
1988 hIC = lpwfs->lpAppInfo;
1989 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1991 INTERNET_ASYNC_RESULT iar;
1993 iar.dwResult = (DWORD)bSuccess;
1994 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1995 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1996 &iar, sizeof(INTERNET_ASYNC_RESULT));
1999 return bSuccess;
2002 /***********************************************************************
2003 * FtpCommandA (WININET.@)
2005 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2006 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2008 BOOL r;
2009 WCHAR *cmdW;
2011 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2012 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2014 if (fExpectResponse)
2016 FIXME("data connection not supported\n");
2017 return FALSE;
2020 if (!lpszCommand || !lpszCommand[0])
2022 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2023 return FALSE;
2026 if (!(cmdW = WININET_strdup_AtoW(lpszCommand)))
2028 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2029 return FALSE;
2032 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2034 HeapFree(GetProcessHeap(), 0, cmdW);
2035 return r;
2038 /***********************************************************************
2039 * FtpCommandW (WININET.@)
2041 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2042 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2044 BOOL r = FALSE;
2045 LPWININETFTPSESSIONW lpwfs;
2046 LPSTR cmd = NULL;
2047 DWORD len, nBytesSent= 0;
2048 INT nResCode, nRC = 0;
2050 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2051 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2053 if (!lpszCommand || !lpszCommand[0])
2055 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2056 return FALSE;
2059 if (fExpectResponse)
2061 FIXME("data connection not supported\n");
2062 return FALSE;
2065 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
2066 if (!lpwfs)
2068 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2069 return FALSE;
2072 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2074 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2075 goto lend;
2078 if (lpwfs->download_in_progress != NULL)
2080 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2081 goto lend;
2084 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2085 if ((cmd = HeapAlloc(GetProcessHeap(), 0, len )))
2086 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2087 else
2089 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2090 goto lend;
2093 strcat(cmd, szCRLF);
2094 len--;
2096 TRACE("Sending (%s) len(%d)\n", cmd, len);
2097 while ((nBytesSent < len) && (nRC != -1))
2099 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2100 if (nRC != -1)
2102 nBytesSent += nRC;
2103 TRACE("Sent %d bytes\n", nRC);
2107 if (nBytesSent)
2109 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2110 if (nResCode > 0 && nResCode < 400)
2111 r = TRUE;
2112 else
2113 FTP_SetResponseError(nResCode);
2116 lend:
2117 WININET_Release( &lpwfs->hdr );
2118 HeapFree(GetProcessHeap(), 0, cmd);
2119 return r;
2123 /***********************************************************************
2124 * FTPSESSION_Destroy (internal)
2126 * Deallocate session handle
2128 static void FTPSESSION_Destroy(WININETHANDLEHEADER *hdr)
2130 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2132 TRACE("\n");
2134 WININET_Release(&lpwfs->lpAppInfo->hdr);
2136 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2137 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2138 HeapFree(GetProcessHeap(), 0, lpwfs);
2141 static void FTPSESSION_CloseConnection(WININETHANDLEHEADER *hdr)
2143 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2145 TRACE("\n");
2147 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2148 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2150 if (lpwfs->download_in_progress != NULL)
2151 lpwfs->download_in_progress->session_deleted = TRUE;
2153 if (lpwfs->sndSocket != -1)
2154 closesocket(lpwfs->sndSocket);
2156 if (lpwfs->lstnSocket != -1)
2157 closesocket(lpwfs->lstnSocket);
2159 if (lpwfs->pasvSocket != -1)
2160 closesocket(lpwfs->pasvSocket);
2162 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2163 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2166 static const HANDLEHEADERVtbl FTPSESSIONVtbl = {
2167 FTPSESSION_Destroy,
2168 FTPSESSION_CloseConnection,
2169 NULL,
2170 NULL,
2171 NULL,
2172 NULL,
2173 NULL,
2174 NULL
2178 /***********************************************************************
2179 * FTP_Connect (internal)
2181 * Connect to a ftp server
2183 * RETURNS
2184 * HINTERNET a session handle on success
2185 * NULL on failure
2187 * NOTES:
2189 * Windows uses 'anonymous' as the username, when given a NULL username
2190 * and a NULL password. The password is first looked up in:
2192 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2194 * If this entry is not present it uses the current username as the password.
2198 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
2199 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2200 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2201 DWORD dwInternalFlags)
2203 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2204 'M','i','c','r','o','s','o','f','t','\\',
2205 'W','i','n','d','o','w','s','\\',
2206 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2207 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2208 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2209 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2210 static const WCHAR szEmpty[] = {'\0'};
2211 struct sockaddr_in socketAddr;
2212 INT nsocket = -1;
2213 UINT sock_namelen;
2214 BOOL bSuccess = FALSE;
2215 LPWININETFTPSESSIONW lpwfs = NULL;
2216 HINTERNET handle = NULL;
2218 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2219 hIC, debugstr_w(lpszServerName),
2220 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2222 assert( hIC->hdr.htype == WH_HINIT );
2224 if (NULL == lpszUserName && NULL != lpszPassword)
2226 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2227 goto lerror;
2230 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
2231 if (NULL == lpwfs)
2233 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2234 goto lerror;
2237 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2238 nServerPort = INTERNET_DEFAULT_FTP_PORT;
2240 lpwfs->hdr.htype = WH_HFTPSESSION;
2241 lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
2242 lpwfs->hdr.dwFlags = dwFlags;
2243 lpwfs->hdr.dwContext = dwContext;
2244 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2245 lpwfs->hdr.refs = 1;
2246 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2247 lpwfs->download_in_progress = NULL;
2248 lpwfs->sndSocket = -1;
2249 lpwfs->lstnSocket = -1;
2250 lpwfs->pasvSocket = -1;
2252 WININET_AddRef( &hIC->hdr );
2253 lpwfs->lpAppInfo = hIC;
2254 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2256 handle = WININET_AllocHandle( &lpwfs->hdr );
2257 if( !handle )
2259 ERR("Failed to alloc handle\n");
2260 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2261 goto lerror;
2264 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2265 if(strchrW(hIC->lpszProxy, ' '))
2266 FIXME("Several proxies not implemented.\n");
2267 if(hIC->lpszProxyBypass)
2268 FIXME("Proxy bypass is ignored.\n");
2270 if ( !lpszUserName) {
2271 HKEY key;
2272 WCHAR szPassword[MAX_PATH];
2273 DWORD len = sizeof(szPassword);
2275 lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
2277 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2278 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2279 /* Nothing in the registry, get the username and use that as the password */
2280 if (!GetUserNameW(szPassword, &len)) {
2281 /* Should never get here, but use an empty password as failsafe */
2282 strcpyW(szPassword, szEmpty);
2285 RegCloseKey(key);
2287 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2288 lpwfs->lpszPassword = WININET_strdupW(szPassword);
2290 else {
2291 lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
2293 if (lpszPassword)
2294 lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
2295 else
2296 lpwfs->lpszPassword = WININET_strdupW(szEmpty);
2299 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2300 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2302 INTERNET_ASYNC_RESULT iar;
2304 iar.dwResult = (DWORD)handle;
2305 iar.dwError = ERROR_SUCCESS;
2307 SendAsyncCallback(&hIC->hdr, dwContext,
2308 INTERNET_STATUS_HANDLE_CREATED, &iar,
2309 sizeof(INTERNET_ASYNC_RESULT));
2312 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2313 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2315 if (!GetAddress(lpszServerName, nServerPort, &socketAddr))
2317 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2318 goto lerror;
2321 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2322 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2324 nsocket = socket(AF_INET,SOCK_STREAM,0);
2325 if (nsocket == -1)
2327 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2328 goto lerror;
2331 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2332 &socketAddr, sizeof(struct sockaddr_in));
2334 if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
2336 ERR("Unable to connect (%s)\n", strerror(errno));
2337 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2339 else
2341 TRACE("Connected to server\n");
2342 lpwfs->sndSocket = nsocket;
2343 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2344 &socketAddr, sizeof(struct sockaddr_in));
2346 sock_namelen = sizeof(lpwfs->socketAddress);
2347 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2349 if (FTP_ConnectToHost(lpwfs))
2351 TRACE("Successfully logged into server\n");
2352 bSuccess = TRUE;
2356 lerror:
2357 if (lpwfs) WININET_Release( &lpwfs->hdr );
2359 if (!bSuccess && handle)
2361 WININET_FreeHandle( handle );
2362 handle = NULL;
2365 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2367 INTERNET_ASYNC_RESULT iar;
2369 iar.dwResult = bSuccess ? (DWORD_PTR)lpwfs : 0;
2370 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2371 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2372 &iar, sizeof(INTERNET_ASYNC_RESULT));
2375 return handle;
2379 /***********************************************************************
2380 * FTP_ConnectToHost (internal)
2382 * Connect to a ftp server
2384 * RETURNS
2385 * TRUE on success
2386 * NULL on failure
2389 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
2391 INT nResCode;
2392 BOOL bSuccess = FALSE;
2394 TRACE("\n");
2395 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2397 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2398 goto lend;
2400 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2401 if (nResCode)
2403 /* Login successful... */
2404 if (nResCode == 230)
2405 bSuccess = TRUE;
2406 /* User name okay, need password... */
2407 else if (nResCode == 331)
2408 bSuccess = FTP_SendPassword(lpwfs);
2409 /* Need account for login... */
2410 else if (nResCode == 332)
2411 bSuccess = FTP_SendAccount(lpwfs);
2412 else
2413 FTP_SetResponseError(nResCode);
2416 TRACE("Returning %d\n", bSuccess);
2417 lend:
2418 return bSuccess;
2422 /***********************************************************************
2423 * FTP_SendCommandA (internal)
2425 * Send command to server
2427 * RETURNS
2428 * TRUE on success
2429 * NULL on failure
2432 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2433 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
2435 DWORD len;
2436 CHAR *buf;
2437 DWORD nBytesSent = 0;
2438 int nRC = 0;
2439 DWORD dwParamLen;
2441 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2443 if (lpfnStatusCB)
2445 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2448 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2449 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2450 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2452 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2453 return FALSE;
2455 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2456 dwParamLen ? lpszParam : "", szCRLF);
2458 TRACE("Sending (%s) len(%d)\n", buf, len);
2459 while((nBytesSent < len) && (nRC != -1))
2461 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2462 nBytesSent += nRC;
2465 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2467 if (lpfnStatusCB)
2469 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2470 &nBytesSent, sizeof(DWORD));
2473 TRACE("Sent %d bytes\n", nBytesSent);
2474 return (nRC != -1);
2477 /***********************************************************************
2478 * FTP_SendCommand (internal)
2480 * Send command to server
2482 * RETURNS
2483 * TRUE on success
2484 * NULL on failure
2487 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2488 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
2490 BOOL ret;
2491 LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
2492 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2493 HeapFree(GetProcessHeap(), 0, lpszParamA);
2494 return ret;
2497 /***********************************************************************
2498 * FTP_ReceiveResponse (internal)
2500 * Receive response from server
2502 * RETURNS
2503 * Reply code on success
2504 * 0 on failure
2507 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD_PTR dwContext)
2509 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2510 DWORD nRecv;
2511 INT rc = 0;
2512 char firstprefix[5];
2513 BOOL multiline = FALSE;
2514 LPWININETAPPINFOW hIC = NULL;
2516 TRACE("socket(%d)\n", lpwfs->sndSocket);
2518 hIC = lpwfs->lpAppInfo;
2519 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2521 while(1)
2523 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2524 goto lerror;
2526 if (nRecv >= 3)
2528 if(!multiline)
2530 if(lpszResponse[3] != '-')
2531 break;
2532 else
2533 { /* Start of multiline response. Loop until we get "nnn " */
2534 multiline = TRUE;
2535 memcpy(firstprefix, lpszResponse, 3);
2536 firstprefix[3] = ' ';
2537 firstprefix[4] = '\0';
2540 else
2542 if(!memcmp(firstprefix, lpszResponse, 4))
2543 break;
2548 if (nRecv >= 3)
2550 rc = atoi(lpszResponse);
2552 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2553 &nRecv, sizeof(DWORD));
2556 lerror:
2557 TRACE("return %d\n", rc);
2558 return rc;
2562 /***********************************************************************
2563 * FTP_SendPassword (internal)
2565 * Send password to ftp server
2567 * RETURNS
2568 * TRUE on success
2569 * NULL on failure
2572 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2574 INT nResCode;
2575 BOOL bSuccess = FALSE;
2577 TRACE("\n");
2578 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2579 goto lend;
2581 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2582 if (nResCode)
2584 TRACE("Received reply code %d\n", nResCode);
2585 /* Login successful... */
2586 if (nResCode == 230)
2587 bSuccess = TRUE;
2588 /* Command not implemented, superfluous at the server site... */
2589 /* Need account for login... */
2590 else if (nResCode == 332)
2591 bSuccess = FTP_SendAccount(lpwfs);
2592 else
2593 FTP_SetResponseError(nResCode);
2596 lend:
2597 TRACE("Returning %d\n", bSuccess);
2598 return bSuccess;
2602 /***********************************************************************
2603 * FTP_SendAccount (internal)
2607 * RETURNS
2608 * TRUE on success
2609 * FALSE on failure
2612 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2614 INT nResCode;
2615 BOOL bSuccess = FALSE;
2617 TRACE("\n");
2618 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2619 goto lend;
2621 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2622 if (nResCode)
2623 bSuccess = TRUE;
2624 else
2625 FTP_SetResponseError(nResCode);
2627 lend:
2628 return bSuccess;
2632 /***********************************************************************
2633 * FTP_SendStore (internal)
2635 * Send request to upload file to ftp server
2637 * RETURNS
2638 * TRUE on success
2639 * FALSE on failure
2642 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2644 INT nResCode;
2645 BOOL bSuccess = FALSE;
2647 TRACE("\n");
2648 if (!FTP_InitListenSocket(lpwfs))
2649 goto lend;
2651 if (!FTP_SendType(lpwfs, dwType))
2652 goto lend;
2654 if (!FTP_SendPortOrPasv(lpwfs))
2655 goto lend;
2657 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2658 goto lend;
2659 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2660 if (nResCode)
2662 if (nResCode == 150 || nResCode == 125)
2663 bSuccess = TRUE;
2664 else
2665 FTP_SetResponseError(nResCode);
2668 lend:
2669 if (!bSuccess && lpwfs->lstnSocket != -1)
2671 closesocket(lpwfs->lstnSocket);
2672 lpwfs->lstnSocket = -1;
2675 return bSuccess;
2679 /***********************************************************************
2680 * FTP_InitListenSocket (internal)
2682 * Create a socket to listen for server response
2684 * RETURNS
2685 * TRUE on success
2686 * FALSE on failure
2689 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2691 BOOL bSuccess = FALSE;
2692 socklen_t namelen = sizeof(struct sockaddr_in);
2694 TRACE("\n");
2696 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2697 if (lpwfs->lstnSocket == -1)
2699 TRACE("Unable to create listening socket\n");
2700 goto lend;
2703 /* We obtain our ip addr from the name of the command channel socket */
2704 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2706 /* and get the system to assign us a port */
2707 lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
2709 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2711 TRACE("Unable to bind socket\n");
2712 goto lend;
2715 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2717 TRACE("listen failed\n");
2718 goto lend;
2721 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2722 bSuccess = TRUE;
2724 lend:
2725 if (!bSuccess && lpwfs->lstnSocket != -1)
2727 closesocket(lpwfs->lstnSocket);
2728 lpwfs->lstnSocket = -1;
2731 return bSuccess;
2735 /***********************************************************************
2736 * FTP_SendType (internal)
2738 * Tell server type of data being transferred
2740 * RETURNS
2741 * TRUE on success
2742 * FALSE on failure
2744 * W98SE doesn't cache the type that's currently set
2745 * (i.e. it sends it always),
2746 * so we probably don't want to do that either.
2748 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2750 INT nResCode;
2751 WCHAR type[] = { 'I','\0' };
2752 BOOL bSuccess = FALSE;
2754 TRACE("\n");
2755 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2756 type[0] = 'A';
2758 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2759 goto lend;
2761 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2762 if (nResCode)
2764 if (nResCode == 2)
2765 bSuccess = TRUE;
2766 else
2767 FTP_SetResponseError(nResCode);
2770 lend:
2771 return bSuccess;
2775 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2776 /***********************************************************************
2777 * FTP_GetFileSize (internal)
2779 * Retrieves from the server the size of the given file
2781 * RETURNS
2782 * TRUE on success
2783 * FALSE on failure
2786 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2788 INT nResCode;
2789 BOOL bSuccess = FALSE;
2791 TRACE("\n");
2793 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2794 goto lend;
2796 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2797 if (nResCode)
2799 if (nResCode == 213) {
2800 /* Now parses the output to get the actual file size */
2801 int i;
2802 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2804 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2805 if (lpszResponseBuffer[i] == '\0') return FALSE;
2806 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2808 bSuccess = TRUE;
2809 } else {
2810 FTP_SetResponseError(nResCode);
2814 lend:
2815 return bSuccess;
2817 #endif
2820 /***********************************************************************
2821 * FTP_SendPort (internal)
2823 * Tell server which port to use
2825 * RETURNS
2826 * TRUE on success
2827 * FALSE on failure
2830 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2832 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2833 INT nResCode;
2834 WCHAR szIPAddress[64];
2835 BOOL bSuccess = FALSE;
2836 TRACE("\n");
2838 sprintfW(szIPAddress, szIPFormat,
2839 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2840 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2841 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2842 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2843 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2844 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2846 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2847 goto lend;
2849 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2850 if (nResCode)
2852 if (nResCode == 200)
2853 bSuccess = TRUE;
2854 else
2855 FTP_SetResponseError(nResCode);
2858 lend:
2859 return bSuccess;
2863 /***********************************************************************
2864 * FTP_DoPassive (internal)
2866 * Tell server that we want to do passive transfers
2867 * and connect data socket
2869 * RETURNS
2870 * TRUE on success
2871 * FALSE on failure
2874 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2876 INT nResCode;
2877 BOOL bSuccess = FALSE;
2879 TRACE("\n");
2880 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2881 goto lend;
2883 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2884 if (nResCode)
2886 if (nResCode == 227)
2888 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2889 LPSTR p;
2890 int f[6];
2891 int i;
2892 char *pAddr, *pPort;
2893 INT nsocket = -1;
2894 struct sockaddr_in dataSocketAddress;
2896 p = lpszResponseBuffer+4; /* skip status code */
2897 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
2899 if (*p == '\0')
2901 ERR("no address found in response, aborting\n");
2902 goto lend;
2905 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
2906 &f[4], &f[5]) != 6)
2908 ERR("unknown response address format '%s', aborting\n", p);
2909 goto lend;
2911 for (i=0; i < 6; i++)
2912 f[i] = f[i] & 0xff;
2914 dataSocketAddress = lpwfs->socketAddress;
2915 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2916 pPort = (char *)&(dataSocketAddress.sin_port);
2917 pAddr[0] = f[0];
2918 pAddr[1] = f[1];
2919 pAddr[2] = f[2];
2920 pAddr[3] = f[3];
2921 pPort[0] = f[4];
2922 pPort[1] = f[5];
2924 nsocket = socket(AF_INET,SOCK_STREAM,0);
2925 if (nsocket == -1)
2926 goto lend;
2928 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2930 ERR("can't connect passive FTP data port.\n");
2931 closesocket(nsocket);
2932 goto lend;
2934 lpwfs->pasvSocket = nsocket;
2935 bSuccess = TRUE;
2937 else
2938 FTP_SetResponseError(nResCode);
2941 lend:
2942 return bSuccess;
2946 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2948 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2950 if (!FTP_DoPassive(lpwfs))
2951 return FALSE;
2953 else
2955 if (!FTP_SendPort(lpwfs))
2956 return FALSE;
2958 return TRUE;
2962 /***********************************************************************
2963 * FTP_GetDataSocket (internal)
2965 * Either accepts an incoming data socket connection from the server
2966 * or just returns the already opened socket after a PASV command
2967 * in case of passive FTP.
2970 * RETURNS
2971 * TRUE on success
2972 * FALSE on failure
2975 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
2977 struct sockaddr_in saddr;
2978 socklen_t addrlen = sizeof(struct sockaddr);
2980 TRACE("\n");
2981 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2983 *nDataSocket = lpwfs->pasvSocket;
2985 else
2987 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
2988 closesocket(lpwfs->lstnSocket);
2989 lpwfs->lstnSocket = -1;
2991 return *nDataSocket != -1;
2995 /***********************************************************************
2996 * FTP_SendData (internal)
2998 * Send data to the server
3000 * RETURNS
3001 * TRUE on success
3002 * FALSE on failure
3005 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
3007 BY_HANDLE_FILE_INFORMATION fi;
3008 DWORD nBytesRead = 0;
3009 DWORD nBytesSent = 0;
3010 DWORD nTotalSent = 0;
3011 DWORD nBytesToSend, nLen;
3012 int nRC = 1;
3013 time_t s_long_time, e_long_time;
3014 LONG nSeconds;
3015 CHAR *lpszBuffer;
3017 TRACE("\n");
3018 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3020 /* Get the size of the file. */
3021 GetFileInformationByHandle(hFile, &fi);
3022 time(&s_long_time);
3026 nBytesToSend = nBytesRead - nBytesSent;
3028 if (nBytesToSend <= 0)
3030 /* Read data from file. */
3031 nBytesSent = 0;
3032 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3033 ERR("Failed reading from file\n");
3035 if (nBytesRead > 0)
3036 nBytesToSend = nBytesRead;
3037 else
3038 break;
3041 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3042 DATA_PACKET_SIZE : nBytesToSend;
3043 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3045 if (nRC != -1)
3047 nBytesSent += nRC;
3048 nTotalSent += nRC;
3051 /* Do some computation to display the status. */
3052 time(&e_long_time);
3053 nSeconds = e_long_time - s_long_time;
3054 if( nSeconds / 60 > 0 )
3056 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3057 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3058 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3060 else
3062 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3063 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3064 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3066 } while (nRC != -1);
3068 TRACE("file transfer complete!\n");
3070 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3072 return nTotalSent;
3076 /***********************************************************************
3077 * FTP_SendRetrieve (internal)
3079 * Send request to retrieve a file
3081 * RETURNS
3082 * Number of bytes to be received on success
3083 * 0 on failure
3086 static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3088 INT nResCode;
3089 BOOL ret;
3091 TRACE("\n");
3092 if (!(ret = FTP_InitListenSocket(lpwfs)))
3093 goto lend;
3095 if (!(ret = FTP_SendType(lpwfs, dwType)))
3096 goto lend;
3098 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3099 goto lend;
3101 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3102 goto lend;
3104 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3105 if ((nResCode != 125) && (nResCode != 150)) {
3106 /* That means that we got an error getting the file. */
3107 FTP_SetResponseError(nResCode);
3108 ret = FALSE;
3111 lend:
3112 if (!ret && lpwfs->lstnSocket != -1)
3114 closesocket(lpwfs->lstnSocket);
3115 lpwfs->lstnSocket = -1;
3118 return ret;
3122 /***********************************************************************
3123 * FTP_RetrieveData (internal)
3125 * Retrieve data from server
3127 * RETURNS
3128 * TRUE on success
3129 * FALSE on failure
3132 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
3134 DWORD nBytesWritten;
3135 DWORD nBytesReceived = 0;
3136 INT nRC = 0;
3137 CHAR *lpszBuffer;
3139 TRACE("\n");
3141 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3142 if (NULL == lpszBuffer)
3144 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3145 return FALSE;
3148 while (nRC != -1)
3150 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3151 if (nRC != -1)
3153 /* other side closed socket. */
3154 if (nRC == 0)
3155 goto recv_end;
3156 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3157 nBytesReceived += nRC;
3161 TRACE("Data transfer complete\n");
3163 recv_end:
3164 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3166 return (nRC != -1);
3169 /***********************************************************************
3170 * FTPFINDNEXT_Destroy (internal)
3172 * Deallocate session handle
3174 static void FTPFINDNEXT_Destroy(WININETHANDLEHEADER *hdr)
3176 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3177 DWORD i;
3179 TRACE("\n");
3181 WININET_Release(&lpwfn->lpFtpSession->hdr);
3183 for (i = 0; i < lpwfn->size; i++)
3185 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3188 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3189 HeapFree(GetProcessHeap(), 0, lpwfn);
3192 static DWORD WINAPI FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3194 WIN32_FIND_DATAW *find_data = data;
3195 DWORD res = ERROR_SUCCESS;
3197 TRACE("index(%d) size(%d)\n", find->index, find->size);
3199 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3201 if (find->index < find->size) {
3202 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3203 find->index++;
3205 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3206 }else {
3207 res = ERROR_NO_MORE_FILES;
3210 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3212 INTERNET_ASYNC_RESULT iar;
3214 iar.dwResult = (res == ERROR_SUCCESS);
3215 iar.dwError = res;
3217 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3218 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3219 sizeof(INTERNET_ASYNC_RESULT));
3222 return res;
3225 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3227 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3229 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3232 static DWORD FTPFINDNEXT_FindNextFileW(WININETHANDLEHEADER *hdr, void *data)
3234 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3236 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3238 WORKREQUEST workRequest;
3239 struct WORKREQ_FTPFINDNEXTW *req;
3241 workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3242 workRequest.hdr = WININET_AddRef( &find->hdr );
3243 req = &workRequest.u.FtpFindNextW;
3244 req->lpFindFileData = data;
3246 INTERNET_AsyncCall(&workRequest);
3248 return ERROR_SUCCESS;
3251 return FTPFINDNEXT_FindNextFileProc(find, data);
3254 static const HANDLEHEADERVtbl FTPFINDNEXTVtbl = {
3255 FTPFINDNEXT_Destroy,
3256 NULL,
3257 NULL,
3258 NULL,
3259 NULL,
3260 NULL,
3261 NULL,
3262 FTPFINDNEXT_FindNextFileW
3265 /***********************************************************************
3266 * FTP_ReceiveFileList (internal)
3268 * Read file list from server
3270 * RETURNS
3271 * Handle to file list on success
3272 * NULL on failure
3275 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3276 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3278 DWORD dwSize = 0;
3279 LPFILEPROPERTIESW lpafp = NULL;
3280 LPWININETFTPFINDNEXTW lpwfn = NULL;
3281 HINTERNET handle = 0;
3283 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3285 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3287 if(lpFindFileData)
3288 FTP_ConvertFileProp(lpafp, lpFindFileData);
3290 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3291 if (lpwfn)
3293 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3294 lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3295 lpwfn->hdr.dwContext = dwContext;
3296 lpwfn->hdr.refs = 1;
3297 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3298 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3299 lpwfn->size = dwSize;
3300 lpwfn->lpafp = lpafp;
3302 WININET_AddRef( &lpwfs->hdr );
3303 lpwfn->lpFtpSession = lpwfs;
3304 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3306 handle = WININET_AllocHandle( &lpwfn->hdr );
3310 if( lpwfn )
3311 WININET_Release( &lpwfn->hdr );
3313 TRACE("Matched %d files\n", dwSize);
3314 return handle;
3318 /***********************************************************************
3319 * FTP_ConvertFileProp (internal)
3321 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3323 * RETURNS
3324 * TRUE on success
3325 * FALSE on failure
3328 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3330 BOOL bSuccess = FALSE;
3332 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3334 if (lpafp)
3336 /* Convert 'Unix' time to Windows time */
3337 RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
3338 (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
3339 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3340 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3342 /* Not all fields are filled in */
3343 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3344 lpFindFileData->nFileSizeLow = lpafp->nSize;
3346 if (lpafp->bIsDirectory)
3347 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3349 if (lpafp->lpszName)
3350 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3352 bSuccess = TRUE;
3355 return bSuccess;
3358 /***********************************************************************
3359 * FTP_ParseNextFile (internal)
3361 * Parse the next line in file listing
3363 * RETURNS
3364 * TRUE on success
3365 * FALSE on failure
3367 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3369 static const char szSpace[] = " \t";
3370 DWORD nBufLen;
3371 char *pszLine;
3372 char *pszToken;
3373 char *pszTmp;
3374 BOOL found = FALSE;
3375 int i;
3377 lpfp->lpszName = NULL;
3378 do {
3379 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3380 return FALSE;
3382 pszToken = strtok(pszLine, szSpace);
3383 /* ls format
3384 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3386 * For instance:
3387 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3389 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3390 if(!FTP_ParsePermission(pszToken, lpfp))
3391 lpfp->bIsDirectory = FALSE;
3392 for(i=0; i<=3; i++) {
3393 if(!(pszToken = strtok(NULL, szSpace)))
3394 break;
3396 if(!pszToken) continue;
3397 if(lpfp->bIsDirectory) {
3398 TRACE("Is directory\n");
3399 lpfp->nSize = 0;
3401 else {
3402 TRACE("Size: %s\n", pszToken);
3403 lpfp->nSize = atol(pszToken);
3406 lpfp->tmLastModified.tm_sec = 0;
3407 lpfp->tmLastModified.tm_min = 0;
3408 lpfp->tmLastModified.tm_hour = 0;
3409 lpfp->tmLastModified.tm_mday = 0;
3410 lpfp->tmLastModified.tm_mon = 0;
3411 lpfp->tmLastModified.tm_year = 0;
3413 /* Determine month */
3414 pszToken = strtok(NULL, szSpace);
3415 if(!pszToken) continue;
3416 if(strlen(pszToken) >= 3) {
3417 pszToken[3] = 0;
3418 if((pszTmp = StrStrIA(szMonths, pszToken)))
3419 lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
3421 /* Determine day */
3422 pszToken = strtok(NULL, szSpace);
3423 if(!pszToken) continue;
3424 lpfp->tmLastModified.tm_mday = atoi(pszToken);
3425 /* Determine time or year */
3426 pszToken = strtok(NULL, szSpace);
3427 if(!pszToken) continue;
3428 if((pszTmp = strchr(pszToken, ':'))) {
3429 struct tm* apTM;
3430 time_t aTime;
3431 *pszTmp = 0;
3432 pszTmp++;
3433 lpfp->tmLastModified.tm_min = atoi(pszTmp);
3434 lpfp->tmLastModified.tm_hour = atoi(pszToken);
3435 time(&aTime);
3436 apTM = localtime(&aTime);
3437 lpfp->tmLastModified.tm_year = apTM->tm_year;
3439 else {
3440 lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
3441 lpfp->tmLastModified.tm_hour = 12;
3443 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3444 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3445 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3446 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3448 pszToken = strtok(NULL, szSpace);
3449 if(!pszToken) continue;
3450 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3451 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3453 /* NT way of parsing ... :
3455 07-13-03 08:55PM <DIR> sakpatch
3456 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3458 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3459 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3461 sscanf(pszToken, "%d-%d-%d",
3462 &lpfp->tmLastModified.tm_mon,
3463 &lpfp->tmLastModified.tm_mday,
3464 &lpfp->tmLastModified.tm_year);
3466 /* Hacky and bad Y2K protection :-) */
3467 if (lpfp->tmLastModified.tm_year < 70)
3468 lpfp->tmLastModified.tm_year += 100;
3470 pszToken = strtok(NULL, szSpace);
3471 if(!pszToken) continue;
3472 sscanf(pszToken, "%d:%d",
3473 &lpfp->tmLastModified.tm_hour,
3474 &lpfp->tmLastModified.tm_min);
3475 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3476 lpfp->tmLastModified.tm_hour += 12;
3478 lpfp->tmLastModified.tm_sec = 0;
3480 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3481 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3482 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3483 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3485 pszToken = strtok(NULL, szSpace);
3486 if(!pszToken) continue;
3487 if(!strcasecmp(pszToken, "<DIR>")) {
3488 lpfp->bIsDirectory = TRUE;
3489 lpfp->nSize = 0;
3490 TRACE("Is directory\n");
3492 else {
3493 lpfp->bIsDirectory = FALSE;
3494 lpfp->nSize = atol(pszToken);
3495 TRACE("Size: %d\n", lpfp->nSize);
3498 pszToken = strtok(NULL, szSpace);
3499 if(!pszToken) continue;
3500 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3501 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3503 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3504 else if(pszToken[0] == '+') {
3505 FIXME("EPLF Format not implemented\n");
3508 if(lpfp->lpszName) {
3509 if((lpszSearchFile == NULL) ||
3510 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3511 found = TRUE;
3512 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3514 else {
3515 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3516 lpfp->lpszName = NULL;
3519 } while(!found);
3520 return TRUE;
3523 /***********************************************************************
3524 * FTP_ParseDirectory (internal)
3526 * Parse string of directory information
3528 * RETURNS
3529 * TRUE on success
3530 * FALSE on failure
3532 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3533 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3535 BOOL bSuccess = TRUE;
3536 INT sizeFilePropArray = 500;/*20; */
3537 INT indexFilePropArray = -1;
3539 TRACE("\n");
3541 /* Allocate initial file properties array */
3542 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3543 if (!*lpafp)
3544 return FALSE;
3546 do {
3547 if (indexFilePropArray+1 >= sizeFilePropArray)
3549 LPFILEPROPERTIESW tmpafp;
3551 sizeFilePropArray *= 2;
3552 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3553 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3554 if (NULL == tmpafp)
3556 bSuccess = FALSE;
3557 break;
3560 *lpafp = tmpafp;
3562 indexFilePropArray++;
3563 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3565 if (bSuccess && indexFilePropArray)
3567 if (indexFilePropArray < sizeFilePropArray - 1)
3569 LPFILEPROPERTIESW tmpafp;
3571 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3572 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3573 if (NULL != tmpafp)
3574 *lpafp = tmpafp;
3576 *dwfp = indexFilePropArray;
3578 else
3580 HeapFree(GetProcessHeap(), 0, *lpafp);
3581 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3582 bSuccess = FALSE;
3585 return bSuccess;
3589 /***********************************************************************
3590 * FTP_ParsePermission (internal)
3592 * Parse permission string of directory information
3594 * RETURNS
3595 * TRUE on success
3596 * FALSE on failure
3599 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3601 BOOL bSuccess = TRUE;
3602 unsigned short nPermission = 0;
3603 INT nPos = 1;
3604 INT nLast = 9;
3606 TRACE("\n");
3607 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3609 bSuccess = FALSE;
3610 return bSuccess;
3613 lpfp->bIsDirectory = (*lpszPermission == 'd');
3616 switch (nPos)
3618 case 1:
3619 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3620 break;
3621 case 2:
3622 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3623 break;
3624 case 3:
3625 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3626 break;
3627 case 4:
3628 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3629 break;
3630 case 5:
3631 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3632 break;
3633 case 6:
3634 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3635 break;
3636 case 7:
3637 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3638 break;
3639 case 8:
3640 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3641 break;
3642 case 9:
3643 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3644 break;
3646 nPos++;
3647 }while (nPos <= nLast);
3649 lpfp->permissions = nPermission;
3650 return bSuccess;
3654 /***********************************************************************
3655 * FTP_SetResponseError (internal)
3657 * Set the appropriate error code for a given response from the server
3659 * RETURNS
3662 static DWORD FTP_SetResponseError(DWORD dwResponse)
3664 DWORD dwCode = 0;
3666 switch(dwResponse)
3668 case 425: /* Cannot open data connection. */
3669 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3670 break;
3672 case 426: /* Connection closed, transer aborted. */
3673 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3674 break;
3676 case 530: /* Not logged in. Login incorrect. */
3677 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3678 break;
3680 case 421: /* Service not available - Server may be shutting down. */
3681 case 450: /* File action not taken. File may be busy. */
3682 case 451: /* Action aborted. Server error. */
3683 case 452: /* Action not taken. Insufficient storage space on server. */
3684 case 500: /* Syntax error. Command unrecognized. */
3685 case 501: /* Syntax error. Error in parameters or arguments. */
3686 case 502: /* Command not implemented. */
3687 case 503: /* Bad sequence of commands. */
3688 case 504: /* Command not implemented for that parameter. */
3689 case 532: /* Need account for storing files */
3690 case 550: /* File action not taken. File not found or no access. */
3691 case 551: /* Requested action aborted. Page type unknown */
3692 case 552: /* Action aborted. Exceeded storage allocation */
3693 case 553: /* Action not taken. File name not allowed. */
3695 default:
3696 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3697 break;
3700 INTERNET_SetLastError(dwCode);
3701 return dwCode;