secur32: Implement NTLM2 encryption.
[wine/gsoc_dplay.git] / dlls / wininet / ftp.c
blob961a915c5e7432fdf9d4a40b3eba4526a2d47b9c
1 /*
2 * WININET - Ftp implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2004 Mike McCormack for CodeWeavers
6 * Copyright 2004 Kevin Koltzau
8 * Ulrich Czekalla
9 * Noureddine Jemmali
11 * Copyright 2000 Andreas Mohr
12 * Copyright 2002 Jaco Greeff
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
32 #include <errno.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #ifdef HAVE_SYS_SOCKET_H
39 # include <sys/socket.h>
40 #endif
41 #ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
44 #include <time.h>
45 #include <assert.h>
47 #include "windef.h"
48 #include "winbase.h"
49 #include "wingdi.h"
50 #include "winuser.h"
51 #include "wininet.h"
52 #include "winnls.h"
53 #include "winerror.h"
54 #include "winreg.h"
55 #include "winternl.h"
56 #include "shlwapi.h"
58 #include "wine/debug.h"
59 #include "internet.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
63 #define DATA_PACKET_SIZE 0x2000
64 #define szCRLF "\r\n"
65 #define MAX_BACKLOG 5
67 typedef enum {
68 /* FTP commands with arguments. */
69 FTP_CMD_ACCT,
70 FTP_CMD_CWD,
71 FTP_CMD_DELE,
72 FTP_CMD_MKD,
73 FTP_CMD_PASS,
74 FTP_CMD_PORT,
75 FTP_CMD_RETR,
76 FTP_CMD_RMD,
77 FTP_CMD_RNFR,
78 FTP_CMD_RNTO,
79 FTP_CMD_STOR,
80 FTP_CMD_TYPE,
81 FTP_CMD_USER,
82 FTP_CMD_SIZE,
84 /* FTP commands without arguments. */
85 FTP_CMD_ABOR,
86 FTP_CMD_LIST,
87 FTP_CMD_NLST,
88 FTP_CMD_PASV,
89 FTP_CMD_PWD,
90 FTP_CMD_QUIT,
91 } FTP_COMMAND;
93 static const CHAR *szFtpCommands[] = {
94 "ACCT",
95 "CWD",
96 "DELE",
97 "MKD",
98 "PASS",
99 "PORT",
100 "RETR",
101 "RMD",
102 "RNFR",
103 "RNTO",
104 "STOR",
105 "TYPE",
106 "USER",
107 "SIZE",
108 "ABOR",
109 "LIST",
110 "NLST",
111 "PASV",
112 "PWD",
113 "QUIT",
116 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
117 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
119 static void FTP_CloseFileTransferHandle(LPWININETHANDLEHEADER hdr);
120 static void FTP_CloseSessionHandle(LPWININETHANDLEHEADER hdr);
121 static void FTP_CloseFindNextHandle(LPWININETHANDLEHEADER hdr);
122 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
123 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext);
124 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
125 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket);
126 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile);
127 static INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD dwContext);
128 static DWORD FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
129 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile);
130 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs);
131 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs);
132 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs);
133 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs);
134 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType);
135 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize);
136 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs);
137 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs);
138 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs);
139 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
140 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
141 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
142 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
143 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
144 LPWIN32_FIND_DATAW lpFindFileData, DWORD dwContext);
145 static DWORD FTP_SetResponseError(DWORD dwResponse);
147 /***********************************************************************
148 * FtpPutFileA (WININET.@)
150 * Uploads a file to the FTP server
152 * RETURNS
153 * TRUE on success
154 * FALSE on failure
157 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
158 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
160 LPWSTR lpwzLocalFile;
161 LPWSTR lpwzNewRemoteFile;
162 BOOL ret;
164 lpwzLocalFile = lpszLocalFile?WININET_strdup_AtoW(lpszLocalFile):NULL;
165 lpwzNewRemoteFile = lpszNewRemoteFile?WININET_strdup_AtoW(lpszNewRemoteFile):NULL;
166 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
167 dwFlags, dwContext);
168 HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
169 HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
170 return ret;
173 /***********************************************************************
174 * FtpPutFileW (WININET.@)
176 * Uploads a file to the FTP server
178 * RETURNS
179 * TRUE on success
180 * FALSE on failure
183 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
184 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
186 LPWININETFTPSESSIONW lpwfs;
187 LPWININETAPPINFOW hIC = NULL;
188 BOOL r = FALSE;
190 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
191 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
193 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
194 goto lend;
197 hIC = lpwfs->lpAppInfo;
198 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
200 WORKREQUEST workRequest;
201 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
203 workRequest.asyncall = FTPPUTFILEW;
204 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
205 req->lpszLocalFile = WININET_strdupW(lpszLocalFile);
206 req->lpszNewRemoteFile = WININET_strdupW(lpszNewRemoteFile);
207 req->dwFlags = dwFlags;
208 req->dwContext = dwContext;
210 r = INTERNET_AsyncCall(&workRequest);
212 else
214 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
215 lpszNewRemoteFile, dwFlags, dwContext);
218 lend:
219 if( lpwfs )
220 WININET_Release( &lpwfs->hdr );
222 return r;
225 /***********************************************************************
226 * FTP_FtpPutFileW (Internal)
228 * Uploads a file to the FTP server
230 * RETURNS
231 * TRUE on success
232 * FALSE on failure
235 BOOL WINAPI FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
236 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
238 HANDLE hFile = NULL;
239 BOOL bSuccess = FALSE;
240 LPWININETAPPINFOW hIC = NULL;
241 INT nResCode;
243 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
245 if (!lpszLocalFile || !lpszNewRemoteFile)
247 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
248 return FALSE;
251 assert( WH_HFTPSESSION == lpwfs->hdr.htype);
253 /* Clear any error information */
254 INTERNET_SetLastError(0);
255 hIC = lpwfs->lpAppInfo;
257 /* Open file to be uploaded */
258 if (INVALID_HANDLE_VALUE ==
259 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
261 INTERNET_SetLastError(ERROR_FILE_NOT_FOUND);
262 goto lend;
265 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
267 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
269 INT nDataSocket;
271 /* Get data socket to server */
272 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
274 FTP_SendData(lpwfs, nDataSocket, hFile);
275 closesocket(nDataSocket);
276 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
277 if (nResCode)
279 if (nResCode == 226)
280 bSuccess = TRUE;
281 else
282 FTP_SetResponseError(nResCode);
287 lend:
288 if (lpwfs->lstnSocket != -1)
289 closesocket(lpwfs->lstnSocket);
291 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
293 INTERNET_ASYNC_RESULT iar;
295 iar.dwResult = (DWORD)bSuccess;
296 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
297 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
298 &iar, sizeof(INTERNET_ASYNC_RESULT));
301 if (hFile)
302 CloseHandle(hFile);
304 return bSuccess;
308 /***********************************************************************
309 * FtpSetCurrentDirectoryA (WININET.@)
311 * Change the working directory on the FTP server
313 * RETURNS
314 * TRUE on success
315 * FALSE on failure
318 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
320 LPWSTR lpwzDirectory;
321 BOOL ret;
323 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
324 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
325 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
326 return ret;
330 /***********************************************************************
331 * FtpSetCurrentDirectoryW (WININET.@)
333 * Change the working directory on the FTP server
335 * RETURNS
336 * TRUE on success
337 * FALSE on failure
340 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
342 LPWININETFTPSESSIONW lpwfs = NULL;
343 LPWININETAPPINFOW hIC = NULL;
344 BOOL r = FALSE;
346 if (!lpszDirectory)
348 SetLastError(ERROR_INVALID_PARAMETER);
349 goto lend;
352 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
353 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
355 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
356 goto lend;
359 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
361 hIC = lpwfs->lpAppInfo;
362 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
364 WORKREQUEST workRequest;
365 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
367 workRequest.asyncall = FTPSETCURRENTDIRECTORYW;
368 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
369 req = &workRequest.u.FtpSetCurrentDirectoryW;
370 req->lpszDirectory = WININET_strdupW(lpszDirectory);
372 r = INTERNET_AsyncCall(&workRequest);
374 else
376 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
379 lend:
380 if( lpwfs )
381 WININET_Release( &lpwfs->hdr );
383 return r;
387 /***********************************************************************
388 * FTP_FtpSetCurrentDirectoryW (Internal)
390 * Change the working directory on the FTP server
392 * RETURNS
393 * TRUE on success
394 * FALSE on failure
397 BOOL WINAPI FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
399 INT nResCode;
400 LPWININETAPPINFOW hIC = NULL;
401 DWORD bSuccess = FALSE;
403 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
405 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
407 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
408 return FALSE;
411 /* Clear any error information */
412 INTERNET_SetLastError(0);
414 hIC = lpwfs->lpAppInfo;
415 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
416 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
417 goto lend;
419 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
421 if (nResCode)
423 if (nResCode == 250)
424 bSuccess = TRUE;
425 else
426 FTP_SetResponseError(nResCode);
429 lend:
430 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
432 INTERNET_ASYNC_RESULT iar;
434 iar.dwResult = (DWORD)bSuccess;
435 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
436 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
437 &iar, sizeof(INTERNET_ASYNC_RESULT));
439 return bSuccess;
443 /***********************************************************************
444 * FtpCreateDirectoryA (WININET.@)
446 * Create new directory on the FTP server
448 * RETURNS
449 * TRUE on success
450 * FALSE on failure
453 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
455 LPWSTR lpwzDirectory;
456 BOOL ret;
458 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
459 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
460 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
461 return ret;
465 /***********************************************************************
466 * FtpCreateDirectoryW (WININET.@)
468 * Create new directory on the FTP server
470 * RETURNS
471 * TRUE on success
472 * FALSE on failure
475 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
477 LPWININETFTPSESSIONW lpwfs;
478 LPWININETAPPINFOW hIC = NULL;
479 BOOL r = FALSE;
481 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
482 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
484 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
485 goto lend;
488 hIC = lpwfs->lpAppInfo;
489 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
491 WORKREQUEST workRequest;
492 struct WORKREQ_FTPCREATEDIRECTORYW *req;
494 workRequest.asyncall = FTPCREATEDIRECTORYW;
495 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
496 req = &workRequest.u.FtpCreateDirectoryW;
497 req->lpszDirectory = WININET_strdupW(lpszDirectory);
499 r = INTERNET_AsyncCall(&workRequest);
501 else
503 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
505 lend:
506 if( lpwfs )
507 WININET_Release( &lpwfs->hdr );
509 return r;
513 /***********************************************************************
514 * FTP_FtpCreateDirectoryW (Internal)
516 * Create new directory on the FTP server
518 * RETURNS
519 * TRUE on success
520 * FALSE on failure
523 BOOL WINAPI FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
525 INT nResCode;
526 BOOL bSuccess = FALSE;
527 LPWININETAPPINFOW hIC = NULL;
529 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
531 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
533 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
534 return FALSE;
537 /* Clear any error information */
538 INTERNET_SetLastError(0);
540 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
541 goto lend;
543 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
544 if (nResCode)
546 if (nResCode == 257)
547 bSuccess = TRUE;
548 else
549 FTP_SetResponseError(nResCode);
552 lend:
553 hIC = lpwfs->lpAppInfo;
554 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
556 INTERNET_ASYNC_RESULT iar;
558 iar.dwResult = (DWORD)bSuccess;
559 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
560 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
561 &iar, sizeof(INTERNET_ASYNC_RESULT));
564 return bSuccess;
567 /***********************************************************************
568 * FtpFindFirstFileA (WININET.@)
570 * Search the specified directory
572 * RETURNS
573 * HINTERNET on success
574 * NULL on failure
577 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
578 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD dwContext)
580 LPWSTR lpwzSearchFile;
581 WIN32_FIND_DATAW wfd;
582 LPWIN32_FIND_DATAW lpFindFileDataW;
583 HINTERNET ret;
585 lpwzSearchFile = lpszSearchFile?WININET_strdup_AtoW(lpszSearchFile):NULL;
586 lpFindFileDataW = lpFindFileData?&wfd:NULL;
587 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
588 HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
590 if(lpFindFileData) {
591 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
593 return ret;
597 /***********************************************************************
598 * FtpFindFirstFileW (WININET.@)
600 * Search the specified directory
602 * RETURNS
603 * HINTERNET on success
604 * NULL on failure
607 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
608 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD dwContext)
610 LPWININETFTPSESSIONW lpwfs;
611 LPWININETAPPINFOW hIC = NULL;
612 HINTERNET r = NULL;
614 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
615 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
617 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
618 goto lend;
621 hIC = lpwfs->lpAppInfo;
622 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
624 WORKREQUEST workRequest;
625 struct WORKREQ_FTPFINDFIRSTFILEW *req;
627 workRequest.asyncall = FTPFINDFIRSTFILEW;
628 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
629 req = &workRequest.u.FtpFindFirstFileW;
630 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : WININET_strdupW(lpszSearchFile);
631 req->lpFindFileData = lpFindFileData;
632 req->dwFlags = dwFlags;
633 req->dwContext= dwContext;
635 INTERNET_AsyncCall(&workRequest);
636 r = NULL;
638 else
640 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
641 dwFlags, dwContext);
643 lend:
644 if( lpwfs )
645 WININET_Release( &lpwfs->hdr );
647 return r;
651 /***********************************************************************
652 * FTP_FtpFindFirstFileW (Internal)
654 * Search the specified directory
656 * RETURNS
657 * HINTERNET on success
658 * NULL on failure
661 HINTERNET WINAPI FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
662 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD dwContext)
664 INT nResCode;
665 LPWININETAPPINFOW hIC = NULL;
666 HINTERNET hFindNext = NULL;
668 TRACE("\n");
670 assert(WH_HFTPSESSION == lpwfs->hdr.htype);
672 /* Clear any error information */
673 INTERNET_SetLastError(0);
675 if (!FTP_InitListenSocket(lpwfs))
676 goto lend;
678 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
679 goto lend;
681 if (!FTP_SendPortOrPasv(lpwfs))
682 goto lend;
684 hIC = lpwfs->lpAppInfo;
685 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
686 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
687 goto lend;
689 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
690 if (nResCode)
692 if (nResCode == 125 || nResCode == 150)
694 INT nDataSocket;
696 /* Get data socket to server */
697 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
699 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
700 closesocket(nDataSocket);
701 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
702 if (nResCode != 226 && nResCode != 250)
703 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
706 else
707 FTP_SetResponseError(nResCode);
710 lend:
711 if (lpwfs->lstnSocket != -1)
712 closesocket(lpwfs->lstnSocket);
714 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
716 INTERNET_ASYNC_RESULT iar;
718 if (hFindNext)
720 iar.dwResult = (DWORD)hFindNext;
721 iar.dwError = ERROR_SUCCESS;
722 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
723 &iar, sizeof(INTERNET_ASYNC_RESULT));
726 iar.dwResult = (DWORD)hFindNext;
727 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
728 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
729 &iar, sizeof(INTERNET_ASYNC_RESULT));
732 return hFindNext;
736 /***********************************************************************
737 * FtpGetCurrentDirectoryA (WININET.@)
739 * Retrieves the current directory
741 * RETURNS
742 * TRUE on success
743 * FALSE on failure
746 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
747 LPDWORD lpdwCurrentDirectory)
749 WCHAR *dir = NULL;
750 DWORD len;
751 BOOL ret;
753 if(lpdwCurrentDirectory) {
754 len = *lpdwCurrentDirectory;
755 if(lpszCurrentDirectory)
757 dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
758 if (NULL == dir)
760 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
761 return FALSE;
765 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
766 if(lpdwCurrentDirectory) {
767 *lpdwCurrentDirectory = len;
768 if(lpszCurrentDirectory) {
769 WideCharToMultiByte(CP_ACP, 0, dir, len, lpszCurrentDirectory, *lpdwCurrentDirectory, NULL, NULL);
770 HeapFree(GetProcessHeap(), 0, dir);
773 return ret;
777 /***********************************************************************
778 * FtpGetCurrentDirectoryW (WININET.@)
780 * Retrieves the current directory
782 * RETURNS
783 * TRUE on success
784 * FALSE on failure
787 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
788 LPDWORD lpdwCurrentDirectory)
790 LPWININETFTPSESSIONW lpwfs;
791 LPWININETAPPINFOW hIC = NULL;
792 BOOL r = FALSE;
794 TRACE("len(%d)\n", *lpdwCurrentDirectory);
796 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
797 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
799 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
800 goto lend;
803 hIC = lpwfs->lpAppInfo;
804 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
806 WORKREQUEST workRequest;
807 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
809 workRequest.asyncall = FTPGETCURRENTDIRECTORYW;
810 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
811 req = &workRequest.u.FtpGetCurrentDirectoryW;
812 req->lpszDirectory = lpszCurrentDirectory;
813 req->lpdwDirectory = lpdwCurrentDirectory;
815 r = INTERNET_AsyncCall(&workRequest);
817 else
819 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
820 lpdwCurrentDirectory);
823 lend:
824 if( lpwfs )
825 WININET_Release( &lpwfs->hdr );
827 return r;
831 /***********************************************************************
832 * FTP_FtpGetCurrentDirectoryA (Internal)
834 * Retrieves the current directory
836 * RETURNS
837 * TRUE on success
838 * FALSE on failure
841 BOOL WINAPI FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
842 LPDWORD lpdwCurrentDirectory)
844 INT nResCode;
845 LPWININETAPPINFOW hIC = NULL;
846 DWORD bSuccess = FALSE;
848 TRACE("len(%d)\n", *lpdwCurrentDirectory);
850 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
852 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
853 return FALSE;
856 /* Clear any error information */
857 INTERNET_SetLastError(0);
859 ZeroMemory(lpszCurrentDirectory, *lpdwCurrentDirectory);
861 hIC = lpwfs->lpAppInfo;
862 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
863 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
864 goto lend;
866 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
867 if (nResCode)
869 if (nResCode == 257) /* Extract directory name */
871 DWORD firstpos, lastpos, len;
872 LPWSTR lpszResponseBuffer = WININET_strdup_AtoW(INTERNET_GetResponseBuffer());
874 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
876 if ('"' == lpszResponseBuffer[lastpos])
878 if (!firstpos)
879 firstpos = lastpos;
880 else
881 break;
885 len = lastpos - firstpos - 1;
886 lstrcpynW(lpszCurrentDirectory, &lpszResponseBuffer[firstpos+1], *lpdwCurrentDirectory);
887 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
888 *lpdwCurrentDirectory = len;
889 bSuccess = TRUE;
891 else
892 FTP_SetResponseError(nResCode);
895 lend:
896 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
898 INTERNET_ASYNC_RESULT iar;
900 iar.dwResult = (DWORD)bSuccess;
901 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
902 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
903 &iar, sizeof(INTERNET_ASYNC_RESULT));
906 return (DWORD) bSuccess;
909 /***********************************************************************
910 * FtpOpenFileA (WININET.@)
912 * Open a remote file for writing or reading
914 * RETURNS
915 * HINTERNET handle on success
916 * NULL on failure
919 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
920 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
921 DWORD dwContext)
923 LPWSTR lpwzFileName;
924 HINTERNET ret;
926 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
927 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
928 HeapFree(GetProcessHeap(), 0, lpwzFileName);
929 return ret;
933 /***********************************************************************
934 * FtpOpenFileW (WININET.@)
936 * Open a remote file for writing or reading
938 * RETURNS
939 * HINTERNET handle on success
940 * NULL on failure
943 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
944 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
945 DWORD dwContext)
947 LPWININETFTPSESSIONW lpwfs;
948 LPWININETAPPINFOW hIC = NULL;
949 HINTERNET r = NULL;
951 TRACE("(%p,%s,0x%08x,0x%08x,0x%08x)\n", hFtpSession,
952 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
954 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
955 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
957 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
958 goto lend;
961 if (lpwfs->download_in_progress != NULL) {
962 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
963 goto lend;
965 hIC = lpwfs->lpAppInfo;
966 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
968 WORKREQUEST workRequest;
969 struct WORKREQ_FTPOPENFILEW *req;
971 workRequest.asyncall = FTPOPENFILEW;
972 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
973 req = &workRequest.u.FtpOpenFileW;
974 req->lpszFilename = WININET_strdupW(lpszFileName);
975 req->dwAccess = fdwAccess;
976 req->dwFlags = dwFlags;
977 req->dwContext = dwContext;
979 INTERNET_AsyncCall(&workRequest);
980 r = NULL;
982 else
984 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
987 lend:
988 if( lpwfs )
989 WININET_Release( &lpwfs->hdr );
991 return r;
995 /***********************************************************************
996 * FTP_FtpOpenFileW (Internal)
998 * Open a remote file for writing or reading
1000 * RETURNS
1001 * HINTERNET handle on success
1002 * NULL on failure
1005 HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
1006 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1007 DWORD dwContext)
1009 INT nDataSocket;
1010 BOOL bSuccess = FALSE;
1011 LPWININETFTPFILE lpwh = NULL;
1012 LPWININETAPPINFOW hIC = NULL;
1013 HINTERNET handle = NULL;
1015 TRACE("\n");
1017 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1019 /* Clear any error information */
1020 INTERNET_SetLastError(0);
1022 if (GENERIC_READ == fdwAccess)
1024 /* Set up socket to retrieve data */
1025 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1027 else if (GENERIC_WRITE == fdwAccess)
1029 /* Set up socket to send data */
1030 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1033 /* Get data socket to server */
1034 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1036 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPFILE));
1037 lpwh->hdr.htype = WH_HFILE;
1038 lpwh->hdr.dwFlags = dwFlags;
1039 lpwh->hdr.dwContext = dwContext;
1040 lpwh->hdr.dwRefCount = 1;
1041 lpwh->hdr.destroy = FTP_CloseFileTransferHandle;
1042 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1043 lpwh->nDataSocket = nDataSocket;
1044 lpwh->session_deleted = FALSE;
1046 WININET_AddRef( &lpwfs->hdr );
1047 lpwh->lpFtpSession = lpwfs;
1049 handle = WININET_AllocHandle( &lpwh->hdr );
1050 if( !handle )
1051 goto lend;
1053 /* Indicate that a download is currently in progress */
1054 lpwfs->download_in_progress = lpwh;
1057 if (lpwfs->lstnSocket != -1)
1058 closesocket(lpwfs->lstnSocket);
1060 hIC = lpwfs->lpAppInfo;
1061 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1063 INTERNET_ASYNC_RESULT iar;
1065 if (lpwh)
1067 iar.dwResult = (DWORD)handle;
1068 iar.dwError = ERROR_SUCCESS;
1069 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1070 &iar, sizeof(INTERNET_ASYNC_RESULT));
1073 iar.dwResult = (DWORD)bSuccess;
1074 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1075 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1076 &iar, sizeof(INTERNET_ASYNC_RESULT));
1079 lend:
1080 if( lpwh )
1081 WININET_Release( &lpwh->hdr );
1083 return handle;
1087 /***********************************************************************
1088 * FtpGetFileA (WININET.@)
1090 * Retrieve file from the FTP server
1092 * RETURNS
1093 * TRUE on success
1094 * FALSE on failure
1097 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1098 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1099 DWORD dwContext)
1101 LPWSTR lpwzRemoteFile;
1102 LPWSTR lpwzNewFile;
1103 BOOL ret;
1105 lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
1106 lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
1107 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1108 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1109 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1110 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1111 return ret;
1115 /***********************************************************************
1116 * FtpGetFileW (WININET.@)
1118 * Retrieve file from the FTP server
1120 * RETURNS
1121 * TRUE on success
1122 * FALSE on failure
1125 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1126 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1127 DWORD dwContext)
1129 LPWININETFTPSESSIONW lpwfs;
1130 LPWININETAPPINFOW hIC = NULL;
1131 BOOL r = FALSE;
1133 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
1134 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1136 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1137 goto lend;
1140 if (lpwfs->download_in_progress != NULL) {
1141 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1142 goto lend;
1145 hIC = lpwfs->lpAppInfo;
1146 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1148 WORKREQUEST workRequest;
1149 struct WORKREQ_FTPGETFILEW *req;
1151 workRequest.asyncall = FTPGETFILEW;
1152 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1153 req = &workRequest.u.FtpGetFileW;
1154 req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
1155 req->lpszNewFile = WININET_strdupW(lpszNewFile);
1156 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1157 req->fFailIfExists = fFailIfExists;
1158 req->dwFlags = dwInternetFlags;
1159 req->dwContext = dwContext;
1161 r = INTERNET_AsyncCall(&workRequest);
1163 else
1165 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1166 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1169 lend:
1170 if( lpwfs )
1171 WININET_Release( &lpwfs->hdr );
1173 return r;
1177 /***********************************************************************
1178 * FTP_FtpGetFileW (Internal)
1180 * Retrieve file from the FTP server
1182 * RETURNS
1183 * TRUE on success
1184 * FALSE on failure
1187 BOOL WINAPI FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1188 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1189 DWORD dwContext)
1191 DWORD nBytes;
1192 BOOL bSuccess = FALSE;
1193 HANDLE hFile;
1194 LPWININETAPPINFOW hIC = NULL;
1196 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1198 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1200 /* Clear any error information */
1201 INTERNET_SetLastError(0);
1203 /* Ensure we can write to lpszNewfile by opening it */
1204 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1205 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1206 if (INVALID_HANDLE_VALUE == hFile)
1207 goto lend;
1209 /* Set up socket to retrieve data */
1210 nBytes = FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags);
1212 if (nBytes > 0)
1214 INT nDataSocket;
1216 /* Get data socket to server */
1217 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1219 INT nResCode;
1221 /* Receive data */
1222 FTP_RetrieveFileData(lpwfs, nDataSocket, nBytes, hFile);
1223 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1224 if (nResCode)
1226 if (nResCode == 226)
1227 bSuccess = TRUE;
1228 else
1229 FTP_SetResponseError(nResCode);
1231 closesocket(nDataSocket);
1235 lend:
1236 if (lpwfs->lstnSocket != -1)
1237 closesocket(lpwfs->lstnSocket);
1239 if (hFile)
1240 CloseHandle(hFile);
1242 hIC = lpwfs->lpAppInfo;
1243 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1245 INTERNET_ASYNC_RESULT iar;
1247 iar.dwResult = (DWORD)bSuccess;
1248 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1249 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1250 &iar, sizeof(INTERNET_ASYNC_RESULT));
1253 return bSuccess;
1256 /***********************************************************************
1257 * FtpGetFileSize (WININET.@)
1259 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1261 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1263 if (lpdwFileSizeHigh)
1264 *lpdwFileSizeHigh = 0;
1266 return 0;
1269 /***********************************************************************
1270 * FtpDeleteFileA (WININET.@)
1272 * Delete a file on the ftp server
1274 * RETURNS
1275 * TRUE on success
1276 * FALSE on failure
1279 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1281 LPWSTR lpwzFileName;
1282 BOOL ret;
1284 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1285 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1286 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1287 return ret;
1290 /***********************************************************************
1291 * FtpDeleteFileW (WININET.@)
1293 * Delete a file on the ftp server
1295 * RETURNS
1296 * TRUE on success
1297 * FALSE on failure
1300 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1302 LPWININETFTPSESSIONW lpwfs;
1303 LPWININETAPPINFOW hIC = NULL;
1304 BOOL r = FALSE;
1306 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1307 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1309 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1310 goto lend;
1313 hIC = lpwfs->lpAppInfo;
1314 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1316 WORKREQUEST workRequest;
1317 struct WORKREQ_FTPDELETEFILEW *req;
1319 workRequest.asyncall = FTPDELETEFILEW;
1320 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1321 req = &workRequest.u.FtpDeleteFileW;
1322 req->lpszFilename = WININET_strdupW(lpszFileName);
1324 r = INTERNET_AsyncCall(&workRequest);
1326 else
1328 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1331 lend:
1332 if( lpwfs )
1333 WININET_Release( &lpwfs->hdr );
1335 return r;
1338 /***********************************************************************
1339 * FTP_FtpDeleteFileW (Internal)
1341 * Delete a file on the ftp server
1343 * RETURNS
1344 * TRUE on success
1345 * FALSE on failure
1348 BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName)
1350 INT nResCode;
1351 BOOL bSuccess = FALSE;
1352 LPWININETAPPINFOW hIC = NULL;
1354 TRACE("%p\n", lpwfs);
1356 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1358 /* Clear any error information */
1359 INTERNET_SetLastError(0);
1361 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1362 goto lend;
1364 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1365 if (nResCode)
1367 if (nResCode == 250)
1368 bSuccess = TRUE;
1369 else
1370 FTP_SetResponseError(nResCode);
1372 lend:
1373 hIC = lpwfs->lpAppInfo;
1374 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1376 INTERNET_ASYNC_RESULT iar;
1378 iar.dwResult = (DWORD)bSuccess;
1379 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1380 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1381 &iar, sizeof(INTERNET_ASYNC_RESULT));
1384 return bSuccess;
1388 /***********************************************************************
1389 * FtpRemoveDirectoryA (WININET.@)
1391 * Remove a directory on the ftp server
1393 * RETURNS
1394 * TRUE on success
1395 * FALSE on failure
1398 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1400 LPWSTR lpwzDirectory;
1401 BOOL ret;
1403 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
1404 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1405 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1406 return ret;
1409 /***********************************************************************
1410 * FtpRemoveDirectoryW (WININET.@)
1412 * Remove a directory on the ftp server
1414 * RETURNS
1415 * TRUE on success
1416 * FALSE on failure
1419 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1421 LPWININETFTPSESSIONW lpwfs;
1422 LPWININETAPPINFOW hIC = NULL;
1423 BOOL r = FALSE;
1425 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1426 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1428 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1429 goto lend;
1432 hIC = lpwfs->lpAppInfo;
1433 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1435 WORKREQUEST workRequest;
1436 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1438 workRequest.asyncall = FTPREMOVEDIRECTORYW;
1439 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1440 req = &workRequest.u.FtpRemoveDirectoryW;
1441 req->lpszDirectory = WININET_strdupW(lpszDirectory);
1443 r = INTERNET_AsyncCall(&workRequest);
1445 else
1447 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1450 lend:
1451 if( lpwfs )
1452 WININET_Release( &lpwfs->hdr );
1454 return r;
1457 /***********************************************************************
1458 * FTP_FtpRemoveDirectoryW (Internal)
1460 * Remove a directory on the ftp server
1462 * RETURNS
1463 * TRUE on success
1464 * FALSE on failure
1467 BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
1469 INT nResCode;
1470 BOOL bSuccess = FALSE;
1471 LPWININETAPPINFOW hIC = NULL;
1473 TRACE("\n");
1475 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1477 /* Clear any error information */
1478 INTERNET_SetLastError(0);
1480 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1481 goto lend;
1483 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1484 if (nResCode)
1486 if (nResCode == 250)
1487 bSuccess = TRUE;
1488 else
1489 FTP_SetResponseError(nResCode);
1492 lend:
1493 hIC = lpwfs->lpAppInfo;
1494 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1496 INTERNET_ASYNC_RESULT iar;
1498 iar.dwResult = (DWORD)bSuccess;
1499 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1500 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1501 &iar, sizeof(INTERNET_ASYNC_RESULT));
1504 return bSuccess;
1508 /***********************************************************************
1509 * FtpRenameFileA (WININET.@)
1511 * Rename a file on the ftp server
1513 * RETURNS
1514 * TRUE on success
1515 * FALSE on failure
1518 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1520 LPWSTR lpwzSrc;
1521 LPWSTR lpwzDest;
1522 BOOL ret;
1524 lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL;
1525 lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL;
1526 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1527 HeapFree(GetProcessHeap(), 0, lpwzSrc);
1528 HeapFree(GetProcessHeap(), 0, lpwzDest);
1529 return ret;
1532 /***********************************************************************
1533 * FtpRenameFileW (WININET.@)
1535 * Rename a file on the ftp server
1537 * RETURNS
1538 * TRUE on success
1539 * FALSE on failure
1542 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
1544 LPWININETFTPSESSIONW lpwfs;
1545 LPWININETAPPINFOW hIC = NULL;
1546 BOOL r = FALSE;
1548 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1549 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1551 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1552 goto lend;
1555 hIC = lpwfs->lpAppInfo;
1556 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1558 WORKREQUEST workRequest;
1559 struct WORKREQ_FTPRENAMEFILEW *req;
1561 workRequest.asyncall = FTPRENAMEFILEW;
1562 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1563 req = &workRequest.u.FtpRenameFileW;
1564 req->lpszSrcFile = WININET_strdupW(lpszSrc);
1565 req->lpszDestFile = WININET_strdupW(lpszDest);
1567 r = INTERNET_AsyncCall(&workRequest);
1569 else
1571 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
1574 lend:
1575 if( lpwfs )
1576 WININET_Release( &lpwfs->hdr );
1578 return r;
1581 /***********************************************************************
1582 * FTP_FtpRenameFileW (Internal)
1584 * Rename a file on the ftp server
1586 * RETURNS
1587 * TRUE on success
1588 * FALSE on failure
1591 BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs,
1592 LPCWSTR lpszSrc, LPCWSTR lpszDest)
1594 INT nResCode;
1595 BOOL bSuccess = FALSE;
1596 LPWININETAPPINFOW hIC = NULL;
1598 TRACE("\n");
1600 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1602 /* Clear any error information */
1603 INTERNET_SetLastError(0);
1605 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
1606 goto lend;
1608 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1609 if (nResCode == 350)
1611 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
1612 goto lend;
1614 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1617 if (nResCode == 250)
1618 bSuccess = TRUE;
1619 else
1620 FTP_SetResponseError(nResCode);
1622 lend:
1623 hIC = lpwfs->lpAppInfo;
1624 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1626 INTERNET_ASYNC_RESULT iar;
1628 iar.dwResult = (DWORD)bSuccess;
1629 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1630 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1631 &iar, sizeof(INTERNET_ASYNC_RESULT));
1634 return bSuccess;
1637 /***********************************************************************
1638 * FtpCommandA (WININET.@)
1640 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1641 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1643 FIXME("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1644 debugstr_a(lpszCommand), dwContext, phFtpCommand);
1646 return TRUE;
1649 /***********************************************************************
1650 * FtpCommandW (WININET.@)
1652 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1653 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1655 FIXME("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1656 debugstr_w(lpszCommand), dwContext, phFtpCommand);
1658 return TRUE;
1661 /***********************************************************************
1662 * FTP_Connect (internal)
1664 * Connect to a ftp server
1666 * RETURNS
1667 * HINTERNET a session handle on success
1668 * NULL on failure
1670 * NOTES:
1672 * Windows uses 'anonymous' as the username, when given a NULL username
1673 * and a NULL password. The password is first looked up in:
1675 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
1677 * If this entry is not present it uses the current username as the password.
1681 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
1682 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
1683 LPCWSTR lpszPassword, DWORD dwFlags, DWORD dwContext,
1684 DWORD dwInternalFlags)
1686 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
1687 'M','i','c','r','o','s','o','f','t','\\',
1688 'W','i','n','d','o','w','s','\\',
1689 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1690 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
1691 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
1692 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
1693 static const WCHAR szEmpty[] = {'\0'};
1694 struct sockaddr_in socketAddr;
1695 INT nsocket = -1;
1696 UINT sock_namelen;
1697 BOOL bSuccess = FALSE;
1698 LPWININETFTPSESSIONW lpwfs = NULL;
1699 HINTERNET handle = NULL;
1701 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
1702 hIC, debugstr_w(lpszServerName),
1703 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
1705 assert( hIC->hdr.htype == WH_HINIT );
1707 if (NULL == lpszUserName && NULL != lpszPassword)
1709 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1710 goto lerror;
1713 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
1714 if (NULL == lpwfs)
1716 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1717 goto lerror;
1720 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
1721 nServerPort = INTERNET_DEFAULT_FTP_PORT;
1723 lpwfs->hdr.htype = WH_HFTPSESSION;
1724 lpwfs->hdr.dwFlags = dwFlags;
1725 lpwfs->hdr.dwContext = dwContext;
1726 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
1727 lpwfs->hdr.dwRefCount = 1;
1728 lpwfs->hdr.destroy = FTP_CloseSessionHandle;
1729 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
1730 lpwfs->download_in_progress = NULL;
1732 WININET_AddRef( &hIC->hdr );
1733 lpwfs->lpAppInfo = hIC;
1735 handle = WININET_AllocHandle( &lpwfs->hdr );
1736 if( !handle )
1738 ERR("Failed to alloc handle\n");
1739 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1740 goto lerror;
1743 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
1744 if(strchrW(hIC->lpszProxy, ' '))
1745 FIXME("Several proxies not implemented.\n");
1746 if(hIC->lpszProxyBypass)
1747 FIXME("Proxy bypass is ignored.\n");
1749 if ( !lpszUserName) {
1750 HKEY key;
1751 WCHAR szPassword[MAX_PATH];
1752 DWORD len = sizeof(szPassword);
1754 lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
1756 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
1757 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
1758 /* Nothing in the registry, get the username and use that as the password */
1759 if (!GetUserNameW(szPassword, &len)) {
1760 /* Should never get here, but use an empty password as failsafe */
1761 strcpyW(szPassword, szEmpty);
1764 RegCloseKey(key);
1766 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
1767 lpwfs->lpszPassword = WININET_strdupW(szPassword);
1769 else {
1770 lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
1772 if (lpszPassword)
1773 lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
1774 else
1775 lpwfs->lpszPassword = WININET_strdupW(szEmpty);
1778 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
1779 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
1781 INTERNET_ASYNC_RESULT iar;
1783 iar.dwResult = (DWORD)handle;
1784 iar.dwError = ERROR_SUCCESS;
1786 SendAsyncCallback(&hIC->hdr, dwContext,
1787 INTERNET_STATUS_HANDLE_CREATED, &iar,
1788 sizeof(INTERNET_ASYNC_RESULT));
1791 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
1792 (LPWSTR) lpszServerName, strlenW(lpszServerName));
1794 if (!GetAddress(lpszServerName, nServerPort, &socketAddr))
1796 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1797 goto lerror;
1800 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
1801 (LPWSTR) lpszServerName, strlenW(lpszServerName));
1803 nsocket = socket(AF_INET,SOCK_STREAM,0);
1804 if (nsocket == -1)
1806 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1807 goto lerror;
1810 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
1811 &socketAddr, sizeof(struct sockaddr_in));
1813 if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
1815 ERR("Unable to connect (%s)\n", strerror(errno));
1816 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1818 else
1820 TRACE("Connected to server\n");
1821 lpwfs->sndSocket = nsocket;
1822 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
1823 &socketAddr, sizeof(struct sockaddr_in));
1825 sock_namelen = sizeof(lpwfs->socketAddress);
1826 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
1828 if (FTP_ConnectToHost(lpwfs))
1830 TRACE("Successfully logged into server\n");
1831 bSuccess = TRUE;
1835 lerror:
1836 if (!bSuccess && nsocket == -1)
1837 closesocket(nsocket);
1839 if (!bSuccess && lpwfs)
1841 HeapFree(GetProcessHeap(), 0, lpwfs);
1842 WININET_FreeHandle( handle );
1843 handle = NULL;
1844 lpwfs = NULL;
1847 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1849 INTERNET_ASYNC_RESULT iar;
1851 iar.dwResult = (DWORD)lpwfs;
1852 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1853 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1854 &iar, sizeof(INTERNET_ASYNC_RESULT));
1857 return handle;
1861 /***********************************************************************
1862 * FTP_ConnectToHost (internal)
1864 * Connect to a ftp server
1866 * RETURNS
1867 * TRUE on success
1868 * NULL on failure
1871 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
1873 INT nResCode;
1874 BOOL bSuccess = FALSE;
1876 TRACE("\n");
1877 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1879 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
1880 goto lend;
1882 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1883 if (nResCode)
1885 /* Login successful... */
1886 if (nResCode == 230)
1887 bSuccess = TRUE;
1888 /* User name okay, need password... */
1889 else if (nResCode == 331)
1890 bSuccess = FTP_SendPassword(lpwfs);
1891 /* Need account for login... */
1892 else if (nResCode == 332)
1893 bSuccess = FTP_SendAccount(lpwfs);
1894 else
1895 FTP_SetResponseError(nResCode);
1898 TRACE("Returning %d\n", bSuccess);
1899 lend:
1900 return bSuccess;
1904 /***********************************************************************
1905 * FTP_SendCommandA (internal)
1907 * Send command to server
1909 * RETURNS
1910 * TRUE on success
1911 * NULL on failure
1914 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
1915 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext)
1917 DWORD len;
1918 CHAR *buf;
1919 DWORD nBytesSent = 0;
1920 int nRC = 0;
1921 DWORD dwParamLen;
1923 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
1925 if (lpfnStatusCB)
1927 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
1930 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
1931 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
1932 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
1934 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1935 return FALSE;
1937 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
1938 dwParamLen ? lpszParam : "", szCRLF);
1940 TRACE("Sending (%s) len(%d)\n", buf, len);
1941 while((nBytesSent < len) && (nRC != -1))
1943 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
1944 nBytesSent += nRC;
1947 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
1949 if (lpfnStatusCB)
1951 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
1952 &nBytesSent, sizeof(DWORD));
1955 TRACE("Sent %d bytes\n", nBytesSent);
1956 return (nRC != -1);
1959 /***********************************************************************
1960 * FTP_SendCommand (internal)
1962 * Send command to server
1964 * RETURNS
1965 * TRUE on success
1966 * NULL on failure
1969 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
1970 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext)
1972 BOOL ret;
1973 LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
1974 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
1975 HeapFree(GetProcessHeap(), 0, lpszParamA);
1976 return ret;
1979 /***********************************************************************
1980 * FTP_ReceiveResponse (internal)
1982 * Receive response from server
1984 * RETURNS
1985 * Reply code on success
1986 * 0 on failure
1989 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD dwContext)
1991 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
1992 DWORD nRecv;
1993 INT rc = 0;
1994 char firstprefix[5];
1995 BOOL multiline = FALSE;
1996 LPWININETAPPINFOW hIC = NULL;
1998 TRACE("socket(%d)\n", lpwfs->sndSocket);
2000 hIC = lpwfs->lpAppInfo;
2001 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2003 while(1)
2005 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2006 goto lerror;
2008 if (nRecv >= 3)
2010 if(!multiline)
2012 if(lpszResponse[3] != '-')
2013 break;
2014 else
2015 { /* Start of multiline repsonse. Loop until we get "nnn " */
2016 multiline = TRUE;
2017 memcpy(firstprefix, lpszResponse, 3);
2018 firstprefix[3] = ' ';
2019 firstprefix[4] = '\0';
2022 else
2024 if(!memcmp(firstprefix, lpszResponse, 4))
2025 break;
2030 if (nRecv >= 3)
2032 rc = atoi(lpszResponse);
2034 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2035 &nRecv, sizeof(DWORD));
2038 lerror:
2039 TRACE("return %d\n", rc);
2040 return rc;
2044 /***********************************************************************
2045 * FTP_SendPassword (internal)
2047 * Send password to ftp server
2049 * RETURNS
2050 * TRUE on success
2051 * NULL on failure
2054 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2056 INT nResCode;
2057 BOOL bSuccess = FALSE;
2059 TRACE("\n");
2060 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2061 goto lend;
2063 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2064 if (nResCode)
2066 TRACE("Received reply code %d\n", nResCode);
2067 /* Login successful... */
2068 if (nResCode == 230)
2069 bSuccess = TRUE;
2070 /* Command not implemented, superfluous at the server site... */
2071 /* Need account for login... */
2072 else if (nResCode == 332)
2073 bSuccess = FTP_SendAccount(lpwfs);
2074 else
2075 FTP_SetResponseError(nResCode);
2078 lend:
2079 TRACE("Returning %d\n", bSuccess);
2080 return bSuccess;
2084 /***********************************************************************
2085 * FTP_SendAccount (internal)
2089 * RETURNS
2090 * TRUE on success
2091 * FALSE on failure
2094 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2096 INT nResCode;
2097 BOOL bSuccess = FALSE;
2099 TRACE("\n");
2100 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2101 goto lend;
2103 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2104 if (nResCode)
2105 bSuccess = TRUE;
2106 else
2107 FTP_SetResponseError(nResCode);
2109 lend:
2110 return bSuccess;
2114 /***********************************************************************
2115 * FTP_SendStore (internal)
2117 * Send request to upload file to ftp server
2119 * RETURNS
2120 * TRUE on success
2121 * FALSE on failure
2124 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2126 INT nResCode;
2127 BOOL bSuccess = FALSE;
2129 TRACE("\n");
2130 if (!FTP_InitListenSocket(lpwfs))
2131 goto lend;
2133 if (!FTP_SendType(lpwfs, dwType))
2134 goto lend;
2136 if (!FTP_SendPortOrPasv(lpwfs))
2137 goto lend;
2139 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2140 goto lend;
2141 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2142 if (nResCode)
2144 if (nResCode == 150 || nResCode == 125)
2145 bSuccess = TRUE;
2146 else
2147 FTP_SetResponseError(nResCode);
2150 lend:
2151 if (!bSuccess && lpwfs->lstnSocket != -1)
2153 closesocket(lpwfs->lstnSocket);
2154 lpwfs->lstnSocket = -1;
2157 return bSuccess;
2161 /***********************************************************************
2162 * FTP_InitListenSocket (internal)
2164 * Create a socket to listen for server response
2166 * RETURNS
2167 * TRUE on success
2168 * FALSE on failure
2171 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2173 BOOL bSuccess = FALSE;
2174 size_t namelen = sizeof(struct sockaddr_in);
2176 TRACE("\n");
2178 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2179 if (lpwfs->lstnSocket == -1)
2181 TRACE("Unable to create listening socket\n");
2182 goto lend;
2185 /* We obtain our ip addr from the name of the command channel socket */
2186 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2188 /* and get the system to assign us a port */
2189 lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
2191 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2193 TRACE("Unable to bind socket\n");
2194 goto lend;
2197 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2199 TRACE("listen failed\n");
2200 goto lend;
2203 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2204 bSuccess = TRUE;
2206 lend:
2207 if (!bSuccess && lpwfs->lstnSocket == -1)
2209 closesocket(lpwfs->lstnSocket);
2210 lpwfs->lstnSocket = -1;
2213 return bSuccess;
2217 /***********************************************************************
2218 * FTP_SendType (internal)
2220 * Tell server type of data being transferred
2222 * RETURNS
2223 * TRUE on success
2224 * FALSE on failure
2226 * W98SE doesn't cache the type that's currently set
2227 * (i.e. it sends it always),
2228 * so we probably don't want to do that either.
2230 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2232 INT nResCode;
2233 WCHAR type[] = { 'I','\0' };
2234 BOOL bSuccess = FALSE;
2236 TRACE("\n");
2237 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2238 type[0] = 'A';
2240 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2241 goto lend;
2243 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2244 if (nResCode)
2246 if (nResCode == 2)
2247 bSuccess = TRUE;
2248 else
2249 FTP_SetResponseError(nResCode);
2252 lend:
2253 return bSuccess;
2256 /***********************************************************************
2257 * FTP_GetFileSize (internal)
2259 * Retrieves from the server the size of the given file
2261 * RETURNS
2262 * TRUE on success
2263 * FALSE on failure
2266 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2268 INT nResCode;
2269 BOOL bSuccess = FALSE;
2271 TRACE("\n");
2273 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2274 goto lend;
2276 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2277 if (nResCode)
2279 if (nResCode == 213) {
2280 /* Now parses the output to get the actual file size */
2281 int i;
2282 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2284 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2285 if (lpszResponseBuffer[i] == '\0') return FALSE;
2286 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2288 bSuccess = TRUE;
2289 } else {
2290 FTP_SetResponseError(nResCode);
2294 lend:
2295 return bSuccess;
2299 /***********************************************************************
2300 * FTP_SendPort (internal)
2302 * Tell server which port to use
2304 * RETURNS
2305 * TRUE on success
2306 * FALSE on failure
2309 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2311 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2312 INT nResCode;
2313 WCHAR szIPAddress[64];
2314 BOOL bSuccess = FALSE;
2315 TRACE("\n");
2317 sprintfW(szIPAddress, szIPFormat,
2318 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2319 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2320 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2321 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2322 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2323 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2325 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2326 goto lend;
2328 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2329 if (nResCode)
2331 if (nResCode == 200)
2332 bSuccess = TRUE;
2333 else
2334 FTP_SetResponseError(nResCode);
2337 lend:
2338 return bSuccess;
2342 /***********************************************************************
2343 * FTP_DoPassive (internal)
2345 * Tell server that we want to do passive transfers
2346 * and connect data socket
2348 * RETURNS
2349 * TRUE on success
2350 * FALSE on failure
2353 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2355 INT nResCode;
2356 BOOL bSuccess = FALSE;
2358 TRACE("\n");
2359 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2360 goto lend;
2362 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2363 if (nResCode)
2365 if (nResCode == 227)
2367 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2368 LPSTR p;
2369 int f[6];
2370 int i;
2371 char *pAddr, *pPort;
2372 INT nsocket = -1;
2373 struct sockaddr_in dataSocketAddress;
2375 p = lpszResponseBuffer+4; /* skip status code */
2377 /* do a very strict check; we can improve that later. */
2379 if (strncmp(p, "Entering Passive Mode", 21))
2381 ERR("unknown response '%.*s', aborting\n", 21, p);
2382 goto lend;
2384 p += 21; /* skip string */
2385 if ((*p++ != ' ') || (*p++ != '('))
2387 ERR("unknown response format, aborting\n");
2388 goto lend;
2391 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
2392 &f[4], &f[5]) != 6)
2394 ERR("unknown response address format '%s', aborting\n", p);
2395 goto lend;
2397 for (i=0; i < 6; i++)
2398 f[i] = f[i] & 0xff;
2400 dataSocketAddress = lpwfs->socketAddress;
2401 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2402 pPort = (char *)&(dataSocketAddress.sin_port);
2403 pAddr[0] = f[0];
2404 pAddr[1] = f[1];
2405 pAddr[2] = f[2];
2406 pAddr[3] = f[3];
2407 pPort[0] = f[4];
2408 pPort[1] = f[5];
2410 nsocket = socket(AF_INET,SOCK_STREAM,0);
2411 if (nsocket == -1)
2412 goto lend;
2414 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2416 ERR("can't connect passive FTP data port.\n");
2417 closesocket(nsocket);
2418 goto lend;
2420 lpwfs->pasvSocket = nsocket;
2421 bSuccess = TRUE;
2423 else
2424 FTP_SetResponseError(nResCode);
2427 lend:
2428 return bSuccess;
2432 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2434 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2436 if (!FTP_DoPassive(lpwfs))
2437 return FALSE;
2439 else
2441 if (!FTP_SendPort(lpwfs))
2442 return FALSE;
2444 return TRUE;
2448 /***********************************************************************
2449 * FTP_GetDataSocket (internal)
2451 * Either accepts an incoming data socket connection from the server
2452 * or just returns the already opened socket after a PASV command
2453 * in case of passive FTP.
2456 * RETURNS
2457 * TRUE on success
2458 * FALSE on failure
2461 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
2463 struct sockaddr_in saddr;
2464 size_t addrlen = sizeof(struct sockaddr);
2466 TRACE("\n");
2467 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2469 *nDataSocket = lpwfs->pasvSocket;
2471 else
2473 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
2474 closesocket(lpwfs->lstnSocket);
2475 lpwfs->lstnSocket = -1;
2477 return *nDataSocket != -1;
2481 /***********************************************************************
2482 * FTP_SendData (internal)
2484 * Send data to the server
2486 * RETURNS
2487 * TRUE on success
2488 * FALSE on failure
2491 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
2493 BY_HANDLE_FILE_INFORMATION fi;
2494 DWORD nBytesRead = 0;
2495 DWORD nBytesSent = 0;
2496 DWORD nTotalSent = 0;
2497 DWORD nBytesToSend, nLen;
2498 int nRC = 1;
2499 time_t s_long_time, e_long_time;
2500 LONG nSeconds;
2501 CHAR *lpszBuffer;
2503 TRACE("\n");
2504 lpszBuffer = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR)*DATA_PACKET_SIZE);
2505 memset(lpszBuffer, 0, sizeof(CHAR)*DATA_PACKET_SIZE);
2507 /* Get the size of the file. */
2508 GetFileInformationByHandle(hFile, &fi);
2509 time(&s_long_time);
2513 nBytesToSend = nBytesRead - nBytesSent;
2515 if (nBytesToSend <= 0)
2517 /* Read data from file. */
2518 nBytesSent = 0;
2519 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
2520 ERR("Failed reading from file\n");
2522 if (nBytesRead > 0)
2523 nBytesToSend = nBytesRead;
2524 else
2525 break;
2528 nLen = DATA_PACKET_SIZE < nBytesToSend ?
2529 DATA_PACKET_SIZE : nBytesToSend;
2530 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
2532 if (nRC != -1)
2534 nBytesSent += nRC;
2535 nTotalSent += nRC;
2538 /* Do some computation to display the status. */
2539 time(&e_long_time);
2540 nSeconds = e_long_time - s_long_time;
2541 if( nSeconds / 60 > 0 )
2543 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
2544 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
2545 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
2547 else
2549 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
2550 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
2551 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
2553 } while (nRC != -1);
2555 TRACE("file transfer complete!\n");
2557 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2559 return nTotalSent;
2563 /***********************************************************************
2564 * FTP_SendRetrieve (internal)
2566 * Send request to retrieve a file
2568 * RETURNS
2569 * Number of bytes to be received on success
2570 * 0 on failure
2573 static DWORD FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2575 INT nResCode;
2576 DWORD nResult = 0;
2578 TRACE("\n");
2579 if (!FTP_InitListenSocket(lpwfs))
2580 goto lend;
2582 if (!FTP_SendType(lpwfs, dwType))
2583 goto lend;
2585 if (!FTP_SendPortOrPasv(lpwfs))
2586 goto lend;
2588 if (!FTP_GetFileSize(lpwfs, lpszRemoteFile, &nResult))
2589 goto lend;
2591 TRACE("Waiting to receive %d bytes\n", nResult);
2593 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0))
2594 goto lend;
2596 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2597 if ((nResCode != 125) && (nResCode != 150)) {
2598 /* That means that we got an error getting the file. */
2599 nResult = 0;
2602 lend:
2603 if (0 == nResult && lpwfs->lstnSocket != -1)
2605 closesocket(lpwfs->lstnSocket);
2606 lpwfs->lstnSocket = -1;
2609 return nResult;
2613 /***********************************************************************
2614 * FTP_RetrieveData (internal)
2616 * Retrieve data from server
2618 * RETURNS
2619 * TRUE on success
2620 * FALSE on failure
2623 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile)
2625 DWORD nBytesWritten;
2626 DWORD nBytesReceived = 0;
2627 INT nRC = 0;
2628 CHAR *lpszBuffer;
2630 TRACE("\n");
2632 if (INVALID_HANDLE_VALUE == hFile)
2633 return FALSE;
2635 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2636 if (NULL == lpszBuffer)
2638 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2639 return FALSE;
2642 while (nBytesReceived < nBytes && nRC != -1)
2644 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
2645 if (nRC != -1)
2647 /* other side closed socket. */
2648 if (nRC == 0)
2649 goto recv_end;
2650 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
2651 nBytesReceived += nRC;
2654 TRACE("%d bytes of %d (%d%%)\r", nBytesReceived, nBytes,
2655 nBytesReceived * 100 / nBytes);
2658 TRACE("Data transfer complete\n");
2659 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2661 recv_end:
2662 return (nRC != -1);
2666 /***********************************************************************
2667 * FTP_CloseSessionHandle (internal)
2669 * Deallocate session handle
2671 * RETURNS
2672 * TRUE on success
2673 * FALSE on failure
2676 static void FTP_CloseSessionHandle(LPWININETHANDLEHEADER hdr)
2678 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2680 TRACE("\n");
2682 WININET_Release(&lpwfs->lpAppInfo->hdr);
2684 if (lpwfs->download_in_progress != NULL)
2685 lpwfs->download_in_progress->session_deleted = TRUE;
2687 if (lpwfs->sndSocket != -1)
2688 closesocket(lpwfs->sndSocket);
2690 if (lpwfs->lstnSocket != -1)
2691 closesocket(lpwfs->lstnSocket);
2693 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2694 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2695 HeapFree(GetProcessHeap(), 0, lpwfs);
2699 /***********************************************************************
2700 * FTP_FindNextFileW (Internal)
2702 * Continues a file search from a previous call to FindFirstFile
2704 * RETURNS
2705 * TRUE on success
2706 * FALSE on failure
2709 BOOL WINAPI FTP_FindNextFileW(LPWININETFTPFINDNEXTW lpwh, LPVOID lpvFindData)
2711 BOOL bSuccess = TRUE;
2712 LPWIN32_FIND_DATAW lpFindFileData;
2714 TRACE("index(%d) size(%d)\n", lpwh->index, lpwh->size);
2716 assert (lpwh->hdr.htype == WH_HFTPFINDNEXT);
2718 /* Clear any error information */
2719 INTERNET_SetLastError(0);
2721 lpFindFileData = (LPWIN32_FIND_DATAW) lpvFindData;
2722 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
2724 if (lpwh->index >= lpwh->size)
2726 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
2727 bSuccess = FALSE;
2728 goto lend;
2731 FTP_ConvertFileProp(&lpwh->lpafp[lpwh->index], lpFindFileData);
2732 lpwh->index++;
2734 TRACE("\nName: %s\nSize: %d\n", debugstr_w(lpFindFileData->cFileName), lpFindFileData->nFileSizeLow);
2736 lend:
2738 if (lpwh->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2740 INTERNET_ASYNC_RESULT iar;
2742 iar.dwResult = (DWORD)bSuccess;
2743 iar.dwError = iar.dwError = bSuccess ? ERROR_SUCCESS :
2744 INTERNET_GetLastError();
2746 INTERNET_SendCallback(&lpwh->hdr, lpwh->hdr.dwContext,
2747 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2748 sizeof(INTERNET_ASYNC_RESULT));
2751 return bSuccess;
2755 /***********************************************************************
2756 * FTP_CloseFindNextHandle (internal)
2758 * Deallocate session handle
2760 * RETURNS
2761 * TRUE on success
2762 * FALSE on failure
2765 static void FTP_CloseFindNextHandle(LPWININETHANDLEHEADER hdr)
2767 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
2768 DWORD i;
2770 TRACE("\n");
2772 WININET_Release(&lpwfn->lpFtpSession->hdr);
2774 for (i = 0; i < lpwfn->size; i++)
2776 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
2779 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
2780 HeapFree(GetProcessHeap(), 0, lpwfn);
2783 /***********************************************************************
2784 * FTP_CloseFileTransferHandle (internal)
2786 * Closes the file transfer handle. This also 'cleans' the data queue of
2787 * the 'transfer conplete' message (this is a bit of a hack though :-/ )
2790 static void FTP_CloseFileTransferHandle(LPWININETHANDLEHEADER hdr)
2792 LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
2793 LPWININETFTPSESSIONW lpwfs = lpwh->lpFtpSession;
2794 INT nResCode;
2796 TRACE("\n");
2798 WININET_Release(&lpwh->lpFtpSession->hdr);
2800 if (!lpwh->session_deleted)
2801 lpwfs->download_in_progress = NULL;
2803 /* This just serves to flush the control socket of any spurrious lines written
2804 to it (like '226 Transfer complete.').
2806 Wonder what to do if the server sends us an error code though...
2808 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2810 if (lpwh->nDataSocket != -1)
2811 closesocket(lpwh->nDataSocket);
2813 HeapFree(GetProcessHeap(), 0, lpwh);
2816 /***********************************************************************
2817 * FTP_ReceiveFileList (internal)
2819 * Read file list from server
2821 * RETURNS
2822 * Handle to file list on success
2823 * NULL on failure
2826 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
2827 LPWIN32_FIND_DATAW lpFindFileData, DWORD dwContext)
2829 DWORD dwSize = 0;
2830 LPFILEPROPERTIESW lpafp = NULL;
2831 LPWININETFTPFINDNEXTW lpwfn = NULL;
2832 HINTERNET handle = 0;
2834 TRACE("(%p,%d,%s,%p,%d)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
2836 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
2838 if(lpFindFileData)
2839 FTP_ConvertFileProp(lpafp, lpFindFileData);
2841 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
2842 if (lpwfn)
2844 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
2845 lpwfn->hdr.dwContext = dwContext;
2846 lpwfn->hdr.dwRefCount = 1;
2847 lpwfn->hdr.destroy = FTP_CloseFindNextHandle;
2848 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
2849 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
2850 lpwfn->size = dwSize;
2851 lpwfn->lpafp = lpafp;
2853 WININET_AddRef( &lpwfs->hdr );
2854 lpwfn->lpFtpSession = lpwfs;
2856 handle = WININET_AllocHandle( &lpwfn->hdr );
2860 if( lpwfn )
2861 WININET_Release( &lpwfn->hdr );
2863 TRACE("Matched %d files\n", dwSize);
2864 return handle;
2868 /***********************************************************************
2869 * FTP_ConvertFileProp (internal)
2871 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
2873 * RETURNS
2874 * TRUE on success
2875 * FALSE on failure
2878 BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
2880 BOOL bSuccess = FALSE;
2882 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
2884 if (lpafp)
2886 /* Convert 'Unix' time to Windows time */
2887 RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
2888 (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
2889 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
2890 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
2892 /* Not all fields are filled in */
2893 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
2894 lpFindFileData->nFileSizeLow = lpafp->nSize;
2896 if (lpafp->bIsDirectory)
2897 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
2899 if (lpafp->lpszName)
2900 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
2902 bSuccess = TRUE;
2905 return bSuccess;
2908 /***********************************************************************
2909 * FTP_ParseNextFile (internal)
2911 * Parse the next line in file listing
2913 * RETURNS
2914 * TRUE on success
2915 * FALSE on failure
2917 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
2919 static const char szSpace[] = " \t";
2920 DWORD nBufLen;
2921 char *pszLine;
2922 char *pszToken;
2923 char *pszTmp;
2924 BOOL found = FALSE;
2925 int i;
2927 lpfp->lpszName = NULL;
2928 do {
2929 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
2930 return FALSE;
2932 pszToken = strtok(pszLine, szSpace);
2933 /* ls format
2934 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
2936 * For instance:
2937 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
2939 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
2940 if(!FTP_ParsePermission(pszToken, lpfp))
2941 lpfp->bIsDirectory = FALSE;
2942 for(i=0; i<=3; i++) {
2943 if(!(pszToken = strtok(NULL, szSpace)))
2944 break;
2946 if(!pszToken) continue;
2947 if(lpfp->bIsDirectory) {
2948 TRACE("Is directory\n");
2949 lpfp->nSize = 0;
2951 else {
2952 TRACE("Size: %s\n", pszToken);
2953 lpfp->nSize = atol(pszToken);
2956 lpfp->tmLastModified.tm_sec = 0;
2957 lpfp->tmLastModified.tm_min = 0;
2958 lpfp->tmLastModified.tm_hour = 0;
2959 lpfp->tmLastModified.tm_mday = 0;
2960 lpfp->tmLastModified.tm_mon = 0;
2961 lpfp->tmLastModified.tm_year = 0;
2963 /* Determine month */
2964 pszToken = strtok(NULL, szSpace);
2965 if(!pszToken) continue;
2966 if(strlen(pszToken) >= 3) {
2967 pszToken[3] = 0;
2968 if((pszTmp = StrStrIA(szMonths, pszToken)))
2969 lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
2971 /* Determine day */
2972 pszToken = strtok(NULL, szSpace);
2973 if(!pszToken) continue;
2974 lpfp->tmLastModified.tm_mday = atoi(pszToken);
2975 /* Determine time or year */
2976 pszToken = strtok(NULL, szSpace);
2977 if(!pszToken) continue;
2978 if((pszTmp = strchr(pszToken, ':'))) {
2979 struct tm* apTM;
2980 time_t aTime;
2981 *pszTmp = 0;
2982 pszTmp++;
2983 lpfp->tmLastModified.tm_min = atoi(pszTmp);
2984 lpfp->tmLastModified.tm_hour = atoi(pszToken);
2985 time(&aTime);
2986 apTM = localtime(&aTime);
2987 lpfp->tmLastModified.tm_year = apTM->tm_year;
2989 else {
2990 lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
2991 lpfp->tmLastModified.tm_hour = 12;
2993 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
2994 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
2995 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
2996 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
2998 pszToken = strtok(NULL, szSpace);
2999 if(!pszToken) continue;
3000 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3001 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3003 /* NT way of parsing ... :
3005 07-13-03 08:55PM <DIR> sakpatch
3006 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3008 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3009 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3011 sscanf(pszToken, "%d-%d-%d",
3012 &lpfp->tmLastModified.tm_mon,
3013 &lpfp->tmLastModified.tm_mday,
3014 &lpfp->tmLastModified.tm_year);
3016 /* Hacky and bad Y2K protection :-) */
3017 if (lpfp->tmLastModified.tm_year < 70)
3018 lpfp->tmLastModified.tm_year += 100;
3020 pszToken = strtok(NULL, szSpace);
3021 if(!pszToken) continue;
3022 sscanf(pszToken, "%d:%d",
3023 &lpfp->tmLastModified.tm_hour,
3024 &lpfp->tmLastModified.tm_min);
3025 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3026 lpfp->tmLastModified.tm_hour += 12;
3028 lpfp->tmLastModified.tm_sec = 0;
3030 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3031 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3032 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3033 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3035 pszToken = strtok(NULL, szSpace);
3036 if(!pszToken) continue;
3037 if(!strcasecmp(pszToken, "<DIR>")) {
3038 lpfp->bIsDirectory = TRUE;
3039 lpfp->nSize = 0;
3040 TRACE("Is directory\n");
3042 else {
3043 lpfp->bIsDirectory = FALSE;
3044 lpfp->nSize = atol(pszToken);
3045 TRACE("Size: %d\n", lpfp->nSize);
3048 pszToken = strtok(NULL, szSpace);
3049 if(!pszToken) continue;
3050 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3051 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3053 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3054 else if(pszToken[0] == '+') {
3055 FIXME("EPLF Format not implemented\n");
3058 if(lpfp->lpszName) {
3059 if((lpszSearchFile == NULL) ||
3060 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3061 found = TRUE;
3062 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3064 else {
3065 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3066 lpfp->lpszName = NULL;
3069 } while(!found);
3070 return TRUE;
3073 /***********************************************************************
3074 * FTP_ParseDirectory (internal)
3076 * Parse string of directory information
3078 * RETURNS
3079 * TRUE on success
3080 * FALSE on failure
3082 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3083 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3085 BOOL bSuccess = TRUE;
3086 INT sizeFilePropArray = 500;/*20; */
3087 INT indexFilePropArray = -1;
3089 TRACE("\n");
3091 /* Allocate intial file properties array */
3092 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3093 if (!*lpafp)
3094 return FALSE;
3096 do {
3097 if (indexFilePropArray+1 >= sizeFilePropArray)
3099 LPFILEPROPERTIESW tmpafp;
3101 sizeFilePropArray *= 2;
3102 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3103 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3104 if (NULL == tmpafp)
3106 bSuccess = FALSE;
3107 break;
3110 *lpafp = tmpafp;
3112 indexFilePropArray++;
3113 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3115 if (bSuccess && indexFilePropArray)
3117 if (indexFilePropArray < sizeFilePropArray - 1)
3119 LPFILEPROPERTIESW tmpafp;
3121 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3122 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3123 if (NULL == tmpafp)
3124 *lpafp = tmpafp;
3126 *dwfp = indexFilePropArray;
3128 else
3130 HeapFree(GetProcessHeap(), 0, *lpafp);
3131 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3132 bSuccess = FALSE;
3135 return bSuccess;
3139 /***********************************************************************
3140 * FTP_ParsePermission (internal)
3142 * Parse permission string of directory information
3144 * RETURNS
3145 * TRUE on success
3146 * FALSE on failure
3149 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3151 BOOL bSuccess = TRUE;
3152 unsigned short nPermission = 0;
3153 INT nPos = 1;
3154 INT nLast = 9;
3156 TRACE("\n");
3157 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3159 bSuccess = FALSE;
3160 return bSuccess;
3163 lpfp->bIsDirectory = (*lpszPermission == 'd');
3166 switch (nPos)
3168 case 1:
3169 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3170 break;
3171 case 2:
3172 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3173 break;
3174 case 3:
3175 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3176 break;
3177 case 4:
3178 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3179 break;
3180 case 5:
3181 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3182 break;
3183 case 6:
3184 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3185 break;
3186 case 7:
3187 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3188 break;
3189 case 8:
3190 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3191 break;
3192 case 9:
3193 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3194 break;
3196 nPos++;
3197 }while (nPos <= nLast);
3199 lpfp->permissions = nPermission;
3200 return bSuccess;
3204 /***********************************************************************
3205 * FTP_SetResponseError (internal)
3207 * Set the appropriate error code for a given response from the server
3209 * RETURNS
3212 static DWORD FTP_SetResponseError(DWORD dwResponse)
3214 DWORD dwCode = 0;
3216 switch(dwResponse)
3218 case 421: /* Service not available - Server may be shutting down. */
3219 dwCode = ERROR_INTERNET_TIMEOUT;
3220 break;
3222 case 425: /* Cannot open data connection. */
3223 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3224 break;
3226 case 426: /* Connection closed, transer aborted. */
3227 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3228 break;
3230 case 500: /* Syntax error. Command unrecognized. */
3231 case 501: /* Syntax error. Error in parameters or arguments. */
3232 dwCode = ERROR_INTERNET_INCORRECT_FORMAT;
3233 break;
3235 case 530: /* Not logged in. Login incorrect. */
3236 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3237 break;
3239 case 550: /* File action not taken. File not found or no access. */
3240 dwCode = ERROR_INTERNET_ITEM_NOT_FOUND;
3241 break;
3243 case 450: /* File action not taken. File may be busy. */
3244 case 451: /* Action aborted. Server error. */
3245 case 452: /* Action not taken. Insufficient storage space on server. */
3246 case 502: /* Command not implemented. */
3247 case 503: /* Bad sequence of command. */
3248 case 504: /* Command not implemented for that parameter. */
3249 case 532: /* Need account for storing files */
3250 case 551: /* Requested action aborted. Page type unknown */
3251 case 552: /* Action aborted. Exceeded storage allocation */
3252 case 553: /* Action not taken. File name not allowed. */
3254 default:
3255 dwCode = ERROR_INTERNET_INTERNAL_ERROR;
3256 break;
3259 INTERNET_SetLastError(dwCode);
3260 return dwCode;