wininet: Avoid a crash with traces on.
[wine.git] / dlls / wininet / ftp.c
blob4077de3fadc06fc8682503bb7615d7aa786dc0cf
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;
2550 LPWININETAPPINFOW hIC = NULL;
2552 TRACE("socket(%d)\n", lpwfs->sndSocket);
2554 hIC = lpwfs->lpAppInfo;
2555 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2557 while(1)
2559 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2560 goto lerror;
2562 if (nRecv >= 3)
2564 if(!multiline)
2566 if(lpszResponse[3] != '-')
2567 break;
2568 else
2569 { /* Start of multiline response. Loop until we get "nnn " */
2570 multiline = TRUE;
2571 memcpy(firstprefix, lpszResponse, 3);
2572 firstprefix[3] = ' ';
2573 firstprefix[4] = '\0';
2576 else
2578 if(!memcmp(firstprefix, lpszResponse, 4))
2579 break;
2584 if (nRecv >= 3)
2586 rc = atoi(lpszResponse);
2588 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2589 &nRecv, sizeof(DWORD));
2592 lerror:
2593 TRACE("return %d\n", rc);
2594 return rc;
2598 /***********************************************************************
2599 * FTP_SendPassword (internal)
2601 * Send password to ftp server
2603 * RETURNS
2604 * TRUE on success
2605 * NULL on failure
2608 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2610 INT nResCode;
2611 BOOL bSuccess = FALSE;
2613 TRACE("\n");
2614 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2615 goto lend;
2617 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2618 if (nResCode)
2620 TRACE("Received reply code %d\n", nResCode);
2621 /* Login successful... */
2622 if (nResCode == 230)
2623 bSuccess = TRUE;
2624 /* Command not implemented, superfluous at the server site... */
2625 /* Need account for login... */
2626 else if (nResCode == 332)
2627 bSuccess = FTP_SendAccount(lpwfs);
2628 else
2629 FTP_SetResponseError(nResCode);
2632 lend:
2633 TRACE("Returning %d\n", bSuccess);
2634 return bSuccess;
2638 /***********************************************************************
2639 * FTP_SendAccount (internal)
2643 * RETURNS
2644 * TRUE on success
2645 * FALSE on failure
2648 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2650 INT nResCode;
2651 BOOL bSuccess = FALSE;
2653 TRACE("\n");
2654 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2655 goto lend;
2657 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2658 if (nResCode)
2659 bSuccess = TRUE;
2660 else
2661 FTP_SetResponseError(nResCode);
2663 lend:
2664 return bSuccess;
2668 /***********************************************************************
2669 * FTP_SendStore (internal)
2671 * Send request to upload file to ftp server
2673 * RETURNS
2674 * TRUE on success
2675 * FALSE on failure
2678 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2680 INT nResCode;
2681 BOOL bSuccess = FALSE;
2683 TRACE("\n");
2684 if (!FTP_InitListenSocket(lpwfs))
2685 goto lend;
2687 if (!FTP_SendType(lpwfs, dwType))
2688 goto lend;
2690 if (!FTP_SendPortOrPasv(lpwfs))
2691 goto lend;
2693 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2694 goto lend;
2695 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2696 if (nResCode)
2698 if (nResCode == 150 || nResCode == 125)
2699 bSuccess = TRUE;
2700 else
2701 FTP_SetResponseError(nResCode);
2704 lend:
2705 if (!bSuccess && lpwfs->lstnSocket != -1)
2707 closesocket(lpwfs->lstnSocket);
2708 lpwfs->lstnSocket = -1;
2711 return bSuccess;
2715 /***********************************************************************
2716 * FTP_InitListenSocket (internal)
2718 * Create a socket to listen for server response
2720 * RETURNS
2721 * TRUE on success
2722 * FALSE on failure
2725 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2727 BOOL bSuccess = FALSE;
2728 socklen_t namelen = sizeof(struct sockaddr_in);
2730 TRACE("\n");
2732 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2733 if (lpwfs->lstnSocket == -1)
2735 TRACE("Unable to create listening socket\n");
2736 goto lend;
2739 /* We obtain our ip addr from the name of the command channel socket */
2740 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2742 /* and get the system to assign us a port */
2743 lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
2745 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2747 TRACE("Unable to bind socket\n");
2748 goto lend;
2751 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2753 TRACE("listen failed\n");
2754 goto lend;
2757 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2758 bSuccess = TRUE;
2760 lend:
2761 if (!bSuccess && lpwfs->lstnSocket != -1)
2763 closesocket(lpwfs->lstnSocket);
2764 lpwfs->lstnSocket = -1;
2767 return bSuccess;
2771 /***********************************************************************
2772 * FTP_SendType (internal)
2774 * Tell server type of data being transferred
2776 * RETURNS
2777 * TRUE on success
2778 * FALSE on failure
2780 * W98SE doesn't cache the type that's currently set
2781 * (i.e. it sends it always),
2782 * so we probably don't want to do that either.
2784 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2786 INT nResCode;
2787 WCHAR type[] = { 'I','\0' };
2788 BOOL bSuccess = FALSE;
2790 TRACE("\n");
2791 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2792 type[0] = 'A';
2794 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2795 goto lend;
2797 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2798 if (nResCode)
2800 if (nResCode == 2)
2801 bSuccess = TRUE;
2802 else
2803 FTP_SetResponseError(nResCode);
2806 lend:
2807 return bSuccess;
2811 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2812 /***********************************************************************
2813 * FTP_GetFileSize (internal)
2815 * Retrieves from the server the size of the given file
2817 * RETURNS
2818 * TRUE on success
2819 * FALSE on failure
2822 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2824 INT nResCode;
2825 BOOL bSuccess = FALSE;
2827 TRACE("\n");
2829 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2830 goto lend;
2832 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2833 if (nResCode)
2835 if (nResCode == 213) {
2836 /* Now parses the output to get the actual file size */
2837 int i;
2838 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2840 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2841 if (lpszResponseBuffer[i] == '\0') return FALSE;
2842 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2844 bSuccess = TRUE;
2845 } else {
2846 FTP_SetResponseError(nResCode);
2850 lend:
2851 return bSuccess;
2853 #endif
2856 /***********************************************************************
2857 * FTP_SendPort (internal)
2859 * Tell server which port to use
2861 * RETURNS
2862 * TRUE on success
2863 * FALSE on failure
2866 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2868 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2869 INT nResCode;
2870 WCHAR szIPAddress[64];
2871 BOOL bSuccess = FALSE;
2872 TRACE("\n");
2874 sprintfW(szIPAddress, szIPFormat,
2875 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2876 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2877 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2878 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2879 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2880 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2882 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2883 goto lend;
2885 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2886 if (nResCode)
2888 if (nResCode == 200)
2889 bSuccess = TRUE;
2890 else
2891 FTP_SetResponseError(nResCode);
2894 lend:
2895 return bSuccess;
2899 /***********************************************************************
2900 * FTP_DoPassive (internal)
2902 * Tell server that we want to do passive transfers
2903 * and connect data socket
2905 * RETURNS
2906 * TRUE on success
2907 * FALSE on failure
2910 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2912 INT nResCode;
2913 BOOL bSuccess = FALSE;
2915 TRACE("\n");
2916 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2917 goto lend;
2919 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2920 if (nResCode)
2922 if (nResCode == 227)
2924 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2925 LPSTR p;
2926 int f[6];
2927 int i;
2928 char *pAddr, *pPort;
2929 INT nsocket = -1;
2930 struct sockaddr_in dataSocketAddress;
2932 p = lpszResponseBuffer+4; /* skip status code */
2933 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
2935 if (*p == '\0')
2937 ERR("no address found in response, aborting\n");
2938 goto lend;
2941 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
2942 &f[4], &f[5]) != 6)
2944 ERR("unknown response address format '%s', aborting\n", p);
2945 goto lend;
2947 for (i=0; i < 6; i++)
2948 f[i] = f[i] & 0xff;
2950 dataSocketAddress = lpwfs->socketAddress;
2951 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2952 pPort = (char *)&(dataSocketAddress.sin_port);
2953 pAddr[0] = f[0];
2954 pAddr[1] = f[1];
2955 pAddr[2] = f[2];
2956 pAddr[3] = f[3];
2957 pPort[0] = f[4];
2958 pPort[1] = f[5];
2960 nsocket = socket(AF_INET,SOCK_STREAM,0);
2961 if (nsocket == -1)
2962 goto lend;
2964 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2966 ERR("can't connect passive FTP data port.\n");
2967 closesocket(nsocket);
2968 goto lend;
2970 lpwfs->pasvSocket = nsocket;
2971 bSuccess = TRUE;
2973 else
2974 FTP_SetResponseError(nResCode);
2977 lend:
2978 return bSuccess;
2982 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2984 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2986 if (!FTP_DoPassive(lpwfs))
2987 return FALSE;
2989 else
2991 if (!FTP_SendPort(lpwfs))
2992 return FALSE;
2994 return TRUE;
2998 /***********************************************************************
2999 * FTP_GetDataSocket (internal)
3001 * Either accepts an incoming data socket connection from the server
3002 * or just returns the already opened socket after a PASV command
3003 * in case of passive FTP.
3006 * RETURNS
3007 * TRUE on success
3008 * FALSE on failure
3011 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
3013 struct sockaddr_in saddr;
3014 socklen_t addrlen = sizeof(struct sockaddr);
3016 TRACE("\n");
3017 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3019 *nDataSocket = lpwfs->pasvSocket;
3021 else
3023 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3024 closesocket(lpwfs->lstnSocket);
3025 lpwfs->lstnSocket = -1;
3027 return *nDataSocket != -1;
3031 /***********************************************************************
3032 * FTP_SendData (internal)
3034 * Send data to the server
3036 * RETURNS
3037 * TRUE on success
3038 * FALSE on failure
3041 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
3043 BY_HANDLE_FILE_INFORMATION fi;
3044 DWORD nBytesRead = 0;
3045 DWORD nBytesSent = 0;
3046 DWORD nTotalSent = 0;
3047 DWORD nBytesToSend, nLen;
3048 int nRC = 1;
3049 time_t s_long_time, e_long_time;
3050 LONG nSeconds;
3051 CHAR *lpszBuffer;
3053 TRACE("\n");
3054 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3056 /* Get the size of the file. */
3057 GetFileInformationByHandle(hFile, &fi);
3058 time(&s_long_time);
3062 nBytesToSend = nBytesRead - nBytesSent;
3064 if (nBytesToSend <= 0)
3066 /* Read data from file. */
3067 nBytesSent = 0;
3068 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3069 ERR("Failed reading from file\n");
3071 if (nBytesRead > 0)
3072 nBytesToSend = nBytesRead;
3073 else
3074 break;
3077 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3078 DATA_PACKET_SIZE : nBytesToSend;
3079 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3081 if (nRC != -1)
3083 nBytesSent += nRC;
3084 nTotalSent += nRC;
3087 /* Do some computation to display the status. */
3088 time(&e_long_time);
3089 nSeconds = e_long_time - s_long_time;
3090 if( nSeconds / 60 > 0 )
3092 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3093 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3094 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3096 else
3098 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3099 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3100 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3102 } while (nRC != -1);
3104 TRACE("file transfer complete!\n");
3106 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3108 return nTotalSent;
3112 /***********************************************************************
3113 * FTP_SendRetrieve (internal)
3115 * Send request to retrieve a file
3117 * RETURNS
3118 * Number of bytes to be received on success
3119 * 0 on failure
3122 static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3124 INT nResCode;
3125 BOOL ret;
3127 TRACE("\n");
3128 if (!(ret = FTP_InitListenSocket(lpwfs)))
3129 goto lend;
3131 if (!(ret = FTP_SendType(lpwfs, dwType)))
3132 goto lend;
3134 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3135 goto lend;
3137 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3138 goto lend;
3140 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3141 if ((nResCode != 125) && (nResCode != 150)) {
3142 /* That means that we got an error getting the file. */
3143 FTP_SetResponseError(nResCode);
3144 ret = FALSE;
3147 lend:
3148 if (!ret && lpwfs->lstnSocket != -1)
3150 closesocket(lpwfs->lstnSocket);
3151 lpwfs->lstnSocket = -1;
3154 return ret;
3158 /***********************************************************************
3159 * FTP_RetrieveData (internal)
3161 * Retrieve data from server
3163 * RETURNS
3164 * TRUE on success
3165 * FALSE on failure
3168 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
3170 DWORD nBytesWritten;
3171 DWORD nBytesReceived = 0;
3172 INT nRC = 0;
3173 CHAR *lpszBuffer;
3175 TRACE("\n");
3177 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3178 if (NULL == lpszBuffer)
3180 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3181 return FALSE;
3184 while (nRC != -1)
3186 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3187 if (nRC != -1)
3189 /* other side closed socket. */
3190 if (nRC == 0)
3191 goto recv_end;
3192 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3193 nBytesReceived += nRC;
3197 TRACE("Data transfer complete\n");
3199 recv_end:
3200 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3202 return (nRC != -1);
3205 /***********************************************************************
3206 * FTPFINDNEXT_Destroy (internal)
3208 * Deallocate session handle
3210 static void FTPFINDNEXT_Destroy(WININETHANDLEHEADER *hdr)
3212 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3213 DWORD i;
3215 TRACE("\n");
3217 WININET_Release(&lpwfn->lpFtpSession->hdr);
3219 for (i = 0; i < lpwfn->size; i++)
3221 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3224 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3225 HeapFree(GetProcessHeap(), 0, lpwfn);
3228 static DWORD WINAPI FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3230 WIN32_FIND_DATAW *find_data = data;
3231 DWORD res = ERROR_SUCCESS;
3233 TRACE("index(%d) size(%d)\n", find->index, find->size);
3235 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3237 if (find->index < find->size) {
3238 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3239 find->index++;
3241 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3242 }else {
3243 res = ERROR_NO_MORE_FILES;
3246 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3248 INTERNET_ASYNC_RESULT iar;
3250 iar.dwResult = (res == ERROR_SUCCESS);
3251 iar.dwError = res;
3253 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3254 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3255 sizeof(INTERNET_ASYNC_RESULT));
3258 return res;
3261 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3263 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3265 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3268 static DWORD FTPFINDNEXT_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3270 switch(option) {
3271 case INTERNET_OPTION_HANDLE_TYPE:
3272 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3274 if (*size < sizeof(ULONG))
3275 return ERROR_INSUFFICIENT_BUFFER;
3277 *size = sizeof(DWORD);
3278 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3279 return ERROR_SUCCESS;
3282 FIXME("Not implemented option %d\n", option);
3283 return ERROR_INTERNET_INVALID_OPTION;
3286 static DWORD FTPFINDNEXT_FindNextFileW(WININETHANDLEHEADER *hdr, void *data)
3288 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3290 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3292 WORKREQUEST workRequest;
3293 struct WORKREQ_FTPFINDNEXTW *req;
3295 workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3296 workRequest.hdr = WININET_AddRef( &find->hdr );
3297 req = &workRequest.u.FtpFindNextW;
3298 req->lpFindFileData = data;
3300 INTERNET_AsyncCall(&workRequest);
3302 return ERROR_SUCCESS;
3305 return FTPFINDNEXT_FindNextFileProc(find, data);
3308 static const HANDLEHEADERVtbl FTPFINDNEXTVtbl = {
3309 FTPFINDNEXT_Destroy,
3310 NULL,
3311 FTPFINDNEXT_QueryOption,
3312 NULL,
3313 NULL,
3314 NULL,
3315 NULL,
3316 NULL,
3317 FTPFINDNEXT_FindNextFileW
3320 /***********************************************************************
3321 * FTP_ReceiveFileList (internal)
3323 * Read file list from server
3325 * RETURNS
3326 * Handle to file list on success
3327 * NULL on failure
3330 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3331 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3333 DWORD dwSize = 0;
3334 LPFILEPROPERTIESW lpafp = NULL;
3335 LPWININETFTPFINDNEXTW lpwfn = NULL;
3336 HINTERNET handle = 0;
3338 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3340 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3342 if(lpFindFileData)
3343 FTP_ConvertFileProp(lpafp, lpFindFileData);
3345 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3346 if (lpwfn)
3348 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3349 lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3350 lpwfn->hdr.dwContext = dwContext;
3351 lpwfn->hdr.refs = 1;
3352 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3353 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3354 lpwfn->size = dwSize;
3355 lpwfn->lpafp = lpafp;
3357 WININET_AddRef( &lpwfs->hdr );
3358 lpwfn->lpFtpSession = lpwfs;
3359 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3361 handle = WININET_AllocHandle( &lpwfn->hdr );
3365 if( lpwfn )
3366 WININET_Release( &lpwfn->hdr );
3368 TRACE("Matched %d files\n", dwSize);
3369 return handle;
3373 /***********************************************************************
3374 * FTP_ConvertFileProp (internal)
3376 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3378 * RETURNS
3379 * TRUE on success
3380 * FALSE on failure
3383 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3385 BOOL bSuccess = FALSE;
3387 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3389 if (lpafp)
3391 /* Convert 'Unix' time to Windows time */
3392 RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
3393 (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
3394 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3395 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3397 /* Not all fields are filled in */
3398 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3399 lpFindFileData->nFileSizeLow = lpafp->nSize;
3401 if (lpafp->bIsDirectory)
3402 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3404 if (lpafp->lpszName)
3405 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3407 bSuccess = TRUE;
3410 return bSuccess;
3413 /***********************************************************************
3414 * FTP_ParseNextFile (internal)
3416 * Parse the next line in file listing
3418 * RETURNS
3419 * TRUE on success
3420 * FALSE on failure
3422 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3424 static const char szSpace[] = " \t";
3425 DWORD nBufLen;
3426 char *pszLine;
3427 char *pszToken;
3428 char *pszTmp;
3429 BOOL found = FALSE;
3430 int i;
3432 lpfp->lpszName = NULL;
3433 do {
3434 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3435 return FALSE;
3437 pszToken = strtok(pszLine, szSpace);
3438 /* ls format
3439 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3441 * For instance:
3442 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3444 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3445 if(!FTP_ParsePermission(pszToken, lpfp))
3446 lpfp->bIsDirectory = FALSE;
3447 for(i=0; i<=3; i++) {
3448 if(!(pszToken = strtok(NULL, szSpace)))
3449 break;
3451 if(!pszToken) continue;
3452 if(lpfp->bIsDirectory) {
3453 TRACE("Is directory\n");
3454 lpfp->nSize = 0;
3456 else {
3457 TRACE("Size: %s\n", pszToken);
3458 lpfp->nSize = atol(pszToken);
3461 lpfp->tmLastModified.tm_sec = 0;
3462 lpfp->tmLastModified.tm_min = 0;
3463 lpfp->tmLastModified.tm_hour = 0;
3464 lpfp->tmLastModified.tm_mday = 0;
3465 lpfp->tmLastModified.tm_mon = 0;
3466 lpfp->tmLastModified.tm_year = 0;
3468 /* Determine month */
3469 pszToken = strtok(NULL, szSpace);
3470 if(!pszToken) continue;
3471 if(strlen(pszToken) >= 3) {
3472 pszToken[3] = 0;
3473 if((pszTmp = StrStrIA(szMonths, pszToken)))
3474 lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
3476 /* Determine day */
3477 pszToken = strtok(NULL, szSpace);
3478 if(!pszToken) continue;
3479 lpfp->tmLastModified.tm_mday = atoi(pszToken);
3480 /* Determine time or year */
3481 pszToken = strtok(NULL, szSpace);
3482 if(!pszToken) continue;
3483 if((pszTmp = strchr(pszToken, ':'))) {
3484 struct tm* apTM;
3485 time_t aTime;
3486 *pszTmp = 0;
3487 pszTmp++;
3488 lpfp->tmLastModified.tm_min = atoi(pszTmp);
3489 lpfp->tmLastModified.tm_hour = atoi(pszToken);
3490 time(&aTime);
3491 apTM = localtime(&aTime);
3492 lpfp->tmLastModified.tm_year = apTM->tm_year;
3494 else {
3495 lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
3496 lpfp->tmLastModified.tm_hour = 12;
3498 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3499 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3500 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3501 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3503 pszToken = strtok(NULL, szSpace);
3504 if(!pszToken) continue;
3505 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3506 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3508 /* NT way of parsing ... :
3510 07-13-03 08:55PM <DIR> sakpatch
3511 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3513 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3514 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3516 sscanf(pszToken, "%d-%d-%d",
3517 &lpfp->tmLastModified.tm_mon,
3518 &lpfp->tmLastModified.tm_mday,
3519 &lpfp->tmLastModified.tm_year);
3521 /* Hacky and bad Y2K protection :-) */
3522 if (lpfp->tmLastModified.tm_year < 70)
3523 lpfp->tmLastModified.tm_year += 100;
3525 pszToken = strtok(NULL, szSpace);
3526 if(!pszToken) continue;
3527 sscanf(pszToken, "%d:%d",
3528 &lpfp->tmLastModified.tm_hour,
3529 &lpfp->tmLastModified.tm_min);
3530 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3531 lpfp->tmLastModified.tm_hour += 12;
3533 lpfp->tmLastModified.tm_sec = 0;
3535 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3536 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3537 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3538 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3540 pszToken = strtok(NULL, szSpace);
3541 if(!pszToken) continue;
3542 if(!strcasecmp(pszToken, "<DIR>")) {
3543 lpfp->bIsDirectory = TRUE;
3544 lpfp->nSize = 0;
3545 TRACE("Is directory\n");
3547 else {
3548 lpfp->bIsDirectory = FALSE;
3549 lpfp->nSize = atol(pszToken);
3550 TRACE("Size: %d\n", lpfp->nSize);
3553 pszToken = strtok(NULL, szSpace);
3554 if(!pszToken) continue;
3555 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3556 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3558 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3559 else if(pszToken[0] == '+') {
3560 FIXME("EPLF Format not implemented\n");
3563 if(lpfp->lpszName) {
3564 if((lpszSearchFile == NULL) ||
3565 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3566 found = TRUE;
3567 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3569 else {
3570 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3571 lpfp->lpszName = NULL;
3574 } while(!found);
3575 return TRUE;
3578 /***********************************************************************
3579 * FTP_ParseDirectory (internal)
3581 * Parse string of directory information
3583 * RETURNS
3584 * TRUE on success
3585 * FALSE on failure
3587 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3588 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3590 BOOL bSuccess = TRUE;
3591 INT sizeFilePropArray = 500;/*20; */
3592 INT indexFilePropArray = -1;
3594 TRACE("\n");
3596 /* Allocate initial file properties array */
3597 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3598 if (!*lpafp)
3599 return FALSE;
3601 do {
3602 if (indexFilePropArray+1 >= sizeFilePropArray)
3604 LPFILEPROPERTIESW tmpafp;
3606 sizeFilePropArray *= 2;
3607 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3608 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3609 if (NULL == tmpafp)
3611 bSuccess = FALSE;
3612 break;
3615 *lpafp = tmpafp;
3617 indexFilePropArray++;
3618 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3620 if (bSuccess && indexFilePropArray)
3622 if (indexFilePropArray < sizeFilePropArray - 1)
3624 LPFILEPROPERTIESW tmpafp;
3626 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3627 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3628 if (NULL != tmpafp)
3629 *lpafp = tmpafp;
3631 *dwfp = indexFilePropArray;
3633 else
3635 HeapFree(GetProcessHeap(), 0, *lpafp);
3636 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3637 bSuccess = FALSE;
3640 return bSuccess;
3644 /***********************************************************************
3645 * FTP_ParsePermission (internal)
3647 * Parse permission string of directory information
3649 * RETURNS
3650 * TRUE on success
3651 * FALSE on failure
3654 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3656 BOOL bSuccess = TRUE;
3657 unsigned short nPermission = 0;
3658 INT nPos = 1;
3659 INT nLast = 9;
3661 TRACE("\n");
3662 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3664 bSuccess = FALSE;
3665 return bSuccess;
3668 lpfp->bIsDirectory = (*lpszPermission == 'd');
3671 switch (nPos)
3673 case 1:
3674 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3675 break;
3676 case 2:
3677 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3678 break;
3679 case 3:
3680 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3681 break;
3682 case 4:
3683 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3684 break;
3685 case 5:
3686 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3687 break;
3688 case 6:
3689 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3690 break;
3691 case 7:
3692 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3693 break;
3694 case 8:
3695 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3696 break;
3697 case 9:
3698 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3699 break;
3701 nPos++;
3702 }while (nPos <= nLast);
3704 lpfp->permissions = nPermission;
3705 return bSuccess;
3709 /***********************************************************************
3710 * FTP_SetResponseError (internal)
3712 * Set the appropriate error code for a given response from the server
3714 * RETURNS
3717 static DWORD FTP_SetResponseError(DWORD dwResponse)
3719 DWORD dwCode = 0;
3721 switch(dwResponse)
3723 case 425: /* Cannot open data connection. */
3724 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3725 break;
3727 case 426: /* Connection closed, transer aborted. */
3728 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3729 break;
3731 case 530: /* Not logged in. Login incorrect. */
3732 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3733 break;
3735 case 421: /* Service not available - Server may be shutting down. */
3736 case 450: /* File action not taken. File may be busy. */
3737 case 451: /* Action aborted. Server error. */
3738 case 452: /* Action not taken. Insufficient storage space on server. */
3739 case 500: /* Syntax error. Command unrecognized. */
3740 case 501: /* Syntax error. Error in parameters or arguments. */
3741 case 502: /* Command not implemented. */
3742 case 503: /* Bad sequence of commands. */
3743 case 504: /* Command not implemented for that parameter. */
3744 case 532: /* Need account for storing files */
3745 case 550: /* File action not taken. File not found or no access. */
3746 case 551: /* Requested action aborted. Page type unknown */
3747 case 552: /* Action aborted. Exceeded storage allocation */
3748 case 553: /* Action not taken. File name not allowed. */
3750 default:
3751 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3752 break;
3755 INTERNET_SetLastError(dwCode);
3756 return dwCode;