push 9e645869891abdc47a8701768b7a401b196a1e38
[wine/hacks.git] / dlls / wininet / ftp.c
blob9ad34dfa1ef52f07114bf299a34a62e23e52162f
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("%p %p %p\n", hFtpSession, lpszCurrentDirectory, 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 /* Clear any error information */
1019 INTERNET_SetLastError(0);
1021 hIC = lpwfs->lpAppInfo;
1022 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1023 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1024 goto lend;
1026 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1027 if (nResCode)
1029 if (nResCode == 257) /* Extract directory name */
1031 DWORD firstpos, lastpos, len;
1032 LPWSTR lpszResponseBuffer = WININET_strdup_AtoW(INTERNET_GetResponseBuffer());
1034 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1036 if ('"' == lpszResponseBuffer[lastpos])
1038 if (!firstpos)
1039 firstpos = lastpos;
1040 else
1041 break;
1044 len = lastpos - firstpos;
1045 if (*lpdwCurrentDirectory >= len)
1047 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1048 lpszCurrentDirectory[len - 1] = 0;
1049 *lpdwCurrentDirectory = len;
1050 bSuccess = TRUE;
1052 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1054 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
1056 else
1057 FTP_SetResponseError(nResCode);
1060 lend:
1061 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1063 INTERNET_ASYNC_RESULT iar;
1065 iar.dwResult = bSuccess;
1066 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1067 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1068 &iar, sizeof(INTERNET_ASYNC_RESULT));
1071 return bSuccess;
1074 /***********************************************************************
1075 * FtpOpenFileA (WININET.@)
1077 * Open a remote file for writing or reading
1079 * RETURNS
1080 * HINTERNET handle on success
1081 * NULL on failure
1084 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1085 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1086 DWORD_PTR dwContext)
1088 LPWSTR lpwzFileName;
1089 HINTERNET ret;
1091 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1092 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1093 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1094 return ret;
1098 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1100 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1101 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1103 TRACE("%p\n", lpwfs);
1105 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1106 req->dwAccess, req->dwFlags, req->dwContext);
1107 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1110 /***********************************************************************
1111 * FtpOpenFileW (WININET.@)
1113 * Open a remote file for writing or reading
1115 * RETURNS
1116 * HINTERNET handle on success
1117 * NULL on failure
1120 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1121 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1122 DWORD_PTR dwContext)
1124 LPWININETFTPSESSIONW lpwfs;
1125 LPWININETAPPINFOW hIC = NULL;
1126 HINTERNET r = NULL;
1128 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1129 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1131 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1132 if (!lpwfs)
1134 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1135 return FALSE;
1138 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1140 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1141 goto lend;
1144 if ((!lpszFileName) ||
1145 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1146 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1148 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1149 goto lend;
1152 if (lpwfs->download_in_progress != NULL)
1154 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1155 goto lend;
1158 hIC = lpwfs->lpAppInfo;
1159 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1161 WORKREQUEST workRequest;
1162 struct WORKREQ_FTPOPENFILEW *req;
1164 workRequest.asyncproc = AsyncFtpOpenFileProc;
1165 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1166 req = &workRequest.u.FtpOpenFileW;
1167 req->lpszFilename = WININET_strdupW(lpszFileName);
1168 req->dwAccess = fdwAccess;
1169 req->dwFlags = dwFlags;
1170 req->dwContext = dwContext;
1172 INTERNET_AsyncCall(&workRequest);
1173 r = NULL;
1175 else
1177 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1180 lend:
1181 WININET_Release( &lpwfs->hdr );
1183 return r;
1187 /***********************************************************************
1188 * FTPFILE_Destroy(internal)
1190 * Closes the file transfer handle. This also 'cleans' the data queue of
1191 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1194 static void FTPFILE_Destroy(WININETHANDLEHEADER *hdr)
1196 LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
1197 LPWININETFTPSESSIONW lpwfs = lpwh->lpFtpSession;
1198 INT nResCode;
1200 TRACE("\n");
1202 WININET_Release(&lpwh->lpFtpSession->hdr);
1204 if (!lpwh->session_deleted)
1205 lpwfs->download_in_progress = NULL;
1207 if (lpwh->nDataSocket != -1)
1208 closesocket(lpwh->nDataSocket);
1210 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1211 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1213 HeapFree(GetProcessHeap(), 0, lpwh);
1216 static DWORD FTPFILE_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1218 switch(option) {
1219 case INTERNET_OPTION_HANDLE_TYPE:
1220 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1222 if (*size < sizeof(ULONG))
1223 return ERROR_INSUFFICIENT_BUFFER;
1225 *size = sizeof(DWORD);
1226 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1227 return ERROR_SUCCESS;
1230 return INET_QueryOption(option, buffer, size, unicode);
1233 static DWORD FTPFILE_ReadFile(WININETHANDLEHEADER *hdr, void *buffer, DWORD size, DWORD *read)
1235 WININETFTPFILE *file = (WININETFTPFILE*)hdr;
1236 int res;
1238 if (file->nDataSocket == -1)
1239 return ERROR_INTERNET_DISCONNECTED;
1241 /* FIXME: FTP should use NETCON_ stuff */
1242 res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1243 *read = res>0 ? res : 0;
1245 return res>=0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME*/
1248 static BOOL FTPFILE_WriteFile(WININETHANDLEHEADER *hdr, const void *buffer, DWORD size, DWORD *written)
1250 LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
1251 int res;
1253 res = send(lpwh->nDataSocket, buffer, size, 0);
1255 *written = res>0 ? res : 0;
1256 return res >= 0;
1259 static const HANDLEHEADERVtbl FTPFILEVtbl = {
1260 FTPFILE_Destroy,
1261 NULL,
1262 FTPFILE_QueryOption,
1263 NULL,
1264 FTPFILE_ReadFile,
1265 NULL,
1266 FTPFILE_WriteFile,
1267 NULL,
1268 NULL
1271 /***********************************************************************
1272 * FTP_FtpOpenFileW (Internal)
1274 * Open a remote file for writing or reading
1276 * RETURNS
1277 * HINTERNET handle on success
1278 * NULL on failure
1281 HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
1282 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1283 DWORD_PTR dwContext)
1285 INT nDataSocket;
1286 BOOL bSuccess = FALSE;
1287 LPWININETFTPFILE lpwh = NULL;
1288 LPWININETAPPINFOW hIC = NULL;
1289 HINTERNET handle = NULL;
1291 TRACE("\n");
1293 /* Clear any error information */
1294 INTERNET_SetLastError(0);
1296 if (GENERIC_READ == fdwAccess)
1298 /* Set up socket to retrieve data */
1299 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1301 else if (GENERIC_WRITE == fdwAccess)
1303 /* Set up socket to send data */
1304 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1307 /* Get data socket to server */
1308 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1310 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPFILE));
1311 lpwh->hdr.htype = WH_HFILE;
1312 lpwh->hdr.vtbl = &FTPFILEVtbl;
1313 lpwh->hdr.dwFlags = dwFlags;
1314 lpwh->hdr.dwContext = dwContext;
1315 lpwh->hdr.refs = 1;
1316 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1317 lpwh->nDataSocket = nDataSocket;
1318 lpwh->session_deleted = FALSE;
1320 WININET_AddRef( &lpwfs->hdr );
1321 lpwh->lpFtpSession = lpwfs;
1322 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1324 handle = WININET_AllocHandle( &lpwh->hdr );
1325 if( !handle )
1326 goto lend;
1328 /* Indicate that a download is currently in progress */
1329 lpwfs->download_in_progress = lpwh;
1332 if (lpwfs->lstnSocket != -1)
1333 closesocket(lpwfs->lstnSocket);
1335 hIC = lpwfs->lpAppInfo;
1336 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1338 INTERNET_ASYNC_RESULT iar;
1340 if (lpwh)
1342 iar.dwResult = (DWORD)handle;
1343 iar.dwError = ERROR_SUCCESS;
1344 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1345 &iar, sizeof(INTERNET_ASYNC_RESULT));
1348 iar.dwResult = (DWORD)bSuccess;
1349 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1350 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1351 &iar, sizeof(INTERNET_ASYNC_RESULT));
1354 lend:
1355 if( lpwh )
1356 WININET_Release( &lpwh->hdr );
1358 return handle;
1362 /***********************************************************************
1363 * FtpGetFileA (WININET.@)
1365 * Retrieve file from the FTP server
1367 * RETURNS
1368 * TRUE on success
1369 * FALSE on failure
1372 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1373 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1374 DWORD_PTR dwContext)
1376 LPWSTR lpwzRemoteFile;
1377 LPWSTR lpwzNewFile;
1378 BOOL ret;
1380 lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
1381 lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
1382 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1383 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1384 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1385 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1386 return ret;
1390 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1392 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1393 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1395 TRACE("%p\n", lpwfs);
1397 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1398 req->lpszNewFile, req->fFailIfExists,
1399 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1400 HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1401 HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1405 /***********************************************************************
1406 * FtpGetFileW (WININET.@)
1408 * Retrieve file from the FTP server
1410 * RETURNS
1411 * TRUE on success
1412 * FALSE on failure
1415 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1416 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1417 DWORD_PTR dwContext)
1419 LPWININETFTPSESSIONW lpwfs;
1420 LPWININETAPPINFOW hIC = NULL;
1421 BOOL r = FALSE;
1423 if (!lpszRemoteFile || !lpszNewFile)
1425 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1426 return FALSE;
1429 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
1430 if (!lpwfs)
1432 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1433 return FALSE;
1436 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1438 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1439 goto lend;
1442 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1444 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1445 goto lend;
1448 if (lpwfs->download_in_progress != NULL)
1450 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1451 goto lend;
1454 hIC = lpwfs->lpAppInfo;
1455 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1457 WORKREQUEST workRequest;
1458 struct WORKREQ_FTPGETFILEW *req;
1460 workRequest.asyncproc = AsyncFtpGetFileProc;
1461 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1462 req = &workRequest.u.FtpGetFileW;
1463 req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
1464 req->lpszNewFile = WININET_strdupW(lpszNewFile);
1465 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1466 req->fFailIfExists = fFailIfExists;
1467 req->dwFlags = dwInternetFlags;
1468 req->dwContext = dwContext;
1470 r = INTERNET_AsyncCall(&workRequest);
1472 else
1474 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1475 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1478 lend:
1479 WININET_Release( &lpwfs->hdr );
1481 return r;
1485 /***********************************************************************
1486 * FTP_FtpGetFileW (Internal)
1488 * Retrieve file from the FTP server
1490 * RETURNS
1491 * TRUE on success
1492 * FALSE on failure
1495 static BOOL FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1496 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1497 DWORD_PTR dwContext)
1499 BOOL bSuccess = FALSE;
1500 HANDLE hFile;
1501 LPWININETAPPINFOW hIC = NULL;
1503 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1505 /* Clear any error information */
1506 INTERNET_SetLastError(0);
1508 /* Ensure we can write to lpszNewfile by opening it */
1509 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1510 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1511 if (INVALID_HANDLE_VALUE == hFile)
1512 return FALSE;
1514 /* Set up socket to retrieve data */
1515 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1517 INT nDataSocket;
1519 /* Get data socket to server */
1520 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1522 INT nResCode;
1524 /* Receive data */
1525 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1526 closesocket(nDataSocket);
1528 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1529 if (nResCode)
1531 if (nResCode == 226)
1532 bSuccess = TRUE;
1533 else
1534 FTP_SetResponseError(nResCode);
1539 if (lpwfs->lstnSocket != -1)
1540 closesocket(lpwfs->lstnSocket);
1542 CloseHandle(hFile);
1544 hIC = lpwfs->lpAppInfo;
1545 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1547 INTERNET_ASYNC_RESULT iar;
1549 iar.dwResult = (DWORD)bSuccess;
1550 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1551 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1552 &iar, sizeof(INTERNET_ASYNC_RESULT));
1555 if (!bSuccess) DeleteFileW(lpszNewFile);
1556 return bSuccess;
1559 /***********************************************************************
1560 * FtpGetFileSize (WININET.@)
1562 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1564 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1566 if (lpdwFileSizeHigh)
1567 *lpdwFileSizeHigh = 0;
1569 return 0;
1572 /***********************************************************************
1573 * FtpDeleteFileA (WININET.@)
1575 * Delete a file on the ftp server
1577 * RETURNS
1578 * TRUE on success
1579 * FALSE on failure
1582 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1584 LPWSTR lpwzFileName;
1585 BOOL ret;
1587 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1588 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1589 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1590 return ret;
1593 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1595 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1596 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1598 TRACE("%p\n", lpwfs);
1600 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1601 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1604 /***********************************************************************
1605 * FtpDeleteFileW (WININET.@)
1607 * Delete a file on the ftp server
1609 * RETURNS
1610 * TRUE on success
1611 * FALSE on failure
1614 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1616 LPWININETFTPSESSIONW lpwfs;
1617 LPWININETAPPINFOW hIC = NULL;
1618 BOOL r = FALSE;
1620 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1621 if (!lpwfs)
1623 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1624 return FALSE;
1627 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1629 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1630 goto lend;
1633 if (lpwfs->download_in_progress != NULL)
1635 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1636 goto lend;
1639 if (!lpszFileName)
1641 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1642 goto lend;
1645 hIC = lpwfs->lpAppInfo;
1646 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1648 WORKREQUEST workRequest;
1649 struct WORKREQ_FTPDELETEFILEW *req;
1651 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1652 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1653 req = &workRequest.u.FtpDeleteFileW;
1654 req->lpszFilename = WININET_strdupW(lpszFileName);
1656 r = INTERNET_AsyncCall(&workRequest);
1658 else
1660 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1663 lend:
1664 WININET_Release( &lpwfs->hdr );
1666 return r;
1669 /***********************************************************************
1670 * FTP_FtpDeleteFileW (Internal)
1672 * Delete a file on the ftp server
1674 * RETURNS
1675 * TRUE on success
1676 * FALSE on failure
1679 BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName)
1681 INT nResCode;
1682 BOOL bSuccess = FALSE;
1683 LPWININETAPPINFOW hIC = NULL;
1685 TRACE("%p\n", lpwfs);
1687 /* Clear any error information */
1688 INTERNET_SetLastError(0);
1690 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1691 goto lend;
1693 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1694 if (nResCode)
1696 if (nResCode == 250)
1697 bSuccess = TRUE;
1698 else
1699 FTP_SetResponseError(nResCode);
1701 lend:
1702 hIC = lpwfs->lpAppInfo;
1703 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1705 INTERNET_ASYNC_RESULT iar;
1707 iar.dwResult = (DWORD)bSuccess;
1708 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1709 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1710 &iar, sizeof(INTERNET_ASYNC_RESULT));
1713 return bSuccess;
1717 /***********************************************************************
1718 * FtpRemoveDirectoryA (WININET.@)
1720 * Remove a directory on the ftp server
1722 * RETURNS
1723 * TRUE on success
1724 * FALSE on failure
1727 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1729 LPWSTR lpwzDirectory;
1730 BOOL ret;
1732 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
1733 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1734 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1735 return ret;
1738 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1740 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1741 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1743 TRACE("%p\n", lpwfs);
1745 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1746 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1749 /***********************************************************************
1750 * FtpRemoveDirectoryW (WININET.@)
1752 * Remove a directory on the ftp server
1754 * RETURNS
1755 * TRUE on success
1756 * FALSE on failure
1759 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1761 LPWININETFTPSESSIONW lpwfs;
1762 LPWININETAPPINFOW hIC = NULL;
1763 BOOL r = FALSE;
1765 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1766 if (!lpwfs)
1768 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1769 return FALSE;
1772 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1774 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1775 goto lend;
1778 if (lpwfs->download_in_progress != NULL)
1780 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1781 goto lend;
1784 if (!lpszDirectory)
1786 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1787 goto lend;
1790 hIC = lpwfs->lpAppInfo;
1791 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1793 WORKREQUEST workRequest;
1794 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1796 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1797 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1798 req = &workRequest.u.FtpRemoveDirectoryW;
1799 req->lpszDirectory = WININET_strdupW(lpszDirectory);
1801 r = INTERNET_AsyncCall(&workRequest);
1803 else
1805 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1808 lend:
1809 WININET_Release( &lpwfs->hdr );
1811 return r;
1814 /***********************************************************************
1815 * FTP_FtpRemoveDirectoryW (Internal)
1817 * Remove a directory on the ftp server
1819 * RETURNS
1820 * TRUE on success
1821 * FALSE on failure
1824 BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
1826 INT nResCode;
1827 BOOL bSuccess = FALSE;
1828 LPWININETAPPINFOW hIC = NULL;
1830 TRACE("\n");
1832 /* Clear any error information */
1833 INTERNET_SetLastError(0);
1835 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1836 goto lend;
1838 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1839 if (nResCode)
1841 if (nResCode == 250)
1842 bSuccess = TRUE;
1843 else
1844 FTP_SetResponseError(nResCode);
1847 lend:
1848 hIC = lpwfs->lpAppInfo;
1849 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1851 INTERNET_ASYNC_RESULT iar;
1853 iar.dwResult = (DWORD)bSuccess;
1854 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1855 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1856 &iar, sizeof(INTERNET_ASYNC_RESULT));
1859 return bSuccess;
1863 /***********************************************************************
1864 * FtpRenameFileA (WININET.@)
1866 * Rename a file on the ftp server
1868 * RETURNS
1869 * TRUE on success
1870 * FALSE on failure
1873 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1875 LPWSTR lpwzSrc;
1876 LPWSTR lpwzDest;
1877 BOOL ret;
1879 lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL;
1880 lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL;
1881 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1882 HeapFree(GetProcessHeap(), 0, lpwzSrc);
1883 HeapFree(GetProcessHeap(), 0, lpwzDest);
1884 return ret;
1887 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
1889 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
1890 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1892 TRACE("%p\n", lpwfs);
1894 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
1895 HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
1896 HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
1899 /***********************************************************************
1900 * FtpRenameFileW (WININET.@)
1902 * Rename a file on the ftp server
1904 * RETURNS
1905 * TRUE on success
1906 * FALSE on failure
1909 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
1911 LPWININETFTPSESSIONW lpwfs;
1912 LPWININETAPPINFOW hIC = NULL;
1913 BOOL r = FALSE;
1915 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1916 if (!lpwfs)
1918 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1919 return FALSE;
1922 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1924 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1925 goto lend;
1928 if (lpwfs->download_in_progress != NULL)
1930 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1931 goto lend;
1934 if (!lpszSrc || !lpszDest)
1936 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1937 goto lend;
1940 hIC = lpwfs->lpAppInfo;
1941 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1943 WORKREQUEST workRequest;
1944 struct WORKREQ_FTPRENAMEFILEW *req;
1946 workRequest.asyncproc = AsyncFtpRenameFileProc;
1947 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1948 req = &workRequest.u.FtpRenameFileW;
1949 req->lpszSrcFile = WININET_strdupW(lpszSrc);
1950 req->lpszDestFile = WININET_strdupW(lpszDest);
1952 r = INTERNET_AsyncCall(&workRequest);
1954 else
1956 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
1959 lend:
1960 WININET_Release( &lpwfs->hdr );
1962 return r;
1965 /***********************************************************************
1966 * FTP_FtpRenameFileW (Internal)
1968 * Rename a file on the ftp server
1970 * RETURNS
1971 * TRUE on success
1972 * FALSE on failure
1975 BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs,
1976 LPCWSTR lpszSrc, LPCWSTR lpszDest)
1978 INT nResCode;
1979 BOOL bSuccess = FALSE;
1980 LPWININETAPPINFOW hIC = NULL;
1982 TRACE("\n");
1984 /* Clear any error information */
1985 INTERNET_SetLastError(0);
1987 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
1988 goto lend;
1990 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1991 if (nResCode == 350)
1993 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
1994 goto lend;
1996 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1999 if (nResCode == 250)
2000 bSuccess = TRUE;
2001 else
2002 FTP_SetResponseError(nResCode);
2004 lend:
2005 hIC = lpwfs->lpAppInfo;
2006 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2008 INTERNET_ASYNC_RESULT iar;
2010 iar.dwResult = (DWORD)bSuccess;
2011 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2012 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2013 &iar, sizeof(INTERNET_ASYNC_RESULT));
2016 return bSuccess;
2019 /***********************************************************************
2020 * FtpCommandA (WININET.@)
2022 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2023 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2025 BOOL r;
2026 WCHAR *cmdW;
2028 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2029 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2031 if (fExpectResponse)
2033 FIXME("data connection not supported\n");
2034 return FALSE;
2037 if (!lpszCommand || !lpszCommand[0])
2039 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2040 return FALSE;
2043 if (!(cmdW = WININET_strdup_AtoW(lpszCommand)))
2045 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2046 return FALSE;
2049 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2051 HeapFree(GetProcessHeap(), 0, cmdW);
2052 return r;
2055 /***********************************************************************
2056 * FtpCommandW (WININET.@)
2058 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2059 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2061 BOOL r = FALSE;
2062 LPWININETFTPSESSIONW lpwfs;
2063 LPSTR cmd = NULL;
2064 DWORD len, nBytesSent= 0;
2065 INT nResCode, nRC = 0;
2067 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2068 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2070 if (!lpszCommand || !lpszCommand[0])
2072 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2073 return FALSE;
2076 if (fExpectResponse)
2078 FIXME("data connection not supported\n");
2079 return FALSE;
2082 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
2083 if (!lpwfs)
2085 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2086 return FALSE;
2089 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2091 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2092 goto lend;
2095 if (lpwfs->download_in_progress != NULL)
2097 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2098 goto lend;
2101 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2102 if ((cmd = HeapAlloc(GetProcessHeap(), 0, len )))
2103 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2104 else
2106 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2107 goto lend;
2110 strcat(cmd, szCRLF);
2111 len--;
2113 TRACE("Sending (%s) len(%d)\n", cmd, len);
2114 while ((nBytesSent < len) && (nRC != -1))
2116 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2117 if (nRC != -1)
2119 nBytesSent += nRC;
2120 TRACE("Sent %d bytes\n", nRC);
2124 if (nBytesSent)
2126 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2127 if (nResCode > 0 && nResCode < 400)
2128 r = TRUE;
2129 else
2130 FTP_SetResponseError(nResCode);
2133 lend:
2134 WININET_Release( &lpwfs->hdr );
2135 HeapFree(GetProcessHeap(), 0, cmd);
2136 return r;
2140 /***********************************************************************
2141 * FTPSESSION_Destroy (internal)
2143 * Deallocate session handle
2145 static void FTPSESSION_Destroy(WININETHANDLEHEADER *hdr)
2147 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2149 TRACE("\n");
2151 WININET_Release(&lpwfs->lpAppInfo->hdr);
2153 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2154 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2155 HeapFree(GetProcessHeap(), 0, lpwfs);
2158 static void FTPSESSION_CloseConnection(WININETHANDLEHEADER *hdr)
2160 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2162 TRACE("\n");
2164 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2165 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2167 if (lpwfs->download_in_progress != NULL)
2168 lpwfs->download_in_progress->session_deleted = TRUE;
2170 if (lpwfs->sndSocket != -1)
2171 closesocket(lpwfs->sndSocket);
2173 if (lpwfs->lstnSocket != -1)
2174 closesocket(lpwfs->lstnSocket);
2176 if (lpwfs->pasvSocket != -1)
2177 closesocket(lpwfs->pasvSocket);
2179 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2180 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2183 static DWORD FTPSESSION_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2185 switch(option) {
2186 case INTERNET_OPTION_HANDLE_TYPE:
2187 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2189 if (*size < sizeof(ULONG))
2190 return ERROR_INSUFFICIENT_BUFFER;
2192 *size = sizeof(DWORD);
2193 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2194 return ERROR_SUCCESS;
2197 return INET_QueryOption(option, buffer, size, unicode);
2200 static const HANDLEHEADERVtbl FTPSESSIONVtbl = {
2201 FTPSESSION_Destroy,
2202 FTPSESSION_CloseConnection,
2203 FTPSESSION_QueryOption,
2204 NULL,
2205 NULL,
2206 NULL,
2207 NULL,
2208 NULL,
2209 NULL
2213 /***********************************************************************
2214 * FTP_Connect (internal)
2216 * Connect to a ftp server
2218 * RETURNS
2219 * HINTERNET a session handle on success
2220 * NULL on failure
2222 * NOTES:
2224 * Windows uses 'anonymous' as the username, when given a NULL username
2225 * and a NULL password. The password is first looked up in:
2227 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2229 * If this entry is not present it uses the current username as the password.
2233 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
2234 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2235 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2236 DWORD dwInternalFlags)
2238 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2239 'M','i','c','r','o','s','o','f','t','\\',
2240 'W','i','n','d','o','w','s','\\',
2241 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2242 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2243 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2244 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2245 static const WCHAR szEmpty[] = {'\0'};
2246 struct sockaddr_in socketAddr;
2247 INT nsocket = -1;
2248 UINT sock_namelen;
2249 BOOL bSuccess = FALSE;
2250 LPWININETFTPSESSIONW lpwfs = NULL;
2251 HINTERNET handle = NULL;
2253 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2254 hIC, debugstr_w(lpszServerName),
2255 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2257 assert( hIC->hdr.htype == WH_HINIT );
2259 if ((!lpszUserName || !strlenW(lpszUserName)) && lpszPassword)
2261 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2262 goto lerror;
2265 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
2266 if (NULL == lpwfs)
2268 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2269 goto lerror;
2272 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2273 nServerPort = INTERNET_DEFAULT_FTP_PORT;
2275 lpwfs->hdr.htype = WH_HFTPSESSION;
2276 lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
2277 lpwfs->hdr.dwFlags = dwFlags;
2278 lpwfs->hdr.dwContext = dwContext;
2279 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2280 lpwfs->hdr.refs = 1;
2281 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2282 lpwfs->download_in_progress = NULL;
2283 lpwfs->sndSocket = -1;
2284 lpwfs->lstnSocket = -1;
2285 lpwfs->pasvSocket = -1;
2287 WININET_AddRef( &hIC->hdr );
2288 lpwfs->lpAppInfo = hIC;
2289 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2291 handle = WININET_AllocHandle( &lpwfs->hdr );
2292 if( !handle )
2294 ERR("Failed to alloc handle\n");
2295 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2296 goto lerror;
2299 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2300 if(strchrW(hIC->lpszProxy, ' '))
2301 FIXME("Several proxies not implemented.\n");
2302 if(hIC->lpszProxyBypass)
2303 FIXME("Proxy bypass is ignored.\n");
2305 if (!lpszUserName || !strlenW(lpszUserName)) {
2306 HKEY key;
2307 WCHAR szPassword[MAX_PATH];
2308 DWORD len = sizeof(szPassword);
2310 lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
2312 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2313 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2314 /* Nothing in the registry, get the username and use that as the password */
2315 if (!GetUserNameW(szPassword, &len)) {
2316 /* Should never get here, but use an empty password as failsafe */
2317 strcpyW(szPassword, szEmpty);
2320 RegCloseKey(key);
2322 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2323 lpwfs->lpszPassword = WININET_strdupW(szPassword);
2325 else {
2326 lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
2328 if (lpszPassword)
2329 lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
2330 else
2331 lpwfs->lpszPassword = WININET_strdupW(szEmpty);
2334 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2335 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2337 INTERNET_ASYNC_RESULT iar;
2339 iar.dwResult = (DWORD)handle;
2340 iar.dwError = ERROR_SUCCESS;
2342 SendAsyncCallback(&hIC->hdr, dwContext,
2343 INTERNET_STATUS_HANDLE_CREATED, &iar,
2344 sizeof(INTERNET_ASYNC_RESULT));
2347 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2348 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2350 if (!GetAddress(lpszServerName, nServerPort, &socketAddr))
2352 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2353 goto lerror;
2356 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2357 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2359 nsocket = socket(AF_INET,SOCK_STREAM,0);
2360 if (nsocket == -1)
2362 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2363 goto lerror;
2366 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2367 &socketAddr, sizeof(struct sockaddr_in));
2369 if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
2371 ERR("Unable to connect (%s)\n", strerror(errno));
2372 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2374 else
2376 TRACE("Connected to server\n");
2377 lpwfs->sndSocket = nsocket;
2378 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2379 &socketAddr, sizeof(struct sockaddr_in));
2381 sock_namelen = sizeof(lpwfs->socketAddress);
2382 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2384 if (FTP_ConnectToHost(lpwfs))
2386 TRACE("Successfully logged into server\n");
2387 bSuccess = TRUE;
2391 lerror:
2392 if (lpwfs) WININET_Release( &lpwfs->hdr );
2394 if (!bSuccess && handle)
2396 WININET_FreeHandle( handle );
2397 handle = NULL;
2400 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2402 INTERNET_ASYNC_RESULT iar;
2404 iar.dwResult = bSuccess ? (DWORD_PTR)lpwfs : 0;
2405 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2406 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2407 &iar, sizeof(INTERNET_ASYNC_RESULT));
2410 return handle;
2414 /***********************************************************************
2415 * FTP_ConnectToHost (internal)
2417 * Connect to a ftp server
2419 * RETURNS
2420 * TRUE on success
2421 * NULL on failure
2424 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
2426 INT nResCode;
2427 BOOL bSuccess = FALSE;
2429 TRACE("\n");
2430 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2432 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2433 goto lend;
2435 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2436 if (nResCode)
2438 /* Login successful... */
2439 if (nResCode == 230)
2440 bSuccess = TRUE;
2441 /* User name okay, need password... */
2442 else if (nResCode == 331)
2443 bSuccess = FTP_SendPassword(lpwfs);
2444 /* Need account for login... */
2445 else if (nResCode == 332)
2446 bSuccess = FTP_SendAccount(lpwfs);
2447 else
2448 FTP_SetResponseError(nResCode);
2451 TRACE("Returning %d\n", bSuccess);
2452 lend:
2453 return bSuccess;
2457 /***********************************************************************
2458 * FTP_SendCommandA (internal)
2460 * Send command to server
2462 * RETURNS
2463 * TRUE on success
2464 * NULL on failure
2467 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2468 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
2470 DWORD len;
2471 CHAR *buf;
2472 DWORD nBytesSent = 0;
2473 int nRC = 0;
2474 DWORD dwParamLen;
2476 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2478 if (lpfnStatusCB)
2480 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2483 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2484 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2485 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2487 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2488 return FALSE;
2490 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2491 dwParamLen ? lpszParam : "", szCRLF);
2493 TRACE("Sending (%s) len(%d)\n", buf, len);
2494 while((nBytesSent < len) && (nRC != -1))
2496 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2497 nBytesSent += nRC;
2500 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2502 if (lpfnStatusCB)
2504 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2505 &nBytesSent, sizeof(DWORD));
2508 TRACE("Sent %d bytes\n", nBytesSent);
2509 return (nRC != -1);
2512 /***********************************************************************
2513 * FTP_SendCommand (internal)
2515 * Send command to server
2517 * RETURNS
2518 * TRUE on success
2519 * NULL on failure
2522 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2523 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
2525 BOOL ret;
2526 LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
2527 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2528 HeapFree(GetProcessHeap(), 0, lpszParamA);
2529 return ret;
2532 /***********************************************************************
2533 * FTP_ReceiveResponse (internal)
2535 * Receive response from server
2537 * RETURNS
2538 * Reply code on success
2539 * 0 on failure
2542 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD_PTR dwContext)
2544 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2545 DWORD nRecv;
2546 INT rc = 0;
2547 char firstprefix[5];
2548 BOOL multiline = FALSE;
2550 TRACE("socket(%d)\n", lpwfs->sndSocket);
2552 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2554 while(1)
2556 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2557 goto lerror;
2559 if (nRecv >= 3)
2561 if(!multiline)
2563 if(lpszResponse[3] != '-')
2564 break;
2565 else
2566 { /* Start of multiline response. Loop until we get "nnn " */
2567 multiline = TRUE;
2568 memcpy(firstprefix, lpszResponse, 3);
2569 firstprefix[3] = ' ';
2570 firstprefix[4] = '\0';
2573 else
2575 if(!memcmp(firstprefix, lpszResponse, 4))
2576 break;
2581 if (nRecv >= 3)
2583 rc = atoi(lpszResponse);
2585 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2586 &nRecv, sizeof(DWORD));
2589 lerror:
2590 TRACE("return %d\n", rc);
2591 return rc;
2595 /***********************************************************************
2596 * FTP_SendPassword (internal)
2598 * Send password to ftp server
2600 * RETURNS
2601 * TRUE on success
2602 * NULL on failure
2605 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2607 INT nResCode;
2608 BOOL bSuccess = FALSE;
2610 TRACE("\n");
2611 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2612 goto lend;
2614 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2615 if (nResCode)
2617 TRACE("Received reply code %d\n", nResCode);
2618 /* Login successful... */
2619 if (nResCode == 230)
2620 bSuccess = TRUE;
2621 /* Command not implemented, superfluous at the server site... */
2622 /* Need account for login... */
2623 else if (nResCode == 332)
2624 bSuccess = FTP_SendAccount(lpwfs);
2625 else
2626 FTP_SetResponseError(nResCode);
2629 lend:
2630 TRACE("Returning %d\n", bSuccess);
2631 return bSuccess;
2635 /***********************************************************************
2636 * FTP_SendAccount (internal)
2640 * RETURNS
2641 * TRUE on success
2642 * FALSE on failure
2645 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2647 INT nResCode;
2648 BOOL bSuccess = FALSE;
2650 TRACE("\n");
2651 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2652 goto lend;
2654 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2655 if (nResCode)
2656 bSuccess = TRUE;
2657 else
2658 FTP_SetResponseError(nResCode);
2660 lend:
2661 return bSuccess;
2665 /***********************************************************************
2666 * FTP_SendStore (internal)
2668 * Send request to upload file to ftp server
2670 * RETURNS
2671 * TRUE on success
2672 * FALSE on failure
2675 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2677 INT nResCode;
2678 BOOL bSuccess = FALSE;
2680 TRACE("\n");
2681 if (!FTP_InitListenSocket(lpwfs))
2682 goto lend;
2684 if (!FTP_SendType(lpwfs, dwType))
2685 goto lend;
2687 if (!FTP_SendPortOrPasv(lpwfs))
2688 goto lend;
2690 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2691 goto lend;
2692 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2693 if (nResCode)
2695 if (nResCode == 150 || nResCode == 125)
2696 bSuccess = TRUE;
2697 else
2698 FTP_SetResponseError(nResCode);
2701 lend:
2702 if (!bSuccess && lpwfs->lstnSocket != -1)
2704 closesocket(lpwfs->lstnSocket);
2705 lpwfs->lstnSocket = -1;
2708 return bSuccess;
2712 /***********************************************************************
2713 * FTP_InitListenSocket (internal)
2715 * Create a socket to listen for server response
2717 * RETURNS
2718 * TRUE on success
2719 * FALSE on failure
2722 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2724 BOOL bSuccess = FALSE;
2725 socklen_t namelen = sizeof(struct sockaddr_in);
2727 TRACE("\n");
2729 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2730 if (lpwfs->lstnSocket == -1)
2732 TRACE("Unable to create listening socket\n");
2733 goto lend;
2736 /* We obtain our ip addr from the name of the command channel socket */
2737 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2739 /* and get the system to assign us a port */
2740 lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
2742 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2744 TRACE("Unable to bind socket\n");
2745 goto lend;
2748 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2750 TRACE("listen failed\n");
2751 goto lend;
2754 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2755 bSuccess = TRUE;
2757 lend:
2758 if (!bSuccess && lpwfs->lstnSocket != -1)
2760 closesocket(lpwfs->lstnSocket);
2761 lpwfs->lstnSocket = -1;
2764 return bSuccess;
2768 /***********************************************************************
2769 * FTP_SendType (internal)
2771 * Tell server type of data being transferred
2773 * RETURNS
2774 * TRUE on success
2775 * FALSE on failure
2777 * W98SE doesn't cache the type that's currently set
2778 * (i.e. it sends it always),
2779 * so we probably don't want to do that either.
2781 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2783 INT nResCode;
2784 WCHAR type[] = { 'I','\0' };
2785 BOOL bSuccess = FALSE;
2787 TRACE("\n");
2788 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2789 type[0] = 'A';
2791 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2792 goto lend;
2794 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2795 if (nResCode)
2797 if (nResCode == 2)
2798 bSuccess = TRUE;
2799 else
2800 FTP_SetResponseError(nResCode);
2803 lend:
2804 return bSuccess;
2808 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2809 /***********************************************************************
2810 * FTP_GetFileSize (internal)
2812 * Retrieves from the server the size of the given file
2814 * RETURNS
2815 * TRUE on success
2816 * FALSE on failure
2819 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2821 INT nResCode;
2822 BOOL bSuccess = FALSE;
2824 TRACE("\n");
2826 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2827 goto lend;
2829 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2830 if (nResCode)
2832 if (nResCode == 213) {
2833 /* Now parses the output to get the actual file size */
2834 int i;
2835 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2837 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2838 if (lpszResponseBuffer[i] == '\0') return FALSE;
2839 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2841 bSuccess = TRUE;
2842 } else {
2843 FTP_SetResponseError(nResCode);
2847 lend:
2848 return bSuccess;
2850 #endif
2853 /***********************************************************************
2854 * FTP_SendPort (internal)
2856 * Tell server which port to use
2858 * RETURNS
2859 * TRUE on success
2860 * FALSE on failure
2863 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2865 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2866 INT nResCode;
2867 WCHAR szIPAddress[64];
2868 BOOL bSuccess = FALSE;
2869 TRACE("\n");
2871 sprintfW(szIPAddress, szIPFormat,
2872 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2873 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2874 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2875 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2876 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2877 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2879 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2880 goto lend;
2882 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2883 if (nResCode)
2885 if (nResCode == 200)
2886 bSuccess = TRUE;
2887 else
2888 FTP_SetResponseError(nResCode);
2891 lend:
2892 return bSuccess;
2896 /***********************************************************************
2897 * FTP_DoPassive (internal)
2899 * Tell server that we want to do passive transfers
2900 * and connect data socket
2902 * RETURNS
2903 * TRUE on success
2904 * FALSE on failure
2907 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2909 INT nResCode;
2910 BOOL bSuccess = FALSE;
2912 TRACE("\n");
2913 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2914 goto lend;
2916 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2917 if (nResCode)
2919 if (nResCode == 227)
2921 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2922 LPSTR p;
2923 int f[6];
2924 int i;
2925 char *pAddr, *pPort;
2926 INT nsocket = -1;
2927 struct sockaddr_in dataSocketAddress;
2929 p = lpszResponseBuffer+4; /* skip status code */
2930 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
2932 if (*p == '\0')
2934 ERR("no address found in response, aborting\n");
2935 goto lend;
2938 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
2939 &f[4], &f[5]) != 6)
2941 ERR("unknown response address format '%s', aborting\n", p);
2942 goto lend;
2944 for (i=0; i < 6; i++)
2945 f[i] = f[i] & 0xff;
2947 dataSocketAddress = lpwfs->socketAddress;
2948 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2949 pPort = (char *)&(dataSocketAddress.sin_port);
2950 pAddr[0] = f[0];
2951 pAddr[1] = f[1];
2952 pAddr[2] = f[2];
2953 pAddr[3] = f[3];
2954 pPort[0] = f[4];
2955 pPort[1] = f[5];
2957 nsocket = socket(AF_INET,SOCK_STREAM,0);
2958 if (nsocket == -1)
2959 goto lend;
2961 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2963 ERR("can't connect passive FTP data port.\n");
2964 closesocket(nsocket);
2965 goto lend;
2967 lpwfs->pasvSocket = nsocket;
2968 bSuccess = TRUE;
2970 else
2971 FTP_SetResponseError(nResCode);
2974 lend:
2975 return bSuccess;
2979 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2981 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2983 if (!FTP_DoPassive(lpwfs))
2984 return FALSE;
2986 else
2988 if (!FTP_SendPort(lpwfs))
2989 return FALSE;
2991 return TRUE;
2995 /***********************************************************************
2996 * FTP_GetDataSocket (internal)
2998 * Either accepts an incoming data socket connection from the server
2999 * or just returns the already opened socket after a PASV command
3000 * in case of passive FTP.
3003 * RETURNS
3004 * TRUE on success
3005 * FALSE on failure
3008 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
3010 struct sockaddr_in saddr;
3011 socklen_t addrlen = sizeof(struct sockaddr);
3013 TRACE("\n");
3014 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3016 *nDataSocket = lpwfs->pasvSocket;
3018 else
3020 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3021 closesocket(lpwfs->lstnSocket);
3022 lpwfs->lstnSocket = -1;
3024 return *nDataSocket != -1;
3028 /***********************************************************************
3029 * FTP_SendData (internal)
3031 * Send data to the server
3033 * RETURNS
3034 * TRUE on success
3035 * FALSE on failure
3038 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
3040 BY_HANDLE_FILE_INFORMATION fi;
3041 DWORD nBytesRead = 0;
3042 DWORD nBytesSent = 0;
3043 DWORD nTotalSent = 0;
3044 DWORD nBytesToSend, nLen;
3045 int nRC = 1;
3046 time_t s_long_time, e_long_time;
3047 LONG nSeconds;
3048 CHAR *lpszBuffer;
3050 TRACE("\n");
3051 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3053 /* Get the size of the file. */
3054 GetFileInformationByHandle(hFile, &fi);
3055 time(&s_long_time);
3059 nBytesToSend = nBytesRead - nBytesSent;
3061 if (nBytesToSend <= 0)
3063 /* Read data from file. */
3064 nBytesSent = 0;
3065 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3066 ERR("Failed reading from file\n");
3068 if (nBytesRead > 0)
3069 nBytesToSend = nBytesRead;
3070 else
3071 break;
3074 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3075 DATA_PACKET_SIZE : nBytesToSend;
3076 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3078 if (nRC != -1)
3080 nBytesSent += nRC;
3081 nTotalSent += nRC;
3084 /* Do some computation to display the status. */
3085 time(&e_long_time);
3086 nSeconds = e_long_time - s_long_time;
3087 if( nSeconds / 60 > 0 )
3089 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3090 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3091 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3093 else
3095 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3096 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3097 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3099 } while (nRC != -1);
3101 TRACE("file transfer complete!\n");
3103 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3105 return nTotalSent;
3109 /***********************************************************************
3110 * FTP_SendRetrieve (internal)
3112 * Send request to retrieve a file
3114 * RETURNS
3115 * Number of bytes to be received on success
3116 * 0 on failure
3119 static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3121 INT nResCode;
3122 BOOL ret;
3124 TRACE("\n");
3125 if (!(ret = FTP_InitListenSocket(lpwfs)))
3126 goto lend;
3128 if (!(ret = FTP_SendType(lpwfs, dwType)))
3129 goto lend;
3131 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3132 goto lend;
3134 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3135 goto lend;
3137 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3138 if ((nResCode != 125) && (nResCode != 150)) {
3139 /* That means that we got an error getting the file. */
3140 FTP_SetResponseError(nResCode);
3141 ret = FALSE;
3144 lend:
3145 if (!ret && lpwfs->lstnSocket != -1)
3147 closesocket(lpwfs->lstnSocket);
3148 lpwfs->lstnSocket = -1;
3151 return ret;
3155 /***********************************************************************
3156 * FTP_RetrieveData (internal)
3158 * Retrieve data from server
3160 * RETURNS
3161 * TRUE on success
3162 * FALSE on failure
3165 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
3167 DWORD nBytesWritten;
3168 INT nRC = 0;
3169 CHAR *lpszBuffer;
3171 TRACE("\n");
3173 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3174 if (NULL == lpszBuffer)
3176 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3177 return FALSE;
3180 while (nRC != -1)
3182 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3183 if (nRC != -1)
3185 /* other side closed socket. */
3186 if (nRC == 0)
3187 goto recv_end;
3188 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3192 TRACE("Data transfer complete\n");
3194 recv_end:
3195 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3197 return (nRC != -1);
3200 /***********************************************************************
3201 * FTPFINDNEXT_Destroy (internal)
3203 * Deallocate session handle
3205 static void FTPFINDNEXT_Destroy(WININETHANDLEHEADER *hdr)
3207 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3208 DWORD i;
3210 TRACE("\n");
3212 WININET_Release(&lpwfn->lpFtpSession->hdr);
3214 for (i = 0; i < lpwfn->size; i++)
3216 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3219 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3220 HeapFree(GetProcessHeap(), 0, lpwfn);
3223 static DWORD WINAPI FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3225 WIN32_FIND_DATAW *find_data = data;
3226 DWORD res = ERROR_SUCCESS;
3228 TRACE("index(%d) size(%d)\n", find->index, find->size);
3230 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3232 if (find->index < find->size) {
3233 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3234 find->index++;
3236 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3237 }else {
3238 res = ERROR_NO_MORE_FILES;
3241 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3243 INTERNET_ASYNC_RESULT iar;
3245 iar.dwResult = (res == ERROR_SUCCESS);
3246 iar.dwError = res;
3248 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3249 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3250 sizeof(INTERNET_ASYNC_RESULT));
3253 return res;
3256 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3258 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3260 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3263 static DWORD FTPFINDNEXT_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3265 switch(option) {
3266 case INTERNET_OPTION_HANDLE_TYPE:
3267 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3269 if (*size < sizeof(ULONG))
3270 return ERROR_INSUFFICIENT_BUFFER;
3272 *size = sizeof(DWORD);
3273 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3274 return ERROR_SUCCESS;
3277 return INET_QueryOption(option, buffer, size, unicode);
3280 static DWORD FTPFINDNEXT_FindNextFileW(WININETHANDLEHEADER *hdr, void *data)
3282 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3284 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3286 WORKREQUEST workRequest;
3287 struct WORKREQ_FTPFINDNEXTW *req;
3289 workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3290 workRequest.hdr = WININET_AddRef( &find->hdr );
3291 req = &workRequest.u.FtpFindNextW;
3292 req->lpFindFileData = data;
3294 INTERNET_AsyncCall(&workRequest);
3296 return ERROR_SUCCESS;
3299 return FTPFINDNEXT_FindNextFileProc(find, data);
3302 static const HANDLEHEADERVtbl FTPFINDNEXTVtbl = {
3303 FTPFINDNEXT_Destroy,
3304 NULL,
3305 FTPFINDNEXT_QueryOption,
3306 NULL,
3307 NULL,
3308 NULL,
3309 NULL,
3310 NULL,
3311 FTPFINDNEXT_FindNextFileW
3314 /***********************************************************************
3315 * FTP_ReceiveFileList (internal)
3317 * Read file list from server
3319 * RETURNS
3320 * Handle to file list on success
3321 * NULL on failure
3324 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3325 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3327 DWORD dwSize = 0;
3328 LPFILEPROPERTIESW lpafp = NULL;
3329 LPWININETFTPFINDNEXTW lpwfn = NULL;
3330 HINTERNET handle = 0;
3332 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3334 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3336 if(lpFindFileData)
3337 FTP_ConvertFileProp(lpafp, lpFindFileData);
3339 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3340 if (lpwfn)
3342 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3343 lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3344 lpwfn->hdr.dwContext = dwContext;
3345 lpwfn->hdr.refs = 1;
3346 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3347 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3348 lpwfn->size = dwSize;
3349 lpwfn->lpafp = lpafp;
3351 WININET_AddRef( &lpwfs->hdr );
3352 lpwfn->lpFtpSession = lpwfs;
3353 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3355 handle = WININET_AllocHandle( &lpwfn->hdr );
3359 if( lpwfn )
3360 WININET_Release( &lpwfn->hdr );
3362 TRACE("Matched %d files\n", dwSize);
3363 return handle;
3367 /***********************************************************************
3368 * FTP_ConvertFileProp (internal)
3370 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3372 * RETURNS
3373 * TRUE on success
3374 * FALSE on failure
3377 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3379 BOOL bSuccess = FALSE;
3381 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3383 if (lpafp)
3385 /* Convert 'Unix' time to Windows time */
3386 RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
3387 (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
3388 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3389 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3391 /* Not all fields are filled in */
3392 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3393 lpFindFileData->nFileSizeLow = lpafp->nSize;
3395 if (lpafp->bIsDirectory)
3396 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3398 if (lpafp->lpszName)
3399 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3401 bSuccess = TRUE;
3404 return bSuccess;
3407 /***********************************************************************
3408 * FTP_ParseNextFile (internal)
3410 * Parse the next line in file listing
3412 * RETURNS
3413 * TRUE on success
3414 * FALSE on failure
3416 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3418 static const char szSpace[] = " \t";
3419 DWORD nBufLen;
3420 char *pszLine;
3421 char *pszToken;
3422 char *pszTmp;
3423 BOOL found = FALSE;
3424 int i;
3426 lpfp->lpszName = NULL;
3427 do {
3428 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3429 return FALSE;
3431 pszToken = strtok(pszLine, szSpace);
3432 /* ls format
3433 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3435 * For instance:
3436 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3438 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3439 if(!FTP_ParsePermission(pszToken, lpfp))
3440 lpfp->bIsDirectory = FALSE;
3441 for(i=0; i<=3; i++) {
3442 if(!(pszToken = strtok(NULL, szSpace)))
3443 break;
3445 if(!pszToken) continue;
3446 if(lpfp->bIsDirectory) {
3447 TRACE("Is directory\n");
3448 lpfp->nSize = 0;
3450 else {
3451 TRACE("Size: %s\n", pszToken);
3452 lpfp->nSize = atol(pszToken);
3455 lpfp->tmLastModified.tm_sec = 0;
3456 lpfp->tmLastModified.tm_min = 0;
3457 lpfp->tmLastModified.tm_hour = 0;
3458 lpfp->tmLastModified.tm_mday = 0;
3459 lpfp->tmLastModified.tm_mon = 0;
3460 lpfp->tmLastModified.tm_year = 0;
3462 /* Determine month */
3463 pszToken = strtok(NULL, szSpace);
3464 if(!pszToken) continue;
3465 if(strlen(pszToken) >= 3) {
3466 pszToken[3] = 0;
3467 if((pszTmp = StrStrIA(szMonths, pszToken)))
3468 lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
3470 /* Determine day */
3471 pszToken = strtok(NULL, szSpace);
3472 if(!pszToken) continue;
3473 lpfp->tmLastModified.tm_mday = atoi(pszToken);
3474 /* Determine time or year */
3475 pszToken = strtok(NULL, szSpace);
3476 if(!pszToken) continue;
3477 if((pszTmp = strchr(pszToken, ':'))) {
3478 struct tm* apTM;
3479 time_t aTime;
3480 *pszTmp = 0;
3481 pszTmp++;
3482 lpfp->tmLastModified.tm_min = atoi(pszTmp);
3483 lpfp->tmLastModified.tm_hour = atoi(pszToken);
3484 time(&aTime);
3485 apTM = localtime(&aTime);
3486 lpfp->tmLastModified.tm_year = apTM->tm_year;
3488 else {
3489 lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
3490 lpfp->tmLastModified.tm_hour = 12;
3492 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3493 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3494 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3495 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3497 pszToken = strtok(NULL, szSpace);
3498 if(!pszToken) continue;
3499 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3500 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3502 /* NT way of parsing ... :
3504 07-13-03 08:55PM <DIR> sakpatch
3505 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3507 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3508 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3510 sscanf(pszToken, "%d-%d-%d",
3511 &lpfp->tmLastModified.tm_mon,
3512 &lpfp->tmLastModified.tm_mday,
3513 &lpfp->tmLastModified.tm_year);
3515 /* Hacky and bad Y2K protection :-) */
3516 if (lpfp->tmLastModified.tm_year < 70)
3517 lpfp->tmLastModified.tm_year += 100;
3519 pszToken = strtok(NULL, szSpace);
3520 if(!pszToken) continue;
3521 sscanf(pszToken, "%d:%d",
3522 &lpfp->tmLastModified.tm_hour,
3523 &lpfp->tmLastModified.tm_min);
3524 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3525 lpfp->tmLastModified.tm_hour += 12;
3527 lpfp->tmLastModified.tm_sec = 0;
3529 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3530 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3531 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3532 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3534 pszToken = strtok(NULL, szSpace);
3535 if(!pszToken) continue;
3536 if(!strcasecmp(pszToken, "<DIR>")) {
3537 lpfp->bIsDirectory = TRUE;
3538 lpfp->nSize = 0;
3539 TRACE("Is directory\n");
3541 else {
3542 lpfp->bIsDirectory = FALSE;
3543 lpfp->nSize = atol(pszToken);
3544 TRACE("Size: %d\n", lpfp->nSize);
3547 pszToken = strtok(NULL, szSpace);
3548 if(!pszToken) continue;
3549 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3550 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3552 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3553 else if(pszToken[0] == '+') {
3554 FIXME("EPLF Format not implemented\n");
3557 if(lpfp->lpszName) {
3558 if((lpszSearchFile == NULL) ||
3559 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3560 found = TRUE;
3561 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3563 else {
3564 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3565 lpfp->lpszName = NULL;
3568 } while(!found);
3569 return TRUE;
3572 /***********************************************************************
3573 * FTP_ParseDirectory (internal)
3575 * Parse string of directory information
3577 * RETURNS
3578 * TRUE on success
3579 * FALSE on failure
3581 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3582 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3584 BOOL bSuccess = TRUE;
3585 INT sizeFilePropArray = 500;/*20; */
3586 INT indexFilePropArray = -1;
3588 TRACE("\n");
3590 /* Allocate initial file properties array */
3591 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3592 if (!*lpafp)
3593 return FALSE;
3595 do {
3596 if (indexFilePropArray+1 >= sizeFilePropArray)
3598 LPFILEPROPERTIESW tmpafp;
3600 sizeFilePropArray *= 2;
3601 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3602 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3603 if (NULL == tmpafp)
3605 bSuccess = FALSE;
3606 break;
3609 *lpafp = tmpafp;
3611 indexFilePropArray++;
3612 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3614 if (bSuccess && indexFilePropArray)
3616 if (indexFilePropArray < sizeFilePropArray - 1)
3618 LPFILEPROPERTIESW tmpafp;
3620 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3621 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3622 if (NULL != tmpafp)
3623 *lpafp = tmpafp;
3625 *dwfp = indexFilePropArray;
3627 else
3629 HeapFree(GetProcessHeap(), 0, *lpafp);
3630 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3631 bSuccess = FALSE;
3634 return bSuccess;
3638 /***********************************************************************
3639 * FTP_ParsePermission (internal)
3641 * Parse permission string of directory information
3643 * RETURNS
3644 * TRUE on success
3645 * FALSE on failure
3648 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3650 BOOL bSuccess = TRUE;
3651 unsigned short nPermission = 0;
3652 INT nPos = 1;
3653 INT nLast = 9;
3655 TRACE("\n");
3656 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3658 bSuccess = FALSE;
3659 return bSuccess;
3662 lpfp->bIsDirectory = (*lpszPermission == 'd');
3665 switch (nPos)
3667 case 1:
3668 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3669 break;
3670 case 2:
3671 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3672 break;
3673 case 3:
3674 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3675 break;
3676 case 4:
3677 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3678 break;
3679 case 5:
3680 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3681 break;
3682 case 6:
3683 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3684 break;
3685 case 7:
3686 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3687 break;
3688 case 8:
3689 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3690 break;
3691 case 9:
3692 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3693 break;
3695 nPos++;
3696 }while (nPos <= nLast);
3698 lpfp->permissions = nPermission;
3699 return bSuccess;
3703 /***********************************************************************
3704 * FTP_SetResponseError (internal)
3706 * Set the appropriate error code for a given response from the server
3708 * RETURNS
3711 static DWORD FTP_SetResponseError(DWORD dwResponse)
3713 DWORD dwCode = 0;
3715 switch(dwResponse)
3717 case 425: /* Cannot open data connection. */
3718 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3719 break;
3721 case 426: /* Connection closed, transer aborted. */
3722 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3723 break;
3725 case 530: /* Not logged in. Login incorrect. */
3726 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3727 break;
3729 case 421: /* Service not available - Server may be shutting down. */
3730 case 450: /* File action not taken. File may be busy. */
3731 case 451: /* Action aborted. Server error. */
3732 case 452: /* Action not taken. Insufficient storage space on server. */
3733 case 500: /* Syntax error. Command unrecognized. */
3734 case 501: /* Syntax error. Error in parameters or arguments. */
3735 case 502: /* Command not implemented. */
3736 case 503: /* Bad sequence of commands. */
3737 case 504: /* Command not implemented for that parameter. */
3738 case 532: /* Need account for storing files */
3739 case 550: /* File action not taken. File not found or no access. */
3740 case 551: /* Requested action aborted. Page type unknown */
3741 case 552: /* Action aborted. Exceeded storage allocation */
3742 case 553: /* Action not taken. File name not allowed. */
3744 default:
3745 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3746 break;
3749 INTERNET_SetLastError(dwCode);
3750 return dwCode;