push 0b5f656d0c5c34ddfb204e40e069e78078c58084
[wine/hacks.git] / dlls / wininet / ftp.c
blob27d3b43a1cd6ea2ca27b2b06d0c911bc518cf818
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 #define DATA_PACKET_SIZE 0x2000
65 #define szCRLF "\r\n"
66 #define MAX_BACKLOG 5
68 /* Testing shows that Windows only accepts dwFlags where the last
69 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
71 #define FTP_CONDITION_MASK 0x0007
73 typedef enum {
74 /* FTP commands with arguments. */
75 FTP_CMD_ACCT,
76 FTP_CMD_CWD,
77 FTP_CMD_DELE,
78 FTP_CMD_MKD,
79 FTP_CMD_PASS,
80 FTP_CMD_PORT,
81 FTP_CMD_RETR,
82 FTP_CMD_RMD,
83 FTP_CMD_RNFR,
84 FTP_CMD_RNTO,
85 FTP_CMD_STOR,
86 FTP_CMD_TYPE,
87 FTP_CMD_USER,
88 FTP_CMD_SIZE,
90 /* FTP commands without arguments. */
91 FTP_CMD_ABOR,
92 FTP_CMD_LIST,
93 FTP_CMD_NLST,
94 FTP_CMD_PASV,
95 FTP_CMD_PWD,
96 FTP_CMD_QUIT,
97 } FTP_COMMAND;
99 static const CHAR *const szFtpCommands[] = {
100 "ACCT",
101 "CWD",
102 "DELE",
103 "MKD",
104 "PASS",
105 "PORT",
106 "RETR",
107 "RMD",
108 "RNFR",
109 "RNTO",
110 "STOR",
111 "TYPE",
112 "USER",
113 "SIZE",
114 "ABOR",
115 "LIST",
116 "NLST",
117 "PASV",
118 "PWD",
119 "QUIT",
122 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
123 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
125 static void FTP_CloseFileTransferHandle(LPWININETHANDLEHEADER hdr);
126 static void FTP_CloseSessionHandle(LPWININETHANDLEHEADER hdr);
127 static void FTP_CloseConnection(LPWININETHANDLEHEADER hdr);
128 static void FTP_CloseFindNextHandle(LPWININETHANDLEHEADER hdr);
129 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
130 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext);
131 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
132 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket);
133 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile);
134 static INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD_PTR dwContext);
135 static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
136 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile);
137 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs);
138 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs);
139 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs);
140 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs);
141 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType);
142 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs);
143 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs);
144 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs);
145 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
146 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
147 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
148 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
149 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
150 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
151 static DWORD FTP_SetResponseError(DWORD dwResponse);
153 /***********************************************************************
154 * FtpPutFileA (WININET.@)
156 * Uploads a file to the FTP server
158 * RETURNS
159 * TRUE on success
160 * FALSE on failure
163 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
164 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
166 LPWSTR lpwzLocalFile;
167 LPWSTR lpwzNewRemoteFile;
168 BOOL ret;
170 lpwzLocalFile = lpszLocalFile?WININET_strdup_AtoW(lpszLocalFile):NULL;
171 lpwzNewRemoteFile = lpszNewRemoteFile?WININET_strdup_AtoW(lpszNewRemoteFile):NULL;
172 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
173 dwFlags, dwContext);
174 HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
175 HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
176 return ret;
179 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
181 struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
182 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
184 TRACE("%p\n", lpwfs);
186 FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
187 req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
189 HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
190 HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
193 /***********************************************************************
194 * FtpPutFileW (WININET.@)
196 * Uploads a file to the FTP server
198 * RETURNS
199 * TRUE on success
200 * FALSE on failure
203 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
204 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
206 LPWININETFTPSESSIONW lpwfs;
207 LPWININETAPPINFOW hIC = NULL;
208 BOOL r = FALSE;
210 if (!lpszLocalFile || !lpszNewRemoteFile)
212 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
213 return FALSE;
216 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
217 if (!lpwfs)
219 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
220 return FALSE;
223 if (WH_HFTPSESSION != lpwfs->hdr.htype)
225 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
226 goto lend;
229 if (lpwfs->download_in_progress != NULL)
231 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
232 goto lend;
235 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
237 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
238 goto lend;
241 hIC = lpwfs->lpAppInfo;
242 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
244 WORKREQUEST workRequest;
245 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
247 workRequest.asyncproc = AsyncFtpPutFileProc;
248 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
249 req->lpszLocalFile = WININET_strdupW(lpszLocalFile);
250 req->lpszNewRemoteFile = WININET_strdupW(lpszNewRemoteFile);
251 req->dwFlags = dwFlags;
252 req->dwContext = dwContext;
254 r = INTERNET_AsyncCall(&workRequest);
256 else
258 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
259 lpszNewRemoteFile, dwFlags, dwContext);
262 lend:
263 WININET_Release( &lpwfs->hdr );
265 return r;
268 /***********************************************************************
269 * FTP_FtpPutFileW (Internal)
271 * Uploads a file to the FTP server
273 * RETURNS
274 * TRUE on success
275 * FALSE on failure
278 BOOL WINAPI FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
279 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
281 HANDLE hFile;
282 BOOL bSuccess = FALSE;
283 LPWININETAPPINFOW hIC = NULL;
284 INT nResCode;
286 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
288 /* Clear any error information */
289 INTERNET_SetLastError(0);
291 /* Open file to be uploaded */
292 if (INVALID_HANDLE_VALUE ==
293 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
294 /* Let CreateFile set the appropriate error */
295 return FALSE;
297 hIC = lpwfs->lpAppInfo;
299 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
301 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
303 INT nDataSocket;
305 /* Get data socket to server */
306 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
308 FTP_SendData(lpwfs, nDataSocket, hFile);
309 closesocket(nDataSocket);
310 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
311 if (nResCode)
313 if (nResCode == 226)
314 bSuccess = TRUE;
315 else
316 FTP_SetResponseError(nResCode);
321 if (lpwfs->lstnSocket != -1)
322 closesocket(lpwfs->lstnSocket);
324 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
326 INTERNET_ASYNC_RESULT iar;
328 iar.dwResult = (DWORD)bSuccess;
329 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
330 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
331 &iar, sizeof(INTERNET_ASYNC_RESULT));
334 CloseHandle(hFile);
336 return bSuccess;
340 /***********************************************************************
341 * FtpSetCurrentDirectoryA (WININET.@)
343 * Change the working directory on the FTP server
345 * RETURNS
346 * TRUE on success
347 * FALSE on failure
350 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
352 LPWSTR lpwzDirectory;
353 BOOL ret;
355 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
356 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
357 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
358 return ret;
362 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
364 struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
365 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
367 TRACE("%p\n", lpwfs);
369 FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
370 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
373 /***********************************************************************
374 * FtpSetCurrentDirectoryW (WININET.@)
376 * Change the working directory on the FTP server
378 * RETURNS
379 * TRUE on success
380 * FALSE on failure
383 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
385 LPWININETFTPSESSIONW lpwfs = NULL;
386 LPWININETAPPINFOW hIC = NULL;
387 BOOL r = FALSE;
389 if (!lpszDirectory)
391 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
392 goto lend;
395 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
396 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
398 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
399 goto lend;
402 if (lpwfs->download_in_progress != NULL)
404 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
405 goto lend;
408 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
410 hIC = lpwfs->lpAppInfo;
411 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
413 WORKREQUEST workRequest;
414 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
416 workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
417 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
418 req = &workRequest.u.FtpSetCurrentDirectoryW;
419 req->lpszDirectory = WININET_strdupW(lpszDirectory);
421 r = INTERNET_AsyncCall(&workRequest);
423 else
425 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
428 lend:
429 if( lpwfs )
430 WININET_Release( &lpwfs->hdr );
432 return r;
436 /***********************************************************************
437 * FTP_FtpSetCurrentDirectoryW (Internal)
439 * Change the working directory on the FTP server
441 * RETURNS
442 * TRUE on success
443 * FALSE on failure
446 BOOL WINAPI FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
448 INT nResCode;
449 LPWININETAPPINFOW hIC = NULL;
450 DWORD bSuccess = FALSE;
452 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
454 /* Clear any error information */
455 INTERNET_SetLastError(0);
457 hIC = lpwfs->lpAppInfo;
458 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
459 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
460 goto lend;
462 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
464 if (nResCode)
466 if (nResCode == 250)
467 bSuccess = TRUE;
468 else
469 FTP_SetResponseError(nResCode);
472 lend:
473 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
475 INTERNET_ASYNC_RESULT iar;
477 iar.dwResult = (DWORD)bSuccess;
478 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
479 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
480 &iar, sizeof(INTERNET_ASYNC_RESULT));
482 return bSuccess;
486 /***********************************************************************
487 * FtpCreateDirectoryA (WININET.@)
489 * Create new directory on the FTP server
491 * RETURNS
492 * TRUE on success
493 * FALSE on failure
496 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
498 LPWSTR lpwzDirectory;
499 BOOL ret;
501 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
502 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
503 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
504 return ret;
508 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
510 struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
511 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
513 TRACE(" %p\n", lpwfs);
515 FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
516 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
519 /***********************************************************************
520 * FtpCreateDirectoryW (WININET.@)
522 * Create new directory on the FTP server
524 * RETURNS
525 * TRUE on success
526 * FALSE on failure
529 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
531 LPWININETFTPSESSIONW lpwfs;
532 LPWININETAPPINFOW hIC = NULL;
533 BOOL r = FALSE;
535 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
536 if (!lpwfs)
538 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
539 return FALSE;
542 if (WH_HFTPSESSION != lpwfs->hdr.htype)
544 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
545 goto lend;
548 if (lpwfs->download_in_progress != NULL)
550 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
551 goto lend;
554 if (!lpszDirectory)
556 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
557 goto lend;
560 hIC = lpwfs->lpAppInfo;
561 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
563 WORKREQUEST workRequest;
564 struct WORKREQ_FTPCREATEDIRECTORYW *req;
566 workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
567 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
568 req = &workRequest.u.FtpCreateDirectoryW;
569 req->lpszDirectory = WININET_strdupW(lpszDirectory);
571 r = INTERNET_AsyncCall(&workRequest);
573 else
575 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
577 lend:
578 WININET_Release( &lpwfs->hdr );
580 return r;
584 /***********************************************************************
585 * FTP_FtpCreateDirectoryW (Internal)
587 * Create new directory on the FTP server
589 * RETURNS
590 * TRUE on success
591 * FALSE on failure
594 BOOL WINAPI FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
596 INT nResCode;
597 BOOL bSuccess = FALSE;
598 LPWININETAPPINFOW hIC = NULL;
600 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
602 /* Clear any error information */
603 INTERNET_SetLastError(0);
605 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
606 goto lend;
608 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
609 if (nResCode)
611 if (nResCode == 257)
612 bSuccess = TRUE;
613 else
614 FTP_SetResponseError(nResCode);
617 lend:
618 hIC = lpwfs->lpAppInfo;
619 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
621 INTERNET_ASYNC_RESULT iar;
623 iar.dwResult = (DWORD)bSuccess;
624 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
625 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
626 &iar, sizeof(INTERNET_ASYNC_RESULT));
629 return bSuccess;
632 /***********************************************************************
633 * FtpFindFirstFileA (WININET.@)
635 * Search the specified directory
637 * RETURNS
638 * HINTERNET on success
639 * NULL on failure
642 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
643 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
645 LPWSTR lpwzSearchFile;
646 WIN32_FIND_DATAW wfd;
647 LPWIN32_FIND_DATAW lpFindFileDataW;
648 HINTERNET ret;
650 lpwzSearchFile = lpszSearchFile?WININET_strdup_AtoW(lpszSearchFile):NULL;
651 lpFindFileDataW = lpFindFileData?&wfd:NULL;
652 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
653 HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
655 if(lpFindFileData) {
656 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
658 return ret;
662 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
664 struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
665 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
667 TRACE("%p\n", lpwfs);
669 FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
670 req->lpFindFileData, req->dwFlags, req->dwContext);
671 HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
674 /***********************************************************************
675 * FtpFindFirstFileW (WININET.@)
677 * Search the specified directory
679 * RETURNS
680 * HINTERNET on success
681 * NULL on failure
684 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
685 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
687 LPWININETFTPSESSIONW lpwfs;
688 LPWININETAPPINFOW hIC = NULL;
689 HINTERNET r = NULL;
691 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
692 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
694 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
695 goto lend;
698 if (lpwfs->download_in_progress != NULL)
700 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
701 goto lend;
704 hIC = lpwfs->lpAppInfo;
705 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
707 WORKREQUEST workRequest;
708 struct WORKREQ_FTPFINDFIRSTFILEW *req;
710 workRequest.asyncproc = AsyncFtpFindFirstFileProc;
711 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
712 req = &workRequest.u.FtpFindFirstFileW;
713 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : WININET_strdupW(lpszSearchFile);
714 req->lpFindFileData = lpFindFileData;
715 req->dwFlags = dwFlags;
716 req->dwContext= dwContext;
718 INTERNET_AsyncCall(&workRequest);
719 r = NULL;
721 else
723 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
724 dwFlags, dwContext);
726 lend:
727 if( lpwfs )
728 WININET_Release( &lpwfs->hdr );
730 return r;
734 /***********************************************************************
735 * FTP_FtpFindFirstFileW (Internal)
737 * Search the specified directory
739 * RETURNS
740 * HINTERNET on success
741 * NULL on failure
744 HINTERNET WINAPI FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
745 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
747 INT nResCode;
748 LPWININETAPPINFOW hIC = NULL;
749 HINTERNET hFindNext = NULL;
751 TRACE("\n");
753 /* Clear any error information */
754 INTERNET_SetLastError(0);
756 if (!FTP_InitListenSocket(lpwfs))
757 goto lend;
759 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
760 goto lend;
762 if (!FTP_SendPortOrPasv(lpwfs))
763 goto lend;
765 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
766 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
767 goto lend;
769 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
770 if (nResCode)
772 if (nResCode == 125 || nResCode == 150)
774 INT nDataSocket;
776 /* Get data socket to server */
777 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
779 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
780 closesocket(nDataSocket);
781 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
782 if (nResCode != 226 && nResCode != 250)
783 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
786 else
787 FTP_SetResponseError(nResCode);
790 lend:
791 if (lpwfs->lstnSocket != -1)
792 closesocket(lpwfs->lstnSocket);
794 hIC = lpwfs->lpAppInfo;
795 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
797 INTERNET_ASYNC_RESULT iar;
799 if (hFindNext)
801 iar.dwResult = (DWORD)hFindNext;
802 iar.dwError = ERROR_SUCCESS;
803 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
804 &iar, sizeof(INTERNET_ASYNC_RESULT));
807 iar.dwResult = (DWORD)hFindNext;
808 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
809 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
810 &iar, sizeof(INTERNET_ASYNC_RESULT));
813 return hFindNext;
817 /***********************************************************************
818 * FtpGetCurrentDirectoryA (WININET.@)
820 * Retrieves the current directory
822 * RETURNS
823 * TRUE on success
824 * FALSE on failure
827 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
828 LPDWORD lpdwCurrentDirectory)
830 WCHAR *dir = NULL;
831 DWORD len;
832 BOOL ret;
834 if(lpdwCurrentDirectory) {
835 len = *lpdwCurrentDirectory;
836 if(lpszCurrentDirectory)
838 dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
839 if (NULL == dir)
841 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
842 return FALSE;
846 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
848 if (ret && lpszCurrentDirectory)
849 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
851 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
852 HeapFree(GetProcessHeap(), 0, dir);
853 return ret;
857 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
859 struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
860 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
862 TRACE("%p\n", lpwfs);
864 FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
867 /***********************************************************************
868 * FtpGetCurrentDirectoryW (WININET.@)
870 * Retrieves the current directory
872 * RETURNS
873 * TRUE on success
874 * FALSE on failure
877 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
878 LPDWORD lpdwCurrentDirectory)
880 LPWININETFTPSESSIONW lpwfs;
881 LPWININETAPPINFOW hIC = NULL;
882 BOOL r = FALSE;
884 TRACE("len(%d)\n", *lpdwCurrentDirectory);
886 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
887 if (NULL == lpwfs)
889 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
890 goto lend;
893 if (WH_HFTPSESSION != lpwfs->hdr.htype)
895 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
896 goto lend;
899 if (!lpdwCurrentDirectory)
901 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
902 goto lend;
905 if (lpszCurrentDirectory == NULL)
907 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
908 goto lend;
911 if (lpwfs->download_in_progress != NULL)
913 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
914 goto lend;
917 hIC = lpwfs->lpAppInfo;
918 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
920 WORKREQUEST workRequest;
921 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
923 workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
924 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
925 req = &workRequest.u.FtpGetCurrentDirectoryW;
926 req->lpszDirectory = lpszCurrentDirectory;
927 req->lpdwDirectory = lpdwCurrentDirectory;
929 r = INTERNET_AsyncCall(&workRequest);
931 else
933 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
934 lpdwCurrentDirectory);
937 lend:
938 if( lpwfs )
939 WININET_Release( &lpwfs->hdr );
941 return r;
945 /***********************************************************************
946 * FTP_FtpGetCurrentDirectoryW (Internal)
948 * Retrieves the current directory
950 * RETURNS
951 * TRUE on success
952 * FALSE on failure
955 BOOL WINAPI FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
956 LPDWORD lpdwCurrentDirectory)
958 INT nResCode;
959 LPWININETAPPINFOW hIC = NULL;
960 DWORD bSuccess = FALSE;
962 TRACE("len(%d)\n", *lpdwCurrentDirectory);
964 /* Clear any error information */
965 INTERNET_SetLastError(0);
967 hIC = lpwfs->lpAppInfo;
968 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
969 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
970 goto lend;
972 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
973 if (nResCode)
975 if (nResCode == 257) /* Extract directory name */
977 DWORD firstpos, lastpos, len;
978 LPWSTR lpszResponseBuffer = WININET_strdup_AtoW(INTERNET_GetResponseBuffer());
980 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
982 if ('"' == lpszResponseBuffer[lastpos])
984 if (!firstpos)
985 firstpos = lastpos;
986 else
987 break;
990 len = lastpos - firstpos;
991 if (*lpdwCurrentDirectory >= len)
993 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
994 lpszCurrentDirectory[len - 1] = 0;
995 *lpdwCurrentDirectory = len;
996 bSuccess = TRUE;
998 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1000 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
1002 else
1003 FTP_SetResponseError(nResCode);
1006 lend:
1007 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1009 INTERNET_ASYNC_RESULT iar;
1011 iar.dwResult = (DWORD)bSuccess;
1012 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1013 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1014 &iar, sizeof(INTERNET_ASYNC_RESULT));
1017 return (DWORD) bSuccess;
1020 /***********************************************************************
1021 * FtpOpenFileA (WININET.@)
1023 * Open a remote file for writing or reading
1025 * RETURNS
1026 * HINTERNET handle on success
1027 * NULL on failure
1030 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1031 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1032 DWORD_PTR dwContext)
1034 LPWSTR lpwzFileName;
1035 HINTERNET ret;
1037 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1038 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1039 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1040 return ret;
1044 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1046 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1047 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1049 TRACE("%p\n", lpwfs);
1051 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1052 req->dwAccess, req->dwFlags, req->dwContext);
1053 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1056 /***********************************************************************
1057 * FtpOpenFileW (WININET.@)
1059 * Open a remote file for writing or reading
1061 * RETURNS
1062 * HINTERNET handle on success
1063 * NULL on failure
1066 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1067 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1068 DWORD_PTR dwContext)
1070 LPWININETFTPSESSIONW lpwfs;
1071 LPWININETAPPINFOW hIC = NULL;
1072 HINTERNET r = NULL;
1074 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1075 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1077 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1078 if (!lpwfs)
1080 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1081 return FALSE;
1084 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1086 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1087 goto lend;
1090 if ((!lpszFileName) ||
1091 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1092 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1094 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1095 goto lend;
1098 if (lpwfs->download_in_progress != NULL)
1100 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1101 goto lend;
1104 hIC = lpwfs->lpAppInfo;
1105 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1107 WORKREQUEST workRequest;
1108 struct WORKREQ_FTPOPENFILEW *req;
1110 workRequest.asyncproc = AsyncFtpOpenFileProc;
1111 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1112 req = &workRequest.u.FtpOpenFileW;
1113 req->lpszFilename = WININET_strdupW(lpszFileName);
1114 req->dwAccess = fdwAccess;
1115 req->dwFlags = dwFlags;
1116 req->dwContext = dwContext;
1118 INTERNET_AsyncCall(&workRequest);
1119 r = NULL;
1121 else
1123 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1126 lend:
1127 WININET_Release( &lpwfs->hdr );
1129 return r;
1133 /***********************************************************************
1134 * FTP_FtpOpenFileW (Internal)
1136 * Open a remote file for writing or reading
1138 * RETURNS
1139 * HINTERNET handle on success
1140 * NULL on failure
1143 HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
1144 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1145 DWORD_PTR dwContext)
1147 INT nDataSocket;
1148 BOOL bSuccess = FALSE;
1149 LPWININETFTPFILE lpwh = NULL;
1150 LPWININETAPPINFOW hIC = NULL;
1151 HINTERNET handle = NULL;
1153 TRACE("\n");
1155 /* Clear any error information */
1156 INTERNET_SetLastError(0);
1158 if (GENERIC_READ == fdwAccess)
1160 /* Set up socket to retrieve data */
1161 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1163 else if (GENERIC_WRITE == fdwAccess)
1165 /* Set up socket to send data */
1166 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1169 /* Get data socket to server */
1170 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1172 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPFILE));
1173 lpwh->hdr.htype = WH_HFILE;
1174 lpwh->hdr.dwFlags = dwFlags;
1175 lpwh->hdr.dwContext = dwContext;
1176 lpwh->hdr.dwRefCount = 1;
1177 lpwh->hdr.close_connection = NULL;
1178 lpwh->hdr.destroy = FTP_CloseFileTransferHandle;
1179 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1180 lpwh->nDataSocket = nDataSocket;
1181 lpwh->session_deleted = FALSE;
1183 WININET_AddRef( &lpwfs->hdr );
1184 lpwh->lpFtpSession = lpwfs;
1185 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1187 handle = WININET_AllocHandle( &lpwh->hdr );
1188 if( !handle )
1189 goto lend;
1191 /* Indicate that a download is currently in progress */
1192 lpwfs->download_in_progress = lpwh;
1195 if (lpwfs->lstnSocket != -1)
1196 closesocket(lpwfs->lstnSocket);
1198 hIC = lpwfs->lpAppInfo;
1199 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1201 INTERNET_ASYNC_RESULT iar;
1203 if (lpwh)
1205 iar.dwResult = (DWORD)handle;
1206 iar.dwError = ERROR_SUCCESS;
1207 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1208 &iar, sizeof(INTERNET_ASYNC_RESULT));
1211 iar.dwResult = (DWORD)bSuccess;
1212 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1213 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1214 &iar, sizeof(INTERNET_ASYNC_RESULT));
1217 lend:
1218 if( lpwh )
1219 WININET_Release( &lpwh->hdr );
1221 return handle;
1225 /***********************************************************************
1226 * FtpGetFileA (WININET.@)
1228 * Retrieve file from the FTP server
1230 * RETURNS
1231 * TRUE on success
1232 * FALSE on failure
1235 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1236 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1237 DWORD_PTR dwContext)
1239 LPWSTR lpwzRemoteFile;
1240 LPWSTR lpwzNewFile;
1241 BOOL ret;
1243 lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
1244 lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
1245 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1246 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1247 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1248 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1249 return ret;
1253 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1255 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1256 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1258 TRACE("%p\n", lpwfs);
1260 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1261 req->lpszNewFile, req->fFailIfExists,
1262 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1263 HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1264 HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1268 /***********************************************************************
1269 * FtpGetFileW (WININET.@)
1271 * Retrieve file from the FTP server
1273 * RETURNS
1274 * TRUE on success
1275 * FALSE on failure
1278 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1279 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1280 DWORD_PTR dwContext)
1282 LPWININETFTPSESSIONW lpwfs;
1283 LPWININETAPPINFOW hIC = NULL;
1284 BOOL r = FALSE;
1286 if (!lpszRemoteFile || !lpszNewFile)
1288 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1289 return FALSE;
1292 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
1293 if (!lpwfs)
1295 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1296 return FALSE;
1299 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1301 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1302 goto lend;
1305 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1307 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1308 goto lend;
1311 if (lpwfs->download_in_progress != NULL)
1313 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1314 goto lend;
1317 hIC = lpwfs->lpAppInfo;
1318 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1320 WORKREQUEST workRequest;
1321 struct WORKREQ_FTPGETFILEW *req;
1323 workRequest.asyncproc = AsyncFtpGetFileProc;
1324 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1325 req = &workRequest.u.FtpGetFileW;
1326 req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
1327 req->lpszNewFile = WININET_strdupW(lpszNewFile);
1328 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1329 req->fFailIfExists = fFailIfExists;
1330 req->dwFlags = dwInternetFlags;
1331 req->dwContext = dwContext;
1333 r = INTERNET_AsyncCall(&workRequest);
1335 else
1337 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1338 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1341 lend:
1342 WININET_Release( &lpwfs->hdr );
1344 return r;
1348 /***********************************************************************
1349 * FTP_FtpGetFileW (Internal)
1351 * Retrieve file from the FTP server
1353 * RETURNS
1354 * TRUE on success
1355 * FALSE on failure
1358 BOOL WINAPI FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1359 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1360 DWORD_PTR dwContext)
1362 BOOL bSuccess = FALSE;
1363 HANDLE hFile;
1364 LPWININETAPPINFOW hIC = NULL;
1366 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1368 /* Clear any error information */
1369 INTERNET_SetLastError(0);
1371 /* Ensure we can write to lpszNewfile by opening it */
1372 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1373 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1374 if (INVALID_HANDLE_VALUE == hFile)
1375 return FALSE;
1377 /* Set up socket to retrieve data */
1378 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1380 INT nDataSocket;
1382 /* Get data socket to server */
1383 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1385 INT nResCode;
1387 /* Receive data */
1388 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1389 closesocket(nDataSocket);
1391 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1392 if (nResCode)
1394 if (nResCode == 226)
1395 bSuccess = TRUE;
1396 else
1397 FTP_SetResponseError(nResCode);
1402 if (lpwfs->lstnSocket != -1)
1403 closesocket(lpwfs->lstnSocket);
1405 CloseHandle(hFile);
1407 hIC = lpwfs->lpAppInfo;
1408 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1410 INTERNET_ASYNC_RESULT iar;
1412 iar.dwResult = (DWORD)bSuccess;
1413 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1414 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1415 &iar, sizeof(INTERNET_ASYNC_RESULT));
1418 return bSuccess;
1421 /***********************************************************************
1422 * FtpGetFileSize (WININET.@)
1424 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1426 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1428 if (lpdwFileSizeHigh)
1429 *lpdwFileSizeHigh = 0;
1431 return 0;
1434 /***********************************************************************
1435 * FtpDeleteFileA (WININET.@)
1437 * Delete a file on the ftp server
1439 * RETURNS
1440 * TRUE on success
1441 * FALSE on failure
1444 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1446 LPWSTR lpwzFileName;
1447 BOOL ret;
1449 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1450 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1451 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1452 return ret;
1455 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1457 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1458 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1460 TRACE("%p\n", lpwfs);
1462 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1463 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1466 /***********************************************************************
1467 * FtpDeleteFileW (WININET.@)
1469 * Delete a file on the ftp server
1471 * RETURNS
1472 * TRUE on success
1473 * FALSE on failure
1476 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1478 LPWININETFTPSESSIONW lpwfs;
1479 LPWININETAPPINFOW hIC = NULL;
1480 BOOL r = FALSE;
1482 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1483 if (!lpwfs)
1485 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1486 return FALSE;
1489 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1491 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1492 goto lend;
1495 if (lpwfs->download_in_progress != NULL)
1497 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1498 goto lend;
1501 if (!lpszFileName)
1503 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1504 goto lend;
1507 hIC = lpwfs->lpAppInfo;
1508 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1510 WORKREQUEST workRequest;
1511 struct WORKREQ_FTPDELETEFILEW *req;
1513 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1514 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1515 req = &workRequest.u.FtpDeleteFileW;
1516 req->lpszFilename = WININET_strdupW(lpszFileName);
1518 r = INTERNET_AsyncCall(&workRequest);
1520 else
1522 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1525 lend:
1526 WININET_Release( &lpwfs->hdr );
1528 return r;
1531 /***********************************************************************
1532 * FTP_FtpDeleteFileW (Internal)
1534 * Delete a file on the ftp server
1536 * RETURNS
1537 * TRUE on success
1538 * FALSE on failure
1541 BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName)
1543 INT nResCode;
1544 BOOL bSuccess = FALSE;
1545 LPWININETAPPINFOW hIC = NULL;
1547 TRACE("%p\n", lpwfs);
1549 /* Clear any error information */
1550 INTERNET_SetLastError(0);
1552 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1553 goto lend;
1555 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1556 if (nResCode)
1558 if (nResCode == 250)
1559 bSuccess = TRUE;
1560 else
1561 FTP_SetResponseError(nResCode);
1563 lend:
1564 hIC = lpwfs->lpAppInfo;
1565 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1567 INTERNET_ASYNC_RESULT iar;
1569 iar.dwResult = (DWORD)bSuccess;
1570 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1571 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1572 &iar, sizeof(INTERNET_ASYNC_RESULT));
1575 return bSuccess;
1579 /***********************************************************************
1580 * FtpRemoveDirectoryA (WININET.@)
1582 * Remove a directory on the ftp server
1584 * RETURNS
1585 * TRUE on success
1586 * FALSE on failure
1589 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1591 LPWSTR lpwzDirectory;
1592 BOOL ret;
1594 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
1595 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1596 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1597 return ret;
1600 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1602 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1603 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1605 TRACE("%p\n", lpwfs);
1607 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1608 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1611 /***********************************************************************
1612 * FtpRemoveDirectoryW (WININET.@)
1614 * Remove a directory on the ftp server
1616 * RETURNS
1617 * TRUE on success
1618 * FALSE on failure
1621 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1623 LPWININETFTPSESSIONW lpwfs;
1624 LPWININETAPPINFOW hIC = NULL;
1625 BOOL r = FALSE;
1627 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1628 if (!lpwfs)
1630 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1631 return FALSE;
1634 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1636 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1637 goto lend;
1640 if (lpwfs->download_in_progress != NULL)
1642 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1643 goto lend;
1646 if (!lpszDirectory)
1648 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1649 goto lend;
1652 hIC = lpwfs->lpAppInfo;
1653 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1655 WORKREQUEST workRequest;
1656 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1658 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1659 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1660 req = &workRequest.u.FtpRemoveDirectoryW;
1661 req->lpszDirectory = WININET_strdupW(lpszDirectory);
1663 r = INTERNET_AsyncCall(&workRequest);
1665 else
1667 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1670 lend:
1671 WININET_Release( &lpwfs->hdr );
1673 return r;
1676 /***********************************************************************
1677 * FTP_FtpRemoveDirectoryW (Internal)
1679 * Remove a directory on the ftp server
1681 * RETURNS
1682 * TRUE on success
1683 * FALSE on failure
1686 BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
1688 INT nResCode;
1689 BOOL bSuccess = FALSE;
1690 LPWININETAPPINFOW hIC = NULL;
1692 TRACE("\n");
1694 /* Clear any error information */
1695 INTERNET_SetLastError(0);
1697 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1698 goto lend;
1700 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1701 if (nResCode)
1703 if (nResCode == 250)
1704 bSuccess = TRUE;
1705 else
1706 FTP_SetResponseError(nResCode);
1709 lend:
1710 hIC = lpwfs->lpAppInfo;
1711 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1713 INTERNET_ASYNC_RESULT iar;
1715 iar.dwResult = (DWORD)bSuccess;
1716 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1717 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1718 &iar, sizeof(INTERNET_ASYNC_RESULT));
1721 return bSuccess;
1725 /***********************************************************************
1726 * FtpRenameFileA (WININET.@)
1728 * Rename a file on the ftp server
1730 * RETURNS
1731 * TRUE on success
1732 * FALSE on failure
1735 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1737 LPWSTR lpwzSrc;
1738 LPWSTR lpwzDest;
1739 BOOL ret;
1741 lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL;
1742 lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL;
1743 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1744 HeapFree(GetProcessHeap(), 0, lpwzSrc);
1745 HeapFree(GetProcessHeap(), 0, lpwzDest);
1746 return ret;
1749 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
1751 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
1752 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1754 TRACE("%p\n", lpwfs);
1756 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
1757 HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
1758 HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
1761 /***********************************************************************
1762 * FtpRenameFileW (WININET.@)
1764 * Rename a file on the ftp server
1766 * RETURNS
1767 * TRUE on success
1768 * FALSE on failure
1771 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
1773 LPWININETFTPSESSIONW lpwfs;
1774 LPWININETAPPINFOW hIC = NULL;
1775 BOOL r = FALSE;
1777 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1778 if (!lpwfs)
1780 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1781 return FALSE;
1784 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1786 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1787 goto lend;
1790 if (lpwfs->download_in_progress != NULL)
1792 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1793 goto lend;
1796 if (!lpszSrc || !lpszDest)
1798 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1799 goto lend;
1802 hIC = lpwfs->lpAppInfo;
1803 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1805 WORKREQUEST workRequest;
1806 struct WORKREQ_FTPRENAMEFILEW *req;
1808 workRequest.asyncproc = AsyncFtpRenameFileProc;
1809 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1810 req = &workRequest.u.FtpRenameFileW;
1811 req->lpszSrcFile = WININET_strdupW(lpszSrc);
1812 req->lpszDestFile = WININET_strdupW(lpszDest);
1814 r = INTERNET_AsyncCall(&workRequest);
1816 else
1818 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
1821 lend:
1822 WININET_Release( &lpwfs->hdr );
1824 return r;
1827 /***********************************************************************
1828 * FTP_FtpRenameFileW (Internal)
1830 * Rename a file on the ftp server
1832 * RETURNS
1833 * TRUE on success
1834 * FALSE on failure
1837 BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs,
1838 LPCWSTR lpszSrc, LPCWSTR lpszDest)
1840 INT nResCode;
1841 BOOL bSuccess = FALSE;
1842 LPWININETAPPINFOW hIC = NULL;
1844 TRACE("\n");
1846 /* Clear any error information */
1847 INTERNET_SetLastError(0);
1849 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
1850 goto lend;
1852 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1853 if (nResCode == 350)
1855 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
1856 goto lend;
1858 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1861 if (nResCode == 250)
1862 bSuccess = TRUE;
1863 else
1864 FTP_SetResponseError(nResCode);
1866 lend:
1867 hIC = lpwfs->lpAppInfo;
1868 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1870 INTERNET_ASYNC_RESULT iar;
1872 iar.dwResult = (DWORD)bSuccess;
1873 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1874 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1875 &iar, sizeof(INTERNET_ASYNC_RESULT));
1878 return bSuccess;
1881 /***********************************************************************
1882 * FtpCommandA (WININET.@)
1884 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1885 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1887 BOOL r;
1888 WCHAR *cmdW;
1890 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1891 debugstr_a(lpszCommand), dwContext, phFtpCommand);
1893 if (fExpectResponse)
1895 FIXME("data connection not supported\n");
1896 return FALSE;
1899 if (!lpszCommand || !lpszCommand[0])
1901 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1902 return FALSE;
1905 if (!(cmdW = WININET_strdup_AtoW(lpszCommand)))
1907 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1908 return FALSE;
1911 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
1913 HeapFree(GetProcessHeap(), 0, cmdW);
1914 return r;
1917 /***********************************************************************
1918 * FtpCommandW (WININET.@)
1920 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1921 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1923 BOOL r = FALSE;
1924 LPWININETFTPSESSIONW lpwfs;
1925 LPSTR cmd = NULL;
1926 DWORD len, nBytesSent= 0;
1927 INT nResCode, nRC = 0;
1929 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1930 debugstr_w(lpszCommand), dwContext, phFtpCommand);
1932 if (!lpszCommand || !lpszCommand[0])
1934 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1935 return FALSE;
1938 if (fExpectResponse)
1940 FIXME("data connection not supported\n");
1941 return FALSE;
1944 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
1945 if (!lpwfs)
1947 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1948 return FALSE;
1951 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1953 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1954 goto lend;
1957 if (lpwfs->download_in_progress != NULL)
1959 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1960 goto lend;
1963 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
1964 if ((cmd = HeapAlloc(GetProcessHeap(), 0, len )))
1965 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
1966 else
1968 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1969 goto lend;
1972 strcat(cmd, szCRLF);
1973 len--;
1975 TRACE("Sending (%s) len(%d)\n", cmd, len);
1976 while ((nBytesSent < len) && (nRC != -1))
1978 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
1979 if (nRC != -1)
1981 nBytesSent += nRC;
1982 TRACE("Sent %d bytes\n", nRC);
1986 if (nBytesSent)
1988 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1989 if (nResCode > 0 && nResCode < 400)
1990 r = TRUE;
1991 else
1992 FTP_SetResponseError(nResCode);
1995 lend:
1996 WININET_Release( &lpwfs->hdr );
1997 HeapFree(GetProcessHeap(), 0, cmd);
1998 return r;
2001 /***********************************************************************
2002 * FTP_Connect (internal)
2004 * Connect to a ftp server
2006 * RETURNS
2007 * HINTERNET a session handle on success
2008 * NULL on failure
2010 * NOTES:
2012 * Windows uses 'anonymous' as the username, when given a NULL username
2013 * and a NULL password. The password is first looked up in:
2015 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2017 * If this entry is not present it uses the current username as the password.
2021 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
2022 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2023 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2024 DWORD dwInternalFlags)
2026 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2027 'M','i','c','r','o','s','o','f','t','\\',
2028 'W','i','n','d','o','w','s','\\',
2029 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2030 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2031 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2032 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2033 static const WCHAR szEmpty[] = {'\0'};
2034 struct sockaddr_in socketAddr;
2035 INT nsocket = -1;
2036 UINT sock_namelen;
2037 BOOL bSuccess = FALSE;
2038 LPWININETFTPSESSIONW lpwfs = NULL;
2039 HINTERNET handle = NULL;
2041 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2042 hIC, debugstr_w(lpszServerName),
2043 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2045 assert( hIC->hdr.htype == WH_HINIT );
2047 if (NULL == lpszUserName && NULL != lpszPassword)
2049 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2050 goto lerror;
2053 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
2054 if (NULL == lpwfs)
2056 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2057 goto lerror;
2060 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2061 nServerPort = INTERNET_DEFAULT_FTP_PORT;
2063 lpwfs->hdr.htype = WH_HFTPSESSION;
2064 lpwfs->hdr.dwFlags = dwFlags;
2065 lpwfs->hdr.dwContext = dwContext;
2066 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2067 lpwfs->hdr.dwRefCount = 1;
2068 lpwfs->hdr.close_connection = FTP_CloseConnection;
2069 lpwfs->hdr.destroy = FTP_CloseSessionHandle;
2070 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2071 lpwfs->download_in_progress = NULL;
2072 lpwfs->sndSocket = -1;
2073 lpwfs->lstnSocket = -1;
2074 lpwfs->pasvSocket = -1;
2076 WININET_AddRef( &hIC->hdr );
2077 lpwfs->lpAppInfo = hIC;
2078 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2080 handle = WININET_AllocHandle( &lpwfs->hdr );
2081 if( !handle )
2083 ERR("Failed to alloc handle\n");
2084 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2085 goto lerror;
2088 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2089 if(strchrW(hIC->lpszProxy, ' '))
2090 FIXME("Several proxies not implemented.\n");
2091 if(hIC->lpszProxyBypass)
2092 FIXME("Proxy bypass is ignored.\n");
2094 if ( !lpszUserName) {
2095 HKEY key;
2096 WCHAR szPassword[MAX_PATH];
2097 DWORD len = sizeof(szPassword);
2099 lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
2101 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2102 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2103 /* Nothing in the registry, get the username and use that as the password */
2104 if (!GetUserNameW(szPassword, &len)) {
2105 /* Should never get here, but use an empty password as failsafe */
2106 strcpyW(szPassword, szEmpty);
2109 RegCloseKey(key);
2111 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2112 lpwfs->lpszPassword = WININET_strdupW(szPassword);
2114 else {
2115 lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
2117 if (lpszPassword)
2118 lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
2119 else
2120 lpwfs->lpszPassword = WININET_strdupW(szEmpty);
2123 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2124 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2126 INTERNET_ASYNC_RESULT iar;
2128 iar.dwResult = (DWORD)handle;
2129 iar.dwError = ERROR_SUCCESS;
2131 SendAsyncCallback(&hIC->hdr, dwContext,
2132 INTERNET_STATUS_HANDLE_CREATED, &iar,
2133 sizeof(INTERNET_ASYNC_RESULT));
2136 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2137 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2139 if (!GetAddress(lpszServerName, nServerPort, &socketAddr))
2141 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2142 goto lerror;
2145 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2146 (LPWSTR) lpszServerName, strlenW(lpszServerName));
2148 nsocket = socket(AF_INET,SOCK_STREAM,0);
2149 if (nsocket == -1)
2151 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2152 goto lerror;
2155 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2156 &socketAddr, sizeof(struct sockaddr_in));
2158 if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
2160 ERR("Unable to connect (%s)\n", strerror(errno));
2161 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2163 else
2165 TRACE("Connected to server\n");
2166 lpwfs->sndSocket = nsocket;
2167 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2168 &socketAddr, sizeof(struct sockaddr_in));
2170 sock_namelen = sizeof(lpwfs->socketAddress);
2171 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2173 if (FTP_ConnectToHost(lpwfs))
2175 TRACE("Successfully logged into server\n");
2176 bSuccess = TRUE;
2180 lerror:
2181 if (lpwfs) WININET_Release( &lpwfs->hdr );
2183 if (!bSuccess && handle)
2185 WININET_FreeHandle( handle );
2186 handle = NULL;
2189 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2191 INTERNET_ASYNC_RESULT iar;
2193 iar.dwResult = bSuccess ? (DWORD_PTR)lpwfs : 0;
2194 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2195 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2196 &iar, sizeof(INTERNET_ASYNC_RESULT));
2199 return handle;
2203 /***********************************************************************
2204 * FTP_ConnectToHost (internal)
2206 * Connect to a ftp server
2208 * RETURNS
2209 * TRUE on success
2210 * NULL on failure
2213 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
2215 INT nResCode;
2216 BOOL bSuccess = FALSE;
2218 TRACE("\n");
2219 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2221 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2222 goto lend;
2224 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2225 if (nResCode)
2227 /* Login successful... */
2228 if (nResCode == 230)
2229 bSuccess = TRUE;
2230 /* User name okay, need password... */
2231 else if (nResCode == 331)
2232 bSuccess = FTP_SendPassword(lpwfs);
2233 /* Need account for login... */
2234 else if (nResCode == 332)
2235 bSuccess = FTP_SendAccount(lpwfs);
2236 else
2237 FTP_SetResponseError(nResCode);
2240 TRACE("Returning %d\n", bSuccess);
2241 lend:
2242 return bSuccess;
2246 /***********************************************************************
2247 * FTP_SendCommandA (internal)
2249 * Send command to server
2251 * RETURNS
2252 * TRUE on success
2253 * NULL on failure
2256 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2257 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
2259 DWORD len;
2260 CHAR *buf;
2261 DWORD nBytesSent = 0;
2262 int nRC = 0;
2263 DWORD dwParamLen;
2265 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2267 if (lpfnStatusCB)
2269 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2272 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2273 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2274 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2276 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2277 return FALSE;
2279 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2280 dwParamLen ? lpszParam : "", szCRLF);
2282 TRACE("Sending (%s) len(%d)\n", buf, len);
2283 while((nBytesSent < len) && (nRC != -1))
2285 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2286 nBytesSent += nRC;
2289 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2291 if (lpfnStatusCB)
2293 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2294 &nBytesSent, sizeof(DWORD));
2297 TRACE("Sent %d bytes\n", nBytesSent);
2298 return (nRC != -1);
2301 /***********************************************************************
2302 * FTP_SendCommand (internal)
2304 * Send command to server
2306 * RETURNS
2307 * TRUE on success
2308 * NULL on failure
2311 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2312 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
2314 BOOL ret;
2315 LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
2316 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2317 HeapFree(GetProcessHeap(), 0, lpszParamA);
2318 return ret;
2321 /***********************************************************************
2322 * FTP_ReceiveResponse (internal)
2324 * Receive response from server
2326 * RETURNS
2327 * Reply code on success
2328 * 0 on failure
2331 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD_PTR dwContext)
2333 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2334 DWORD nRecv;
2335 INT rc = 0;
2336 char firstprefix[5];
2337 BOOL multiline = FALSE;
2338 LPWININETAPPINFOW hIC = NULL;
2340 TRACE("socket(%d)\n", lpwfs->sndSocket);
2342 hIC = lpwfs->lpAppInfo;
2343 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2345 while(1)
2347 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2348 goto lerror;
2350 if (nRecv >= 3)
2352 if(!multiline)
2354 if(lpszResponse[3] != '-')
2355 break;
2356 else
2357 { /* Start of multiline repsonse. Loop until we get "nnn " */
2358 multiline = TRUE;
2359 memcpy(firstprefix, lpszResponse, 3);
2360 firstprefix[3] = ' ';
2361 firstprefix[4] = '\0';
2364 else
2366 if(!memcmp(firstprefix, lpszResponse, 4))
2367 break;
2372 if (nRecv >= 3)
2374 rc = atoi(lpszResponse);
2376 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2377 &nRecv, sizeof(DWORD));
2380 lerror:
2381 TRACE("return %d\n", rc);
2382 return rc;
2386 /***********************************************************************
2387 * FTP_SendPassword (internal)
2389 * Send password to ftp server
2391 * RETURNS
2392 * TRUE on success
2393 * NULL on failure
2396 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2398 INT nResCode;
2399 BOOL bSuccess = FALSE;
2401 TRACE("\n");
2402 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2403 goto lend;
2405 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2406 if (nResCode)
2408 TRACE("Received reply code %d\n", nResCode);
2409 /* Login successful... */
2410 if (nResCode == 230)
2411 bSuccess = TRUE;
2412 /* Command not implemented, superfluous at the server site... */
2413 /* Need account for login... */
2414 else if (nResCode == 332)
2415 bSuccess = FTP_SendAccount(lpwfs);
2416 else
2417 FTP_SetResponseError(nResCode);
2420 lend:
2421 TRACE("Returning %d\n", bSuccess);
2422 return bSuccess;
2426 /***********************************************************************
2427 * FTP_SendAccount (internal)
2431 * RETURNS
2432 * TRUE on success
2433 * FALSE on failure
2436 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2438 INT nResCode;
2439 BOOL bSuccess = FALSE;
2441 TRACE("\n");
2442 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2443 goto lend;
2445 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2446 if (nResCode)
2447 bSuccess = TRUE;
2448 else
2449 FTP_SetResponseError(nResCode);
2451 lend:
2452 return bSuccess;
2456 /***********************************************************************
2457 * FTP_SendStore (internal)
2459 * Send request to upload file to ftp server
2461 * RETURNS
2462 * TRUE on success
2463 * FALSE on failure
2466 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2468 INT nResCode;
2469 BOOL bSuccess = FALSE;
2471 TRACE("\n");
2472 if (!FTP_InitListenSocket(lpwfs))
2473 goto lend;
2475 if (!FTP_SendType(lpwfs, dwType))
2476 goto lend;
2478 if (!FTP_SendPortOrPasv(lpwfs))
2479 goto lend;
2481 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2482 goto lend;
2483 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2484 if (nResCode)
2486 if (nResCode == 150 || nResCode == 125)
2487 bSuccess = TRUE;
2488 else
2489 FTP_SetResponseError(nResCode);
2492 lend:
2493 if (!bSuccess && lpwfs->lstnSocket != -1)
2495 closesocket(lpwfs->lstnSocket);
2496 lpwfs->lstnSocket = -1;
2499 return bSuccess;
2503 /***********************************************************************
2504 * FTP_InitListenSocket (internal)
2506 * Create a socket to listen for server response
2508 * RETURNS
2509 * TRUE on success
2510 * FALSE on failure
2513 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2515 BOOL bSuccess = FALSE;
2516 socklen_t namelen = sizeof(struct sockaddr_in);
2518 TRACE("\n");
2520 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2521 if (lpwfs->lstnSocket == -1)
2523 TRACE("Unable to create listening socket\n");
2524 goto lend;
2527 /* We obtain our ip addr from the name of the command channel socket */
2528 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2530 /* and get the system to assign us a port */
2531 lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
2533 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2535 TRACE("Unable to bind socket\n");
2536 goto lend;
2539 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2541 TRACE("listen failed\n");
2542 goto lend;
2545 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2546 bSuccess = TRUE;
2548 lend:
2549 if (!bSuccess && lpwfs->lstnSocket != -1)
2551 closesocket(lpwfs->lstnSocket);
2552 lpwfs->lstnSocket = -1;
2555 return bSuccess;
2559 /***********************************************************************
2560 * FTP_SendType (internal)
2562 * Tell server type of data being transferred
2564 * RETURNS
2565 * TRUE on success
2566 * FALSE on failure
2568 * W98SE doesn't cache the type that's currently set
2569 * (i.e. it sends it always),
2570 * so we probably don't want to do that either.
2572 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2574 INT nResCode;
2575 WCHAR type[] = { 'I','\0' };
2576 BOOL bSuccess = FALSE;
2578 TRACE("\n");
2579 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2580 type[0] = 'A';
2582 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2583 goto lend;
2585 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2586 if (nResCode)
2588 if (nResCode == 2)
2589 bSuccess = TRUE;
2590 else
2591 FTP_SetResponseError(nResCode);
2594 lend:
2595 return bSuccess;
2599 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2600 /***********************************************************************
2601 * FTP_GetFileSize (internal)
2603 * Retrieves from the server the size of the given file
2605 * RETURNS
2606 * TRUE on success
2607 * FALSE on failure
2610 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2612 INT nResCode;
2613 BOOL bSuccess = FALSE;
2615 TRACE("\n");
2617 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2618 goto lend;
2620 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2621 if (nResCode)
2623 if (nResCode == 213) {
2624 /* Now parses the output to get the actual file size */
2625 int i;
2626 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2628 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2629 if (lpszResponseBuffer[i] == '\0') return FALSE;
2630 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2632 bSuccess = TRUE;
2633 } else {
2634 FTP_SetResponseError(nResCode);
2638 lend:
2639 return bSuccess;
2641 #endif
2644 /***********************************************************************
2645 * FTP_SendPort (internal)
2647 * Tell server which port to use
2649 * RETURNS
2650 * TRUE on success
2651 * FALSE on failure
2654 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2656 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2657 INT nResCode;
2658 WCHAR szIPAddress[64];
2659 BOOL bSuccess = FALSE;
2660 TRACE("\n");
2662 sprintfW(szIPAddress, szIPFormat,
2663 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2664 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2665 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2666 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2667 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2668 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2670 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2671 goto lend;
2673 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2674 if (nResCode)
2676 if (nResCode == 200)
2677 bSuccess = TRUE;
2678 else
2679 FTP_SetResponseError(nResCode);
2682 lend:
2683 return bSuccess;
2687 /***********************************************************************
2688 * FTP_DoPassive (internal)
2690 * Tell server that we want to do passive transfers
2691 * and connect data socket
2693 * RETURNS
2694 * TRUE on success
2695 * FALSE on failure
2698 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2700 INT nResCode;
2701 BOOL bSuccess = FALSE;
2703 TRACE("\n");
2704 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2705 goto lend;
2707 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2708 if (nResCode)
2710 if (nResCode == 227)
2712 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2713 LPSTR p;
2714 int f[6];
2715 int i;
2716 char *pAddr, *pPort;
2717 INT nsocket = -1;
2718 struct sockaddr_in dataSocketAddress;
2720 p = lpszResponseBuffer+4; /* skip status code */
2721 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
2723 if (*p == '\0')
2725 ERR("no address found in response, aborting\n");
2726 goto lend;
2729 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
2730 &f[4], &f[5]) != 6)
2732 ERR("unknown response address format '%s', aborting\n", p);
2733 goto lend;
2735 for (i=0; i < 6; i++)
2736 f[i] = f[i] & 0xff;
2738 dataSocketAddress = lpwfs->socketAddress;
2739 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2740 pPort = (char *)&(dataSocketAddress.sin_port);
2741 pAddr[0] = f[0];
2742 pAddr[1] = f[1];
2743 pAddr[2] = f[2];
2744 pAddr[3] = f[3];
2745 pPort[0] = f[4];
2746 pPort[1] = f[5];
2748 nsocket = socket(AF_INET,SOCK_STREAM,0);
2749 if (nsocket == -1)
2750 goto lend;
2752 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2754 ERR("can't connect passive FTP data port.\n");
2755 closesocket(nsocket);
2756 goto lend;
2758 lpwfs->pasvSocket = nsocket;
2759 bSuccess = TRUE;
2761 else
2762 FTP_SetResponseError(nResCode);
2765 lend:
2766 return bSuccess;
2770 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2772 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2774 if (!FTP_DoPassive(lpwfs))
2775 return FALSE;
2777 else
2779 if (!FTP_SendPort(lpwfs))
2780 return FALSE;
2782 return TRUE;
2786 /***********************************************************************
2787 * FTP_GetDataSocket (internal)
2789 * Either accepts an incoming data socket connection from the server
2790 * or just returns the already opened socket after a PASV command
2791 * in case of passive FTP.
2794 * RETURNS
2795 * TRUE on success
2796 * FALSE on failure
2799 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
2801 struct sockaddr_in saddr;
2802 socklen_t addrlen = sizeof(struct sockaddr);
2804 TRACE("\n");
2805 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2807 *nDataSocket = lpwfs->pasvSocket;
2809 else
2811 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
2812 closesocket(lpwfs->lstnSocket);
2813 lpwfs->lstnSocket = -1;
2815 return *nDataSocket != -1;
2819 /***********************************************************************
2820 * FTP_SendData (internal)
2822 * Send data to the server
2824 * RETURNS
2825 * TRUE on success
2826 * FALSE on failure
2829 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
2831 BY_HANDLE_FILE_INFORMATION fi;
2832 DWORD nBytesRead = 0;
2833 DWORD nBytesSent = 0;
2834 DWORD nTotalSent = 0;
2835 DWORD nBytesToSend, nLen;
2836 int nRC = 1;
2837 time_t s_long_time, e_long_time;
2838 LONG nSeconds;
2839 CHAR *lpszBuffer;
2841 TRACE("\n");
2842 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2844 /* Get the size of the file. */
2845 GetFileInformationByHandle(hFile, &fi);
2846 time(&s_long_time);
2850 nBytesToSend = nBytesRead - nBytesSent;
2852 if (nBytesToSend <= 0)
2854 /* Read data from file. */
2855 nBytesSent = 0;
2856 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
2857 ERR("Failed reading from file\n");
2859 if (nBytesRead > 0)
2860 nBytesToSend = nBytesRead;
2861 else
2862 break;
2865 nLen = DATA_PACKET_SIZE < nBytesToSend ?
2866 DATA_PACKET_SIZE : nBytesToSend;
2867 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
2869 if (nRC != -1)
2871 nBytesSent += nRC;
2872 nTotalSent += nRC;
2875 /* Do some computation to display the status. */
2876 time(&e_long_time);
2877 nSeconds = e_long_time - s_long_time;
2878 if( nSeconds / 60 > 0 )
2880 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
2881 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
2882 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
2884 else
2886 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
2887 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
2888 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
2890 } while (nRC != -1);
2892 TRACE("file transfer complete!\n");
2894 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2896 return nTotalSent;
2900 /***********************************************************************
2901 * FTP_SendRetrieve (internal)
2903 * Send request to retrieve a file
2905 * RETURNS
2906 * Number of bytes to be received on success
2907 * 0 on failure
2910 static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2912 INT nResCode;
2913 BOOL ret;
2915 TRACE("\n");
2916 if (!(ret = FTP_InitListenSocket(lpwfs)))
2917 goto lend;
2919 if (!(ret = FTP_SendType(lpwfs, dwType)))
2920 goto lend;
2922 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
2923 goto lend;
2925 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
2926 goto lend;
2928 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2929 if ((nResCode != 125) && (nResCode != 150)) {
2930 /* That means that we got an error getting the file. */
2931 FTP_SetResponseError(nResCode);
2932 ret = FALSE;
2935 lend:
2936 if (!ret && lpwfs->lstnSocket != -1)
2938 closesocket(lpwfs->lstnSocket);
2939 lpwfs->lstnSocket = -1;
2942 return ret;
2946 /***********************************************************************
2947 * FTP_RetrieveData (internal)
2949 * Retrieve data from server
2951 * RETURNS
2952 * TRUE on success
2953 * FALSE on failure
2956 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
2958 DWORD nBytesWritten;
2959 DWORD nBytesReceived = 0;
2960 INT nRC = 0;
2961 CHAR *lpszBuffer;
2963 TRACE("\n");
2965 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2966 if (NULL == lpszBuffer)
2968 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2969 return FALSE;
2972 while (nRC != -1)
2974 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
2975 if (nRC != -1)
2977 /* other side closed socket. */
2978 if (nRC == 0)
2979 goto recv_end;
2980 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
2981 nBytesReceived += nRC;
2985 TRACE("Data transfer complete\n");
2987 recv_end:
2988 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2990 return (nRC != -1);
2993 /***********************************************************************
2994 * FTP_CloseConnection (internal)
2996 * Close connections
2998 static void FTP_CloseConnection(LPWININETHANDLEHEADER hdr)
3000 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
3002 TRACE("\n");
3004 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
3005 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
3007 if (lpwfs->download_in_progress != NULL)
3008 lpwfs->download_in_progress->session_deleted = TRUE;
3010 if (lpwfs->sndSocket != -1)
3011 closesocket(lpwfs->sndSocket);
3013 if (lpwfs->lstnSocket != -1)
3014 closesocket(lpwfs->lstnSocket);
3016 if (lpwfs->pasvSocket != -1)
3017 closesocket(lpwfs->pasvSocket);
3019 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
3020 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
3024 /***********************************************************************
3025 * FTP_CloseSessionHandle (internal)
3027 * Deallocate session handle
3029 static void FTP_CloseSessionHandle(LPWININETHANDLEHEADER hdr)
3031 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
3033 TRACE("\n");
3035 WININET_Release(&lpwfs->lpAppInfo->hdr);
3037 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
3038 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
3039 HeapFree(GetProcessHeap(), 0, lpwfs);
3043 /***********************************************************************
3044 * FTP_FindNextFileW (Internal)
3046 * Continues a file search from a previous call to FindFirstFile
3048 * RETURNS
3049 * TRUE on success
3050 * FALSE on failure
3053 BOOL WINAPI FTP_FindNextFileW(LPWININETFTPFINDNEXTW lpwh, LPVOID lpvFindData)
3055 BOOL bSuccess = TRUE;
3056 LPWIN32_FIND_DATAW lpFindFileData;
3058 TRACE("index(%d) size(%d)\n", lpwh->index, lpwh->size);
3060 assert (lpwh->hdr.htype == WH_HFTPFINDNEXT);
3062 /* Clear any error information */
3063 INTERNET_SetLastError(0);
3065 lpFindFileData = (LPWIN32_FIND_DATAW) lpvFindData;
3066 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
3068 if (lpwh->index >= lpwh->size)
3070 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3071 bSuccess = FALSE;
3072 goto lend;
3075 FTP_ConvertFileProp(&lpwh->lpafp[lpwh->index], lpFindFileData);
3076 lpwh->index++;
3078 TRACE("\nName: %s\nSize: %d\n", debugstr_w(lpFindFileData->cFileName), lpFindFileData->nFileSizeLow);
3080 lend:
3082 if (lpwh->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3084 INTERNET_ASYNC_RESULT iar;
3086 iar.dwResult = (DWORD)bSuccess;
3087 iar.dwError = iar.dwError = bSuccess ? ERROR_SUCCESS :
3088 INTERNET_GetLastError();
3090 INTERNET_SendCallback(&lpwh->hdr, lpwh->hdr.dwContext,
3091 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3092 sizeof(INTERNET_ASYNC_RESULT));
3095 return bSuccess;
3099 /***********************************************************************
3100 * FTP_CloseFindNextHandle (internal)
3102 * Deallocate session handle
3104 * RETURNS
3105 * TRUE on success
3106 * FALSE on failure
3109 static void FTP_CloseFindNextHandle(LPWININETHANDLEHEADER hdr)
3111 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3112 DWORD i;
3114 TRACE("\n");
3116 WININET_Release(&lpwfn->lpFtpSession->hdr);
3118 for (i = 0; i < lpwfn->size; i++)
3120 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3123 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3124 HeapFree(GetProcessHeap(), 0, lpwfn);
3127 /***********************************************************************
3128 * FTP_CloseFileTransferHandle (internal)
3130 * Closes the file transfer handle. This also 'cleans' the data queue of
3131 * the 'transfer complete' message (this is a bit of a hack though :-/ )
3134 static void FTP_CloseFileTransferHandle(LPWININETHANDLEHEADER hdr)
3136 LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
3137 LPWININETFTPSESSIONW lpwfs = lpwh->lpFtpSession;
3138 INT nResCode;
3140 TRACE("\n");
3142 WININET_Release(&lpwh->lpFtpSession->hdr);
3144 if (!lpwh->session_deleted)
3145 lpwfs->download_in_progress = NULL;
3147 if (lpwh->nDataSocket != -1)
3148 closesocket(lpwh->nDataSocket);
3150 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3151 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
3153 HeapFree(GetProcessHeap(), 0, lpwh);
3156 /***********************************************************************
3157 * FTP_ReceiveFileList (internal)
3159 * Read file list from server
3161 * RETURNS
3162 * Handle to file list on success
3163 * NULL on failure
3166 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3167 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3169 DWORD dwSize = 0;
3170 LPFILEPROPERTIESW lpafp = NULL;
3171 LPWININETFTPFINDNEXTW lpwfn = NULL;
3172 HINTERNET handle = 0;
3174 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3176 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3178 if(lpFindFileData)
3179 FTP_ConvertFileProp(lpafp, lpFindFileData);
3181 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3182 if (lpwfn)
3184 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3185 lpwfn->hdr.dwContext = dwContext;
3186 lpwfn->hdr.dwRefCount = 1;
3187 lpwfn->hdr.close_connection = NULL;
3188 lpwfn->hdr.destroy = FTP_CloseFindNextHandle;
3189 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3190 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3191 lpwfn->size = dwSize;
3192 lpwfn->lpafp = lpafp;
3194 WININET_AddRef( &lpwfs->hdr );
3195 lpwfn->lpFtpSession = lpwfs;
3196 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3198 handle = WININET_AllocHandle( &lpwfn->hdr );
3202 if( lpwfn )
3203 WININET_Release( &lpwfn->hdr );
3205 TRACE("Matched %d files\n", dwSize);
3206 return handle;
3210 /***********************************************************************
3211 * FTP_ConvertFileProp (internal)
3213 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3215 * RETURNS
3216 * TRUE on success
3217 * FALSE on failure
3220 BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3222 BOOL bSuccess = FALSE;
3224 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3226 if (lpafp)
3228 /* Convert 'Unix' time to Windows time */
3229 RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
3230 (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
3231 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3232 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3234 /* Not all fields are filled in */
3235 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3236 lpFindFileData->nFileSizeLow = lpafp->nSize;
3238 if (lpafp->bIsDirectory)
3239 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3241 if (lpafp->lpszName)
3242 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3244 bSuccess = TRUE;
3247 return bSuccess;
3250 /***********************************************************************
3251 * FTP_ParseNextFile (internal)
3253 * Parse the next line in file listing
3255 * RETURNS
3256 * TRUE on success
3257 * FALSE on failure
3259 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3261 static const char szSpace[] = " \t";
3262 DWORD nBufLen;
3263 char *pszLine;
3264 char *pszToken;
3265 char *pszTmp;
3266 BOOL found = FALSE;
3267 int i;
3269 lpfp->lpszName = NULL;
3270 do {
3271 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3272 return FALSE;
3274 pszToken = strtok(pszLine, szSpace);
3275 /* ls format
3276 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3278 * For instance:
3279 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3281 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3282 if(!FTP_ParsePermission(pszToken, lpfp))
3283 lpfp->bIsDirectory = FALSE;
3284 for(i=0; i<=3; i++) {
3285 if(!(pszToken = strtok(NULL, szSpace)))
3286 break;
3288 if(!pszToken) continue;
3289 if(lpfp->bIsDirectory) {
3290 TRACE("Is directory\n");
3291 lpfp->nSize = 0;
3293 else {
3294 TRACE("Size: %s\n", pszToken);
3295 lpfp->nSize = atol(pszToken);
3298 lpfp->tmLastModified.tm_sec = 0;
3299 lpfp->tmLastModified.tm_min = 0;
3300 lpfp->tmLastModified.tm_hour = 0;
3301 lpfp->tmLastModified.tm_mday = 0;
3302 lpfp->tmLastModified.tm_mon = 0;
3303 lpfp->tmLastModified.tm_year = 0;
3305 /* Determine month */
3306 pszToken = strtok(NULL, szSpace);
3307 if(!pszToken) continue;
3308 if(strlen(pszToken) >= 3) {
3309 pszToken[3] = 0;
3310 if((pszTmp = StrStrIA(szMonths, pszToken)))
3311 lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
3313 /* Determine day */
3314 pszToken = strtok(NULL, szSpace);
3315 if(!pszToken) continue;
3316 lpfp->tmLastModified.tm_mday = atoi(pszToken);
3317 /* Determine time or year */
3318 pszToken = strtok(NULL, szSpace);
3319 if(!pszToken) continue;
3320 if((pszTmp = strchr(pszToken, ':'))) {
3321 struct tm* apTM;
3322 time_t aTime;
3323 *pszTmp = 0;
3324 pszTmp++;
3325 lpfp->tmLastModified.tm_min = atoi(pszTmp);
3326 lpfp->tmLastModified.tm_hour = atoi(pszToken);
3327 time(&aTime);
3328 apTM = localtime(&aTime);
3329 lpfp->tmLastModified.tm_year = apTM->tm_year;
3331 else {
3332 lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
3333 lpfp->tmLastModified.tm_hour = 12;
3335 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3336 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3337 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3338 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3340 pszToken = strtok(NULL, szSpace);
3341 if(!pszToken) continue;
3342 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3343 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3345 /* NT way of parsing ... :
3347 07-13-03 08:55PM <DIR> sakpatch
3348 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3350 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3351 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3353 sscanf(pszToken, "%d-%d-%d",
3354 &lpfp->tmLastModified.tm_mon,
3355 &lpfp->tmLastModified.tm_mday,
3356 &lpfp->tmLastModified.tm_year);
3358 /* Hacky and bad Y2K protection :-) */
3359 if (lpfp->tmLastModified.tm_year < 70)
3360 lpfp->tmLastModified.tm_year += 100;
3362 pszToken = strtok(NULL, szSpace);
3363 if(!pszToken) continue;
3364 sscanf(pszToken, "%d:%d",
3365 &lpfp->tmLastModified.tm_hour,
3366 &lpfp->tmLastModified.tm_min);
3367 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3368 lpfp->tmLastModified.tm_hour += 12;
3370 lpfp->tmLastModified.tm_sec = 0;
3372 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3373 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3374 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3375 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3377 pszToken = strtok(NULL, szSpace);
3378 if(!pszToken) continue;
3379 if(!strcasecmp(pszToken, "<DIR>")) {
3380 lpfp->bIsDirectory = TRUE;
3381 lpfp->nSize = 0;
3382 TRACE("Is directory\n");
3384 else {
3385 lpfp->bIsDirectory = FALSE;
3386 lpfp->nSize = atol(pszToken);
3387 TRACE("Size: %d\n", lpfp->nSize);
3390 pszToken = strtok(NULL, szSpace);
3391 if(!pszToken) continue;
3392 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3393 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3395 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3396 else if(pszToken[0] == '+') {
3397 FIXME("EPLF Format not implemented\n");
3400 if(lpfp->lpszName) {
3401 if((lpszSearchFile == NULL) ||
3402 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3403 found = TRUE;
3404 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3406 else {
3407 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3408 lpfp->lpszName = NULL;
3411 } while(!found);
3412 return TRUE;
3415 /***********************************************************************
3416 * FTP_ParseDirectory (internal)
3418 * Parse string of directory information
3420 * RETURNS
3421 * TRUE on success
3422 * FALSE on failure
3424 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3425 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3427 BOOL bSuccess = TRUE;
3428 INT sizeFilePropArray = 500;/*20; */
3429 INT indexFilePropArray = -1;
3431 TRACE("\n");
3433 /* Allocate intial file properties array */
3434 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3435 if (!*lpafp)
3436 return FALSE;
3438 do {
3439 if (indexFilePropArray+1 >= sizeFilePropArray)
3441 LPFILEPROPERTIESW tmpafp;
3443 sizeFilePropArray *= 2;
3444 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3445 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3446 if (NULL == tmpafp)
3448 bSuccess = FALSE;
3449 break;
3452 *lpafp = tmpafp;
3454 indexFilePropArray++;
3455 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3457 if (bSuccess && indexFilePropArray)
3459 if (indexFilePropArray < sizeFilePropArray - 1)
3461 LPFILEPROPERTIESW tmpafp;
3463 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3464 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3465 if (NULL != tmpafp)
3466 *lpafp = tmpafp;
3468 *dwfp = indexFilePropArray;
3470 else
3472 HeapFree(GetProcessHeap(), 0, *lpafp);
3473 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3474 bSuccess = FALSE;
3477 return bSuccess;
3481 /***********************************************************************
3482 * FTP_ParsePermission (internal)
3484 * Parse permission string of directory information
3486 * RETURNS
3487 * TRUE on success
3488 * FALSE on failure
3491 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3493 BOOL bSuccess = TRUE;
3494 unsigned short nPermission = 0;
3495 INT nPos = 1;
3496 INT nLast = 9;
3498 TRACE("\n");
3499 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3501 bSuccess = FALSE;
3502 return bSuccess;
3505 lpfp->bIsDirectory = (*lpszPermission == 'd');
3508 switch (nPos)
3510 case 1:
3511 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3512 break;
3513 case 2:
3514 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3515 break;
3516 case 3:
3517 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3518 break;
3519 case 4:
3520 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3521 break;
3522 case 5:
3523 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3524 break;
3525 case 6:
3526 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3527 break;
3528 case 7:
3529 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3530 break;
3531 case 8:
3532 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3533 break;
3534 case 9:
3535 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3536 break;
3538 nPos++;
3539 }while (nPos <= nLast);
3541 lpfp->permissions = nPermission;
3542 return bSuccess;
3546 /***********************************************************************
3547 * FTP_SetResponseError (internal)
3549 * Set the appropriate error code for a given response from the server
3551 * RETURNS
3554 static DWORD FTP_SetResponseError(DWORD dwResponse)
3556 DWORD dwCode = 0;
3558 switch(dwResponse)
3560 case 425: /* Cannot open data connection. */
3561 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3562 break;
3564 case 426: /* Connection closed, transer aborted. */
3565 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3566 break;
3568 case 530: /* Not logged in. Login incorrect. */
3569 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3570 break;
3572 case 421: /* Service not available - Server may be shutting down. */
3573 case 450: /* File action not taken. File may be busy. */
3574 case 451: /* Action aborted. Server error. */
3575 case 452: /* Action not taken. Insufficient storage space on server. */
3576 case 500: /* Syntax error. Command unrecognized. */
3577 case 501: /* Syntax error. Error in parameters or arguments. */
3578 case 502: /* Command not implemented. */
3579 case 503: /* Bad sequence of commands. */
3580 case 504: /* Command not implemented for that parameter. */
3581 case 532: /* Need account for storing files */
3582 case 550: /* File action not taken. File not found or no access. */
3583 case 551: /* Requested action aborted. Page type unknown */
3584 case 552: /* Action aborted. Exceeded storage allocation */
3585 case 553: /* Action not taken. File name not allowed. */
3587 default:
3588 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3589 break;
3592 INTERNET_SetLastError(dwCode);
3593 return dwCode;