imm32: Avoid false FIXMEs.
[wine/multimedia.git] / dlls / wininet / ftp.c
blob36e90057008630357d910e0d3681f9a0f725b36b
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 FIXME("Not implemented option %d\n", option);
1231 return ERROR_INTERNET_INVALID_OPTION;
1234 static DWORD FTPFILE_ReadFile(WININETHANDLEHEADER *hdr, void *buffer, DWORD size, DWORD *read)
1236 WININETFTPFILE *file = (WININETFTPFILE*)hdr;
1237 int res;
1239 if (file->nDataSocket == -1)
1240 return ERROR_INTERNET_DISCONNECTED;
1242 /* FIXME: FTP should use NETCON_ stuff */
1243 res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1244 *read = res>0 ? res : 0;
1246 return res>=0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME*/
1249 static BOOL FTPFILE_WriteFile(WININETHANDLEHEADER *hdr, const void *buffer, DWORD size, DWORD *written)
1251 LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
1252 int res;
1254 res = send(lpwh->nDataSocket, buffer, size, 0);
1256 *written = res>0 ? res : 0;
1257 return res >= 0;
1260 static const HANDLEHEADERVtbl FTPFILEVtbl = {
1261 FTPFILE_Destroy,
1262 NULL,
1263 FTPFILE_QueryOption,
1264 NULL,
1265 FTPFILE_ReadFile,
1266 NULL,
1267 FTPFILE_WriteFile,
1268 NULL,
1269 NULL
1272 /***********************************************************************
1273 * FTP_FtpOpenFileW (Internal)
1275 * Open a remote file for writing or reading
1277 * RETURNS
1278 * HINTERNET handle on success
1279 * NULL on failure
1282 HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
1283 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1284 DWORD_PTR dwContext)
1286 INT nDataSocket;
1287 BOOL bSuccess = FALSE;
1288 LPWININETFTPFILE lpwh = NULL;
1289 LPWININETAPPINFOW hIC = NULL;
1290 HINTERNET handle = NULL;
1292 TRACE("\n");
1294 /* Clear any error information */
1295 INTERNET_SetLastError(0);
1297 if (GENERIC_READ == fdwAccess)
1299 /* Set up socket to retrieve data */
1300 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1302 else if (GENERIC_WRITE == fdwAccess)
1304 /* Set up socket to send data */
1305 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1308 /* Get data socket to server */
1309 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1311 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPFILE));
1312 lpwh->hdr.htype = WH_HFILE;
1313 lpwh->hdr.vtbl = &FTPFILEVtbl;
1314 lpwh->hdr.dwFlags = dwFlags;
1315 lpwh->hdr.dwContext = dwContext;
1316 lpwh->hdr.refs = 1;
1317 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1318 lpwh->nDataSocket = nDataSocket;
1319 lpwh->session_deleted = FALSE;
1321 WININET_AddRef( &lpwfs->hdr );
1322 lpwh->lpFtpSession = lpwfs;
1323 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1325 handle = WININET_AllocHandle( &lpwh->hdr );
1326 if( !handle )
1327 goto lend;
1329 /* Indicate that a download is currently in progress */
1330 lpwfs->download_in_progress = lpwh;
1333 if (lpwfs->lstnSocket != -1)
1334 closesocket(lpwfs->lstnSocket);
1336 hIC = lpwfs->lpAppInfo;
1337 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1339 INTERNET_ASYNC_RESULT iar;
1341 if (lpwh)
1343 iar.dwResult = (DWORD)handle;
1344 iar.dwError = ERROR_SUCCESS;
1345 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1346 &iar, sizeof(INTERNET_ASYNC_RESULT));
1349 iar.dwResult = (DWORD)bSuccess;
1350 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1351 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1352 &iar, sizeof(INTERNET_ASYNC_RESULT));
1355 lend:
1356 if( lpwh )
1357 WININET_Release( &lpwh->hdr );
1359 return handle;
1363 /***********************************************************************
1364 * FtpGetFileA (WININET.@)
1366 * Retrieve file from the FTP server
1368 * RETURNS
1369 * TRUE on success
1370 * FALSE on failure
1373 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1374 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1375 DWORD_PTR dwContext)
1377 LPWSTR lpwzRemoteFile;
1378 LPWSTR lpwzNewFile;
1379 BOOL ret;
1381 lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
1382 lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
1383 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1384 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1385 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1386 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1387 return ret;
1391 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1393 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1394 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1396 TRACE("%p\n", lpwfs);
1398 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1399 req->lpszNewFile, req->fFailIfExists,
1400 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1401 HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1402 HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1406 /***********************************************************************
1407 * FtpGetFileW (WININET.@)
1409 * Retrieve file from the FTP server
1411 * RETURNS
1412 * TRUE on success
1413 * FALSE on failure
1416 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1417 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1418 DWORD_PTR dwContext)
1420 LPWININETFTPSESSIONW lpwfs;
1421 LPWININETAPPINFOW hIC = NULL;
1422 BOOL r = FALSE;
1424 if (!lpszRemoteFile || !lpszNewFile)
1426 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1427 return FALSE;
1430 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
1431 if (!lpwfs)
1433 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1434 return FALSE;
1437 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1439 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1440 goto lend;
1443 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1445 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1446 goto lend;
1449 if (lpwfs->download_in_progress != NULL)
1451 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1452 goto lend;
1455 hIC = lpwfs->lpAppInfo;
1456 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1458 WORKREQUEST workRequest;
1459 struct WORKREQ_FTPGETFILEW *req;
1461 workRequest.asyncproc = AsyncFtpGetFileProc;
1462 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1463 req = &workRequest.u.FtpGetFileW;
1464 req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
1465 req->lpszNewFile = WININET_strdupW(lpszNewFile);
1466 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1467 req->fFailIfExists = fFailIfExists;
1468 req->dwFlags = dwInternetFlags;
1469 req->dwContext = dwContext;
1471 r = INTERNET_AsyncCall(&workRequest);
1473 else
1475 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1476 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1479 lend:
1480 WININET_Release( &lpwfs->hdr );
1482 return r;
1486 /***********************************************************************
1487 * FTP_FtpGetFileW (Internal)
1489 * Retrieve file from the FTP server
1491 * RETURNS
1492 * TRUE on success
1493 * FALSE on failure
1496 static BOOL FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1497 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1498 DWORD_PTR dwContext)
1500 BOOL bSuccess = FALSE;
1501 HANDLE hFile;
1502 LPWININETAPPINFOW hIC = NULL;
1504 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1506 /* Clear any error information */
1507 INTERNET_SetLastError(0);
1509 /* Ensure we can write to lpszNewfile by opening it */
1510 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1511 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1512 if (INVALID_HANDLE_VALUE == hFile)
1513 return FALSE;
1515 /* Set up socket to retrieve data */
1516 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1518 INT nDataSocket;
1520 /* Get data socket to server */
1521 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1523 INT nResCode;
1525 /* Receive data */
1526 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1527 closesocket(nDataSocket);
1529 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1530 if (nResCode)
1532 if (nResCode == 226)
1533 bSuccess = TRUE;
1534 else
1535 FTP_SetResponseError(nResCode);
1540 if (lpwfs->lstnSocket != -1)
1541 closesocket(lpwfs->lstnSocket);
1543 CloseHandle(hFile);
1545 hIC = lpwfs->lpAppInfo;
1546 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1548 INTERNET_ASYNC_RESULT iar;
1550 iar.dwResult = (DWORD)bSuccess;
1551 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1552 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1553 &iar, sizeof(INTERNET_ASYNC_RESULT));
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 FIXME("Not implemented option %d\n", option);
2198 return ERROR_INTERNET_INVALID_OPTION;
2201 static const HANDLEHEADERVtbl FTPSESSIONVtbl = {
2202 FTPSESSION_Destroy,
2203 FTPSESSION_CloseConnection,
2204 FTPSESSION_QueryOption,
2205 NULL,
2206 NULL,
2207 NULL,
2208 NULL,
2209 NULL,
2210 NULL
2214 /***********************************************************************
2215 * FTP_Connect (internal)
2217 * Connect to a ftp server
2219 * RETURNS
2220 * HINTERNET a session handle on success
2221 * NULL on failure
2223 * NOTES:
2225 * Windows uses 'anonymous' as the username, when given a NULL username
2226 * and a NULL password. The password is first looked up in:
2228 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2230 * If this entry is not present it uses the current username as the password.
2234 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
2235 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2236 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2237 DWORD dwInternalFlags)
2239 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2240 'M','i','c','r','o','s','o','f','t','\\',
2241 'W','i','n','d','o','w','s','\\',
2242 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2243 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2244 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2245 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2246 static const WCHAR szEmpty[] = {'\0'};
2247 struct sockaddr_in socketAddr;
2248 INT nsocket = -1;
2249 UINT sock_namelen;
2250 BOOL bSuccess = FALSE;
2251 LPWININETFTPSESSIONW lpwfs = NULL;
2252 HINTERNET handle = NULL;
2254 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2255 hIC, debugstr_w(lpszServerName),
2256 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2258 assert( hIC->hdr.htype == WH_HINIT );
2260 if (NULL == lpszUserName && NULL != lpszPassword)
2262 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2263 goto lerror;
2266 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
2267 if (NULL == lpwfs)
2269 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2270 goto lerror;
2273 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2274 nServerPort = INTERNET_DEFAULT_FTP_PORT;
2276 lpwfs->hdr.htype = WH_HFTPSESSION;
2277 lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
2278 lpwfs->hdr.dwFlags = dwFlags;
2279 lpwfs->hdr.dwContext = dwContext;
2280 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2281 lpwfs->hdr.refs = 1;
2282 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2283 lpwfs->download_in_progress = NULL;
2284 lpwfs->sndSocket = -1;
2285 lpwfs->lstnSocket = -1;
2286 lpwfs->pasvSocket = -1;
2288 WININET_AddRef( &hIC->hdr );
2289 lpwfs->lpAppInfo = hIC;
2290 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2292 handle = WININET_AllocHandle( &lpwfs->hdr );
2293 if( !handle )
2295 ERR("Failed to alloc handle\n");
2296 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2297 goto lerror;
2300 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2301 if(strchrW(hIC->lpszProxy, ' '))
2302 FIXME("Several proxies not implemented.\n");
2303 if(hIC->lpszProxyBypass)
2304 FIXME("Proxy bypass is ignored.\n");
2306 if ( !lpszUserName) {
2307 HKEY key;
2308 WCHAR szPassword[MAX_PATH];
2309 DWORD len = sizeof(szPassword);
2311 lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
2313 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2314 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2315 /* Nothing in the registry, get the username and use that as the password */
2316 if (!GetUserNameW(szPassword, &len)) {
2317 /* Should never get here, but use an empty password as failsafe */
2318 strcpyW(szPassword, szEmpty);
2321 RegCloseKey(key);
2323 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2324 lpwfs->lpszPassword = WININET_strdupW(szPassword);
2326 else {
2327 lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
2329 if (lpszPassword)
2330 lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
2331 else
2332 lpwfs->lpszPassword = WININET_strdupW(szEmpty);
2335 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2336 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2338 INTERNET_ASYNC_RESULT iar;
2340 iar.dwResult = (DWORD)handle;
2341 iar.dwError = ERROR_SUCCESS;
2343 SendAsyncCallback(&hIC->hdr, dwContext,
2344 INTERNET_STATUS_HANDLE_CREATED, &iar,
2345 sizeof(INTERNET_ASYNC_RESULT));
2348 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2349 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2351 if (!GetAddress(lpszServerName, nServerPort, &socketAddr))
2353 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2354 goto lerror;
2357 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2358 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2360 nsocket = socket(AF_INET,SOCK_STREAM,0);
2361 if (nsocket == -1)
2363 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2364 goto lerror;
2367 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2368 &socketAddr, sizeof(struct sockaddr_in));
2370 if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
2372 ERR("Unable to connect (%s)\n", strerror(errno));
2373 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2375 else
2377 TRACE("Connected to server\n");
2378 lpwfs->sndSocket = nsocket;
2379 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2380 &socketAddr, sizeof(struct sockaddr_in));
2382 sock_namelen = sizeof(lpwfs->socketAddress);
2383 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2385 if (FTP_ConnectToHost(lpwfs))
2387 TRACE("Successfully logged into server\n");
2388 bSuccess = TRUE;
2392 lerror:
2393 if (lpwfs) WININET_Release( &lpwfs->hdr );
2395 if (!bSuccess && handle)
2397 WININET_FreeHandle( handle );
2398 handle = NULL;
2401 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2403 INTERNET_ASYNC_RESULT iar;
2405 iar.dwResult = bSuccess ? (DWORD_PTR)lpwfs : 0;
2406 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2407 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2408 &iar, sizeof(INTERNET_ASYNC_RESULT));
2411 return handle;
2415 /***********************************************************************
2416 * FTP_ConnectToHost (internal)
2418 * Connect to a ftp server
2420 * RETURNS
2421 * TRUE on success
2422 * NULL on failure
2425 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
2427 INT nResCode;
2428 BOOL bSuccess = FALSE;
2430 TRACE("\n");
2431 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2433 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2434 goto lend;
2436 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2437 if (nResCode)
2439 /* Login successful... */
2440 if (nResCode == 230)
2441 bSuccess = TRUE;
2442 /* User name okay, need password... */
2443 else if (nResCode == 331)
2444 bSuccess = FTP_SendPassword(lpwfs);
2445 /* Need account for login... */
2446 else if (nResCode == 332)
2447 bSuccess = FTP_SendAccount(lpwfs);
2448 else
2449 FTP_SetResponseError(nResCode);
2452 TRACE("Returning %d\n", bSuccess);
2453 lend:
2454 return bSuccess;
2458 /***********************************************************************
2459 * FTP_SendCommandA (internal)
2461 * Send command to server
2463 * RETURNS
2464 * TRUE on success
2465 * NULL on failure
2468 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2469 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
2471 DWORD len;
2472 CHAR *buf;
2473 DWORD nBytesSent = 0;
2474 int nRC = 0;
2475 DWORD dwParamLen;
2477 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2479 if (lpfnStatusCB)
2481 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2484 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2485 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2486 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2488 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2489 return FALSE;
2491 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2492 dwParamLen ? lpszParam : "", szCRLF);
2494 TRACE("Sending (%s) len(%d)\n", buf, len);
2495 while((nBytesSent < len) && (nRC != -1))
2497 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2498 nBytesSent += nRC;
2501 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2503 if (lpfnStatusCB)
2505 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2506 &nBytesSent, sizeof(DWORD));
2509 TRACE("Sent %d bytes\n", nBytesSent);
2510 return (nRC != -1);
2513 /***********************************************************************
2514 * FTP_SendCommand (internal)
2516 * Send command to server
2518 * RETURNS
2519 * TRUE on success
2520 * NULL on failure
2523 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2524 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
2526 BOOL ret;
2527 LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
2528 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2529 HeapFree(GetProcessHeap(), 0, lpszParamA);
2530 return ret;
2533 /***********************************************************************
2534 * FTP_ReceiveResponse (internal)
2536 * Receive response from server
2538 * RETURNS
2539 * Reply code on success
2540 * 0 on failure
2543 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD_PTR dwContext)
2545 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2546 DWORD nRecv;
2547 INT rc = 0;
2548 char firstprefix[5];
2549 BOOL multiline = FALSE;
2551 TRACE("socket(%d)\n", lpwfs->sndSocket);
2553 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2555 while(1)
2557 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2558 goto lerror;
2560 if (nRecv >= 3)
2562 if(!multiline)
2564 if(lpszResponse[3] != '-')
2565 break;
2566 else
2567 { /* Start of multiline response. Loop until we get "nnn " */
2568 multiline = TRUE;
2569 memcpy(firstprefix, lpszResponse, 3);
2570 firstprefix[3] = ' ';
2571 firstprefix[4] = '\0';
2574 else
2576 if(!memcmp(firstprefix, lpszResponse, 4))
2577 break;
2582 if (nRecv >= 3)
2584 rc = atoi(lpszResponse);
2586 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2587 &nRecv, sizeof(DWORD));
2590 lerror:
2591 TRACE("return %d\n", rc);
2592 return rc;
2596 /***********************************************************************
2597 * FTP_SendPassword (internal)
2599 * Send password to ftp server
2601 * RETURNS
2602 * TRUE on success
2603 * NULL on failure
2606 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2608 INT nResCode;
2609 BOOL bSuccess = FALSE;
2611 TRACE("\n");
2612 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2613 goto lend;
2615 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2616 if (nResCode)
2618 TRACE("Received reply code %d\n", nResCode);
2619 /* Login successful... */
2620 if (nResCode == 230)
2621 bSuccess = TRUE;
2622 /* Command not implemented, superfluous at the server site... */
2623 /* Need account for login... */
2624 else if (nResCode == 332)
2625 bSuccess = FTP_SendAccount(lpwfs);
2626 else
2627 FTP_SetResponseError(nResCode);
2630 lend:
2631 TRACE("Returning %d\n", bSuccess);
2632 return bSuccess;
2636 /***********************************************************************
2637 * FTP_SendAccount (internal)
2641 * RETURNS
2642 * TRUE on success
2643 * FALSE on failure
2646 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2648 INT nResCode;
2649 BOOL bSuccess = FALSE;
2651 TRACE("\n");
2652 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2653 goto lend;
2655 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2656 if (nResCode)
2657 bSuccess = TRUE;
2658 else
2659 FTP_SetResponseError(nResCode);
2661 lend:
2662 return bSuccess;
2666 /***********************************************************************
2667 * FTP_SendStore (internal)
2669 * Send request to upload file to ftp server
2671 * RETURNS
2672 * TRUE on success
2673 * FALSE on failure
2676 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2678 INT nResCode;
2679 BOOL bSuccess = FALSE;
2681 TRACE("\n");
2682 if (!FTP_InitListenSocket(lpwfs))
2683 goto lend;
2685 if (!FTP_SendType(lpwfs, dwType))
2686 goto lend;
2688 if (!FTP_SendPortOrPasv(lpwfs))
2689 goto lend;
2691 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2692 goto lend;
2693 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2694 if (nResCode)
2696 if (nResCode == 150 || nResCode == 125)
2697 bSuccess = TRUE;
2698 else
2699 FTP_SetResponseError(nResCode);
2702 lend:
2703 if (!bSuccess && lpwfs->lstnSocket != -1)
2705 closesocket(lpwfs->lstnSocket);
2706 lpwfs->lstnSocket = -1;
2709 return bSuccess;
2713 /***********************************************************************
2714 * FTP_InitListenSocket (internal)
2716 * Create a socket to listen for server response
2718 * RETURNS
2719 * TRUE on success
2720 * FALSE on failure
2723 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2725 BOOL bSuccess = FALSE;
2726 socklen_t namelen = sizeof(struct sockaddr_in);
2728 TRACE("\n");
2730 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2731 if (lpwfs->lstnSocket == -1)
2733 TRACE("Unable to create listening socket\n");
2734 goto lend;
2737 /* We obtain our ip addr from the name of the command channel socket */
2738 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2740 /* and get the system to assign us a port */
2741 lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
2743 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2745 TRACE("Unable to bind socket\n");
2746 goto lend;
2749 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2751 TRACE("listen failed\n");
2752 goto lend;
2755 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2756 bSuccess = TRUE;
2758 lend:
2759 if (!bSuccess && lpwfs->lstnSocket != -1)
2761 closesocket(lpwfs->lstnSocket);
2762 lpwfs->lstnSocket = -1;
2765 return bSuccess;
2769 /***********************************************************************
2770 * FTP_SendType (internal)
2772 * Tell server type of data being transferred
2774 * RETURNS
2775 * TRUE on success
2776 * FALSE on failure
2778 * W98SE doesn't cache the type that's currently set
2779 * (i.e. it sends it always),
2780 * so we probably don't want to do that either.
2782 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2784 INT nResCode;
2785 WCHAR type[] = { 'I','\0' };
2786 BOOL bSuccess = FALSE;
2788 TRACE("\n");
2789 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2790 type[0] = 'A';
2792 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2793 goto lend;
2795 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2796 if (nResCode)
2798 if (nResCode == 2)
2799 bSuccess = TRUE;
2800 else
2801 FTP_SetResponseError(nResCode);
2804 lend:
2805 return bSuccess;
2809 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2810 /***********************************************************************
2811 * FTP_GetFileSize (internal)
2813 * Retrieves from the server the size of the given file
2815 * RETURNS
2816 * TRUE on success
2817 * FALSE on failure
2820 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2822 INT nResCode;
2823 BOOL bSuccess = FALSE;
2825 TRACE("\n");
2827 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2828 goto lend;
2830 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2831 if (nResCode)
2833 if (nResCode == 213) {
2834 /* Now parses the output to get the actual file size */
2835 int i;
2836 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2838 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2839 if (lpszResponseBuffer[i] == '\0') return FALSE;
2840 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2842 bSuccess = TRUE;
2843 } else {
2844 FTP_SetResponseError(nResCode);
2848 lend:
2849 return bSuccess;
2851 #endif
2854 /***********************************************************************
2855 * FTP_SendPort (internal)
2857 * Tell server which port to use
2859 * RETURNS
2860 * TRUE on success
2861 * FALSE on failure
2864 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2866 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2867 INT nResCode;
2868 WCHAR szIPAddress[64];
2869 BOOL bSuccess = FALSE;
2870 TRACE("\n");
2872 sprintfW(szIPAddress, szIPFormat,
2873 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2874 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2875 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2876 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2877 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2878 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2880 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2881 goto lend;
2883 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2884 if (nResCode)
2886 if (nResCode == 200)
2887 bSuccess = TRUE;
2888 else
2889 FTP_SetResponseError(nResCode);
2892 lend:
2893 return bSuccess;
2897 /***********************************************************************
2898 * FTP_DoPassive (internal)
2900 * Tell server that we want to do passive transfers
2901 * and connect data socket
2903 * RETURNS
2904 * TRUE on success
2905 * FALSE on failure
2908 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2910 INT nResCode;
2911 BOOL bSuccess = FALSE;
2913 TRACE("\n");
2914 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2915 goto lend;
2917 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2918 if (nResCode)
2920 if (nResCode == 227)
2922 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2923 LPSTR p;
2924 int f[6];
2925 int i;
2926 char *pAddr, *pPort;
2927 INT nsocket = -1;
2928 struct sockaddr_in dataSocketAddress;
2930 p = lpszResponseBuffer+4; /* skip status code */
2931 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
2933 if (*p == '\0')
2935 ERR("no address found in response, aborting\n");
2936 goto lend;
2939 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
2940 &f[4], &f[5]) != 6)
2942 ERR("unknown response address format '%s', aborting\n", p);
2943 goto lend;
2945 for (i=0; i < 6; i++)
2946 f[i] = f[i] & 0xff;
2948 dataSocketAddress = lpwfs->socketAddress;
2949 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2950 pPort = (char *)&(dataSocketAddress.sin_port);
2951 pAddr[0] = f[0];
2952 pAddr[1] = f[1];
2953 pAddr[2] = f[2];
2954 pAddr[3] = f[3];
2955 pPort[0] = f[4];
2956 pPort[1] = f[5];
2958 nsocket = socket(AF_INET,SOCK_STREAM,0);
2959 if (nsocket == -1)
2960 goto lend;
2962 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2964 ERR("can't connect passive FTP data port.\n");
2965 closesocket(nsocket);
2966 goto lend;
2968 lpwfs->pasvSocket = nsocket;
2969 bSuccess = TRUE;
2971 else
2972 FTP_SetResponseError(nResCode);
2975 lend:
2976 return bSuccess;
2980 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2982 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2984 if (!FTP_DoPassive(lpwfs))
2985 return FALSE;
2987 else
2989 if (!FTP_SendPort(lpwfs))
2990 return FALSE;
2992 return TRUE;
2996 /***********************************************************************
2997 * FTP_GetDataSocket (internal)
2999 * Either accepts an incoming data socket connection from the server
3000 * or just returns the already opened socket after a PASV command
3001 * in case of passive FTP.
3004 * RETURNS
3005 * TRUE on success
3006 * FALSE on failure
3009 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
3011 struct sockaddr_in saddr;
3012 socklen_t addrlen = sizeof(struct sockaddr);
3014 TRACE("\n");
3015 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3017 *nDataSocket = lpwfs->pasvSocket;
3019 else
3021 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3022 closesocket(lpwfs->lstnSocket);
3023 lpwfs->lstnSocket = -1;
3025 return *nDataSocket != -1;
3029 /***********************************************************************
3030 * FTP_SendData (internal)
3032 * Send data to the server
3034 * RETURNS
3035 * TRUE on success
3036 * FALSE on failure
3039 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
3041 BY_HANDLE_FILE_INFORMATION fi;
3042 DWORD nBytesRead = 0;
3043 DWORD nBytesSent = 0;
3044 DWORD nTotalSent = 0;
3045 DWORD nBytesToSend, nLen;
3046 int nRC = 1;
3047 time_t s_long_time, e_long_time;
3048 LONG nSeconds;
3049 CHAR *lpszBuffer;
3051 TRACE("\n");
3052 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3054 /* Get the size of the file. */
3055 GetFileInformationByHandle(hFile, &fi);
3056 time(&s_long_time);
3060 nBytesToSend = nBytesRead - nBytesSent;
3062 if (nBytesToSend <= 0)
3064 /* Read data from file. */
3065 nBytesSent = 0;
3066 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3067 ERR("Failed reading from file\n");
3069 if (nBytesRead > 0)
3070 nBytesToSend = nBytesRead;
3071 else
3072 break;
3075 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3076 DATA_PACKET_SIZE : nBytesToSend;
3077 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3079 if (nRC != -1)
3081 nBytesSent += nRC;
3082 nTotalSent += nRC;
3085 /* Do some computation to display the status. */
3086 time(&e_long_time);
3087 nSeconds = e_long_time - s_long_time;
3088 if( nSeconds / 60 > 0 )
3090 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3091 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3092 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3094 else
3096 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3097 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3098 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3100 } while (nRC != -1);
3102 TRACE("file transfer complete!\n");
3104 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3106 return nTotalSent;
3110 /***********************************************************************
3111 * FTP_SendRetrieve (internal)
3113 * Send request to retrieve a file
3115 * RETURNS
3116 * Number of bytes to be received on success
3117 * 0 on failure
3120 static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3122 INT nResCode;
3123 BOOL ret;
3125 TRACE("\n");
3126 if (!(ret = FTP_InitListenSocket(lpwfs)))
3127 goto lend;
3129 if (!(ret = FTP_SendType(lpwfs, dwType)))
3130 goto lend;
3132 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3133 goto lend;
3135 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3136 goto lend;
3138 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3139 if ((nResCode != 125) && (nResCode != 150)) {
3140 /* That means that we got an error getting the file. */
3141 FTP_SetResponseError(nResCode);
3142 ret = FALSE;
3145 lend:
3146 if (!ret && lpwfs->lstnSocket != -1)
3148 closesocket(lpwfs->lstnSocket);
3149 lpwfs->lstnSocket = -1;
3152 return ret;
3156 /***********************************************************************
3157 * FTP_RetrieveData (internal)
3159 * Retrieve data from server
3161 * RETURNS
3162 * TRUE on success
3163 * FALSE on failure
3166 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
3168 DWORD nBytesWritten;
3169 INT nRC = 0;
3170 CHAR *lpszBuffer;
3172 TRACE("\n");
3174 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3175 if (NULL == lpszBuffer)
3177 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3178 return FALSE;
3181 while (nRC != -1)
3183 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3184 if (nRC != -1)
3186 /* other side closed socket. */
3187 if (nRC == 0)
3188 goto recv_end;
3189 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3193 TRACE("Data transfer complete\n");
3195 recv_end:
3196 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3198 return (nRC != -1);
3201 /***********************************************************************
3202 * FTPFINDNEXT_Destroy (internal)
3204 * Deallocate session handle
3206 static void FTPFINDNEXT_Destroy(WININETHANDLEHEADER *hdr)
3208 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3209 DWORD i;
3211 TRACE("\n");
3213 WININET_Release(&lpwfn->lpFtpSession->hdr);
3215 for (i = 0; i < lpwfn->size; i++)
3217 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3220 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3221 HeapFree(GetProcessHeap(), 0, lpwfn);
3224 static DWORD WINAPI FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3226 WIN32_FIND_DATAW *find_data = data;
3227 DWORD res = ERROR_SUCCESS;
3229 TRACE("index(%d) size(%d)\n", find->index, find->size);
3231 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3233 if (find->index < find->size) {
3234 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3235 find->index++;
3237 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3238 }else {
3239 res = ERROR_NO_MORE_FILES;
3242 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3244 INTERNET_ASYNC_RESULT iar;
3246 iar.dwResult = (res == ERROR_SUCCESS);
3247 iar.dwError = res;
3249 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3250 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3251 sizeof(INTERNET_ASYNC_RESULT));
3254 return res;
3257 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3259 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3261 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3264 static DWORD FTPFINDNEXT_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3266 switch(option) {
3267 case INTERNET_OPTION_HANDLE_TYPE:
3268 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3270 if (*size < sizeof(ULONG))
3271 return ERROR_INSUFFICIENT_BUFFER;
3273 *size = sizeof(DWORD);
3274 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3275 return ERROR_SUCCESS;
3278 FIXME("Not implemented option %d\n", option);
3279 return ERROR_INTERNET_INVALID_OPTION;
3282 static DWORD FTPFINDNEXT_FindNextFileW(WININETHANDLEHEADER *hdr, void *data)
3284 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3286 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3288 WORKREQUEST workRequest;
3289 struct WORKREQ_FTPFINDNEXTW *req;
3291 workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3292 workRequest.hdr = WININET_AddRef( &find->hdr );
3293 req = &workRequest.u.FtpFindNextW;
3294 req->lpFindFileData = data;
3296 INTERNET_AsyncCall(&workRequest);
3298 return ERROR_SUCCESS;
3301 return FTPFINDNEXT_FindNextFileProc(find, data);
3304 static const HANDLEHEADERVtbl FTPFINDNEXTVtbl = {
3305 FTPFINDNEXT_Destroy,
3306 NULL,
3307 FTPFINDNEXT_QueryOption,
3308 NULL,
3309 NULL,
3310 NULL,
3311 NULL,
3312 NULL,
3313 FTPFINDNEXT_FindNextFileW
3316 /***********************************************************************
3317 * FTP_ReceiveFileList (internal)
3319 * Read file list from server
3321 * RETURNS
3322 * Handle to file list on success
3323 * NULL on failure
3326 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3327 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3329 DWORD dwSize = 0;
3330 LPFILEPROPERTIESW lpafp = NULL;
3331 LPWININETFTPFINDNEXTW lpwfn = NULL;
3332 HINTERNET handle = 0;
3334 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3336 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3338 if(lpFindFileData)
3339 FTP_ConvertFileProp(lpafp, lpFindFileData);
3341 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3342 if (lpwfn)
3344 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3345 lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3346 lpwfn->hdr.dwContext = dwContext;
3347 lpwfn->hdr.refs = 1;
3348 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3349 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3350 lpwfn->size = dwSize;
3351 lpwfn->lpafp = lpafp;
3353 WININET_AddRef( &lpwfs->hdr );
3354 lpwfn->lpFtpSession = lpwfs;
3355 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3357 handle = WININET_AllocHandle( &lpwfn->hdr );
3361 if( lpwfn )
3362 WININET_Release( &lpwfn->hdr );
3364 TRACE("Matched %d files\n", dwSize);
3365 return handle;
3369 /***********************************************************************
3370 * FTP_ConvertFileProp (internal)
3372 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3374 * RETURNS
3375 * TRUE on success
3376 * FALSE on failure
3379 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3381 BOOL bSuccess = FALSE;
3383 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3385 if (lpafp)
3387 /* Convert 'Unix' time to Windows time */
3388 RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
3389 (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
3390 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3391 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3393 /* Not all fields are filled in */
3394 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3395 lpFindFileData->nFileSizeLow = lpafp->nSize;
3397 if (lpafp->bIsDirectory)
3398 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3400 if (lpafp->lpszName)
3401 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3403 bSuccess = TRUE;
3406 return bSuccess;
3409 /***********************************************************************
3410 * FTP_ParseNextFile (internal)
3412 * Parse the next line in file listing
3414 * RETURNS
3415 * TRUE on success
3416 * FALSE on failure
3418 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3420 static const char szSpace[] = " \t";
3421 DWORD nBufLen;
3422 char *pszLine;
3423 char *pszToken;
3424 char *pszTmp;
3425 BOOL found = FALSE;
3426 int i;
3428 lpfp->lpszName = NULL;
3429 do {
3430 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3431 return FALSE;
3433 pszToken = strtok(pszLine, szSpace);
3434 /* ls format
3435 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3437 * For instance:
3438 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3440 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3441 if(!FTP_ParsePermission(pszToken, lpfp))
3442 lpfp->bIsDirectory = FALSE;
3443 for(i=0; i<=3; i++) {
3444 if(!(pszToken = strtok(NULL, szSpace)))
3445 break;
3447 if(!pszToken) continue;
3448 if(lpfp->bIsDirectory) {
3449 TRACE("Is directory\n");
3450 lpfp->nSize = 0;
3452 else {
3453 TRACE("Size: %s\n", pszToken);
3454 lpfp->nSize = atol(pszToken);
3457 lpfp->tmLastModified.tm_sec = 0;
3458 lpfp->tmLastModified.tm_min = 0;
3459 lpfp->tmLastModified.tm_hour = 0;
3460 lpfp->tmLastModified.tm_mday = 0;
3461 lpfp->tmLastModified.tm_mon = 0;
3462 lpfp->tmLastModified.tm_year = 0;
3464 /* Determine month */
3465 pszToken = strtok(NULL, szSpace);
3466 if(!pszToken) continue;
3467 if(strlen(pszToken) >= 3) {
3468 pszToken[3] = 0;
3469 if((pszTmp = StrStrIA(szMonths, pszToken)))
3470 lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
3472 /* Determine day */
3473 pszToken = strtok(NULL, szSpace);
3474 if(!pszToken) continue;
3475 lpfp->tmLastModified.tm_mday = atoi(pszToken);
3476 /* Determine time or year */
3477 pszToken = strtok(NULL, szSpace);
3478 if(!pszToken) continue;
3479 if((pszTmp = strchr(pszToken, ':'))) {
3480 struct tm* apTM;
3481 time_t aTime;
3482 *pszTmp = 0;
3483 pszTmp++;
3484 lpfp->tmLastModified.tm_min = atoi(pszTmp);
3485 lpfp->tmLastModified.tm_hour = atoi(pszToken);
3486 time(&aTime);
3487 apTM = localtime(&aTime);
3488 lpfp->tmLastModified.tm_year = apTM->tm_year;
3490 else {
3491 lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
3492 lpfp->tmLastModified.tm_hour = 12;
3494 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3495 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3496 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3497 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3499 pszToken = strtok(NULL, szSpace);
3500 if(!pszToken) continue;
3501 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3502 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3504 /* NT way of parsing ... :
3506 07-13-03 08:55PM <DIR> sakpatch
3507 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3509 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3510 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3512 sscanf(pszToken, "%d-%d-%d",
3513 &lpfp->tmLastModified.tm_mon,
3514 &lpfp->tmLastModified.tm_mday,
3515 &lpfp->tmLastModified.tm_year);
3517 /* Hacky and bad Y2K protection :-) */
3518 if (lpfp->tmLastModified.tm_year < 70)
3519 lpfp->tmLastModified.tm_year += 100;
3521 pszToken = strtok(NULL, szSpace);
3522 if(!pszToken) continue;
3523 sscanf(pszToken, "%d:%d",
3524 &lpfp->tmLastModified.tm_hour,
3525 &lpfp->tmLastModified.tm_min);
3526 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3527 lpfp->tmLastModified.tm_hour += 12;
3529 lpfp->tmLastModified.tm_sec = 0;
3531 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3532 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3533 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3534 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3536 pszToken = strtok(NULL, szSpace);
3537 if(!pszToken) continue;
3538 if(!strcasecmp(pszToken, "<DIR>")) {
3539 lpfp->bIsDirectory = TRUE;
3540 lpfp->nSize = 0;
3541 TRACE("Is directory\n");
3543 else {
3544 lpfp->bIsDirectory = FALSE;
3545 lpfp->nSize = atol(pszToken);
3546 TRACE("Size: %d\n", lpfp->nSize);
3549 pszToken = strtok(NULL, szSpace);
3550 if(!pszToken) continue;
3551 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3552 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3554 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3555 else if(pszToken[0] == '+') {
3556 FIXME("EPLF Format not implemented\n");
3559 if(lpfp->lpszName) {
3560 if((lpszSearchFile == NULL) ||
3561 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3562 found = TRUE;
3563 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3565 else {
3566 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3567 lpfp->lpszName = NULL;
3570 } while(!found);
3571 return TRUE;
3574 /***********************************************************************
3575 * FTP_ParseDirectory (internal)
3577 * Parse string of directory information
3579 * RETURNS
3580 * TRUE on success
3581 * FALSE on failure
3583 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3584 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3586 BOOL bSuccess = TRUE;
3587 INT sizeFilePropArray = 500;/*20; */
3588 INT indexFilePropArray = -1;
3590 TRACE("\n");
3592 /* Allocate initial file properties array */
3593 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3594 if (!*lpafp)
3595 return FALSE;
3597 do {
3598 if (indexFilePropArray+1 >= sizeFilePropArray)
3600 LPFILEPROPERTIESW tmpafp;
3602 sizeFilePropArray *= 2;
3603 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3604 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3605 if (NULL == tmpafp)
3607 bSuccess = FALSE;
3608 break;
3611 *lpafp = tmpafp;
3613 indexFilePropArray++;
3614 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3616 if (bSuccess && indexFilePropArray)
3618 if (indexFilePropArray < sizeFilePropArray - 1)
3620 LPFILEPROPERTIESW tmpafp;
3622 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3623 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3624 if (NULL != tmpafp)
3625 *lpafp = tmpafp;
3627 *dwfp = indexFilePropArray;
3629 else
3631 HeapFree(GetProcessHeap(), 0, *lpafp);
3632 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3633 bSuccess = FALSE;
3636 return bSuccess;
3640 /***********************************************************************
3641 * FTP_ParsePermission (internal)
3643 * Parse permission string of directory information
3645 * RETURNS
3646 * TRUE on success
3647 * FALSE on failure
3650 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3652 BOOL bSuccess = TRUE;
3653 unsigned short nPermission = 0;
3654 INT nPos = 1;
3655 INT nLast = 9;
3657 TRACE("\n");
3658 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3660 bSuccess = FALSE;
3661 return bSuccess;
3664 lpfp->bIsDirectory = (*lpszPermission == 'd');
3667 switch (nPos)
3669 case 1:
3670 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3671 break;
3672 case 2:
3673 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3674 break;
3675 case 3:
3676 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3677 break;
3678 case 4:
3679 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3680 break;
3681 case 5:
3682 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3683 break;
3684 case 6:
3685 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3686 break;
3687 case 7:
3688 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3689 break;
3690 case 8:
3691 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3692 break;
3693 case 9:
3694 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3695 break;
3697 nPos++;
3698 }while (nPos <= nLast);
3700 lpfp->permissions = nPermission;
3701 return bSuccess;
3705 /***********************************************************************
3706 * FTP_SetResponseError (internal)
3708 * Set the appropriate error code for a given response from the server
3710 * RETURNS
3713 static DWORD FTP_SetResponseError(DWORD dwResponse)
3715 DWORD dwCode = 0;
3717 switch(dwResponse)
3719 case 425: /* Cannot open data connection. */
3720 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3721 break;
3723 case 426: /* Connection closed, transer aborted. */
3724 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3725 break;
3727 case 530: /* Not logged in. Login incorrect. */
3728 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3729 break;
3731 case 421: /* Service not available - Server may be shutting down. */
3732 case 450: /* File action not taken. File may be busy. */
3733 case 451: /* Action aborted. Server error. */
3734 case 452: /* Action not taken. Insufficient storage space on server. */
3735 case 500: /* Syntax error. Command unrecognized. */
3736 case 501: /* Syntax error. Error in parameters or arguments. */
3737 case 502: /* Command not implemented. */
3738 case 503: /* Bad sequence of commands. */
3739 case 504: /* Command not implemented for that parameter. */
3740 case 532: /* Need account for storing files */
3741 case 550: /* File action not taken. File not found or no access. */
3742 case 551: /* Requested action aborted. Page type unknown */
3743 case 552: /* Action aborted. Exceeded storage allocation */
3744 case 553: /* Action not taken. File name not allowed. */
3746 default:
3747 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3748 break;
3751 INTERNET_SetLastError(dwCode);
3752 return dwCode;