msi: Change an ERR to a WARN.
[wine.git] / dlls / wininet / ftp.c
blob62f60c1b2ec6a3d1529b5e3c2faa7de19e5ffe91
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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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(%ld)\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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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(%ld)\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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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%08lx,0x%08lx,0x%08lx)\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 = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
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 LPWININETFILE 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(WININETFILE));
1037 lpwh->hdr.htype = WH_HFILE;
1038 lpwh->hdr.dwFlags = dwFlags;
1039 lpwh->hdr.dwContext = dwContext;
1040 lpwh->hdr.lpwhparent = WININET_AddRef( &lpwfs->hdr );
1041 lpwh->hdr.dwRefCount = 1;
1042 lpwh->hdr.destroy = FTP_CloseFileTransferHandle;
1043 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1044 lpwh->nDataSocket = nDataSocket;
1045 lpwh->session_deleted = FALSE;
1047 handle = WININET_AllocHandle( &lpwh->hdr );
1048 if( !handle )
1049 goto lend;
1051 /* Indicate that a download is currently in progress */
1052 lpwfs->download_in_progress = lpwh;
1055 if (lpwfs->lstnSocket != -1)
1056 closesocket(lpwfs->lstnSocket);
1058 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1059 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1061 INTERNET_ASYNC_RESULT iar;
1063 if (lpwh)
1065 iar.dwResult = (DWORD)handle;
1066 iar.dwError = ERROR_SUCCESS;
1067 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1068 &iar, sizeof(INTERNET_ASYNC_RESULT));
1071 iar.dwResult = (DWORD)bSuccess;
1072 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1073 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1074 &iar, sizeof(INTERNET_ASYNC_RESULT));
1077 lend:
1078 if( lpwh )
1079 WININET_Release( &lpwh->hdr );
1081 return handle;
1085 /***********************************************************************
1086 * FtpGetFileA (WININET.@)
1088 * Retrieve file from the FTP server
1090 * RETURNS
1091 * TRUE on success
1092 * FALSE on failure
1095 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1096 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1097 DWORD dwContext)
1099 LPWSTR lpwzRemoteFile;
1100 LPWSTR lpwzNewFile;
1101 BOOL ret;
1103 lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
1104 lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
1105 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1106 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1107 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1108 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1109 return ret;
1113 /***********************************************************************
1114 * FtpGetFileW (WININET.@)
1116 * Retrieve file from the FTP server
1118 * RETURNS
1119 * TRUE on success
1120 * FALSE on failure
1123 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1124 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1125 DWORD dwContext)
1127 LPWININETFTPSESSIONW lpwfs;
1128 LPWININETAPPINFOW hIC = NULL;
1129 BOOL r = FALSE;
1131 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
1132 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1134 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1135 goto lend;
1138 if (lpwfs->download_in_progress != NULL) {
1139 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1140 goto lend;
1143 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1144 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1146 WORKREQUEST workRequest;
1147 struct WORKREQ_FTPGETFILEW *req;
1149 workRequest.asyncall = FTPGETFILEW;
1150 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1151 req = &workRequest.u.FtpGetFileW;
1152 req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
1153 req->lpszNewFile = WININET_strdupW(lpszNewFile);
1154 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1155 req->fFailIfExists = fFailIfExists;
1156 req->dwFlags = dwInternetFlags;
1157 req->dwContext = dwContext;
1159 r = INTERNET_AsyncCall(&workRequest);
1161 else
1163 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1164 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1167 lend:
1168 if( lpwfs )
1169 WININET_Release( &lpwfs->hdr );
1171 return r;
1175 /***********************************************************************
1176 * FTP_FtpGetFileW (Internal)
1178 * Retrieve file from the FTP server
1180 * RETURNS
1181 * TRUE on success
1182 * FALSE on failure
1185 BOOL WINAPI FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1186 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1187 DWORD dwContext)
1189 DWORD nBytes;
1190 BOOL bSuccess = FALSE;
1191 HANDLE hFile;
1192 LPWININETAPPINFOW hIC = NULL;
1194 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1196 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1198 /* Clear any error information */
1199 INTERNET_SetLastError(0);
1201 /* Ensure we can write to lpszNewfile by opening it */
1202 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1203 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1204 if (INVALID_HANDLE_VALUE == hFile)
1205 goto lend;
1207 /* Set up socket to retrieve data */
1208 nBytes = FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags);
1210 if (nBytes > 0)
1212 INT nDataSocket;
1214 /* Get data socket to server */
1215 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1217 INT nResCode;
1219 /* Receive data */
1220 FTP_RetrieveFileData(lpwfs, nDataSocket, nBytes, hFile);
1221 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1222 if (nResCode)
1224 if (nResCode == 226)
1225 bSuccess = TRUE;
1226 else
1227 FTP_SetResponseError(nResCode);
1229 closesocket(nDataSocket);
1233 lend:
1234 if (lpwfs->lstnSocket != -1)
1235 closesocket(lpwfs->lstnSocket);
1237 if (hFile)
1238 CloseHandle(hFile);
1240 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1241 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1243 INTERNET_ASYNC_RESULT iar;
1245 iar.dwResult = (DWORD)bSuccess;
1246 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1247 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1248 &iar, sizeof(INTERNET_ASYNC_RESULT));
1251 return bSuccess;
1254 /***********************************************************************
1255 * FtpGetFileSize (WININET.@)
1257 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1259 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1261 if (lpdwFileSizeHigh)
1262 *lpdwFileSizeHigh = 0;
1264 return 0;
1267 /***********************************************************************
1268 * FtpDeleteFileA (WININET.@)
1270 * Delete a file on the ftp server
1272 * RETURNS
1273 * TRUE on success
1274 * FALSE on failure
1277 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1279 LPWSTR lpwzFileName;
1280 BOOL ret;
1282 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1283 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1284 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1285 return ret;
1288 /***********************************************************************
1289 * FtpDeleteFileW (WININET.@)
1291 * Delete a file on the ftp server
1293 * RETURNS
1294 * TRUE on success
1295 * FALSE on failure
1298 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1300 LPWININETFTPSESSIONW lpwfs;
1301 LPWININETAPPINFOW hIC = NULL;
1302 BOOL r = FALSE;
1304 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1305 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1307 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1308 goto lend;
1311 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1312 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1314 WORKREQUEST workRequest;
1315 struct WORKREQ_FTPDELETEFILEW *req;
1317 workRequest.asyncall = FTPDELETEFILEW;
1318 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1319 req = &workRequest.u.FtpDeleteFileW;
1320 req->lpszFilename = WININET_strdupW(lpszFileName);
1322 r = INTERNET_AsyncCall(&workRequest);
1324 else
1326 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1329 lend:
1330 if( lpwfs )
1331 WININET_Release( &lpwfs->hdr );
1333 return r;
1336 /***********************************************************************
1337 * FTP_FtpDeleteFileW (Internal)
1339 * Delete a file on the ftp server
1341 * RETURNS
1342 * TRUE on success
1343 * FALSE on failure
1346 BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName)
1348 INT nResCode;
1349 BOOL bSuccess = FALSE;
1350 LPWININETAPPINFOW hIC = NULL;
1352 TRACE("%p\n", lpwfs);
1354 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1356 /* Clear any error information */
1357 INTERNET_SetLastError(0);
1359 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1360 goto lend;
1362 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1363 if (nResCode)
1365 if (nResCode == 250)
1366 bSuccess = TRUE;
1367 else
1368 FTP_SetResponseError(nResCode);
1370 lend:
1371 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1372 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1374 INTERNET_ASYNC_RESULT iar;
1376 iar.dwResult = (DWORD)bSuccess;
1377 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1378 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1379 &iar, sizeof(INTERNET_ASYNC_RESULT));
1382 return bSuccess;
1386 /***********************************************************************
1387 * FtpRemoveDirectoryA (WININET.@)
1389 * Remove a directory on the ftp server
1391 * RETURNS
1392 * TRUE on success
1393 * FALSE on failure
1396 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1398 LPWSTR lpwzDirectory;
1399 BOOL ret;
1401 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
1402 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1403 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1404 return ret;
1407 /***********************************************************************
1408 * FtpRemoveDirectoryW (WININET.@)
1410 * Remove a directory on the ftp server
1412 * RETURNS
1413 * TRUE on success
1414 * FALSE on failure
1417 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1419 LPWININETFTPSESSIONW lpwfs;
1420 LPWININETAPPINFOW hIC = NULL;
1421 BOOL r = FALSE;
1423 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1424 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1426 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1427 goto lend;
1430 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1431 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1433 WORKREQUEST workRequest;
1434 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1436 workRequest.asyncall = FTPREMOVEDIRECTORYW;
1437 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1438 req = &workRequest.u.FtpRemoveDirectoryW;
1439 req->lpszDirectory = WININET_strdupW(lpszDirectory);
1441 r = INTERNET_AsyncCall(&workRequest);
1443 else
1445 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1448 lend:
1449 if( lpwfs )
1450 WININET_Release( &lpwfs->hdr );
1452 return r;
1455 /***********************************************************************
1456 * FTP_FtpRemoveDirectoryW (Internal)
1458 * Remove a directory on the ftp server
1460 * RETURNS
1461 * TRUE on success
1462 * FALSE on failure
1465 BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
1467 INT nResCode;
1468 BOOL bSuccess = FALSE;
1469 LPWININETAPPINFOW hIC = NULL;
1471 TRACE("\n");
1473 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1475 /* Clear any error information */
1476 INTERNET_SetLastError(0);
1478 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1479 goto lend;
1481 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1482 if (nResCode)
1484 if (nResCode == 250)
1485 bSuccess = TRUE;
1486 else
1487 FTP_SetResponseError(nResCode);
1490 lend:
1491 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1492 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1494 INTERNET_ASYNC_RESULT iar;
1496 iar.dwResult = (DWORD)bSuccess;
1497 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1498 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1499 &iar, sizeof(INTERNET_ASYNC_RESULT));
1502 return bSuccess;
1506 /***********************************************************************
1507 * FtpRenameFileA (WININET.@)
1509 * Rename a file on the ftp server
1511 * RETURNS
1512 * TRUE on success
1513 * FALSE on failure
1516 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1518 LPWSTR lpwzSrc;
1519 LPWSTR lpwzDest;
1520 BOOL ret;
1522 lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL;
1523 lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL;
1524 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1525 HeapFree(GetProcessHeap(), 0, lpwzSrc);
1526 HeapFree(GetProcessHeap(), 0, lpwzDest);
1527 return ret;
1530 /***********************************************************************
1531 * FtpRenameFileW (WININET.@)
1533 * Rename a file on the ftp server
1535 * RETURNS
1536 * TRUE on success
1537 * FALSE on failure
1540 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
1542 LPWININETFTPSESSIONW lpwfs;
1543 LPWININETAPPINFOW hIC = NULL;
1544 BOOL r = FALSE;
1546 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1547 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1549 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1550 goto lend;
1553 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1554 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1556 WORKREQUEST workRequest;
1557 struct WORKREQ_FTPRENAMEFILEW *req;
1559 workRequest.asyncall = FTPRENAMEFILEW;
1560 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1561 req = &workRequest.u.FtpRenameFileW;
1562 req->lpszSrcFile = WININET_strdupW(lpszSrc);
1563 req->lpszDestFile = WININET_strdupW(lpszDest);
1565 r = INTERNET_AsyncCall(&workRequest);
1567 else
1569 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
1572 lend:
1573 if( lpwfs )
1574 WININET_Release( &lpwfs->hdr );
1576 return r;
1579 /***********************************************************************
1580 * FTP_FtpRenameFileW (Internal)
1582 * Rename a file on the ftp server
1584 * RETURNS
1585 * TRUE on success
1586 * FALSE on failure
1589 BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs,
1590 LPCWSTR lpszSrc, LPCWSTR lpszDest)
1592 INT nResCode;
1593 BOOL bSuccess = FALSE;
1594 LPWININETAPPINFOW hIC = NULL;
1596 TRACE("\n");
1598 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1600 /* Clear any error information */
1601 INTERNET_SetLastError(0);
1603 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
1604 goto lend;
1606 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1607 if (nResCode == 350)
1609 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
1610 goto lend;
1612 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1615 if (nResCode == 250)
1616 bSuccess = TRUE;
1617 else
1618 FTP_SetResponseError(nResCode);
1620 lend:
1621 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1622 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1624 INTERNET_ASYNC_RESULT iar;
1626 iar.dwResult = (DWORD)bSuccess;
1627 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1628 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1629 &iar, sizeof(INTERNET_ASYNC_RESULT));
1632 return bSuccess;
1635 /***********************************************************************
1636 * FtpCommandA (WININET.@)
1638 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1639 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1641 FIXME("%p %d 0x%08lx %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1642 debugstr_a(lpszCommand), dwContext, phFtpCommand);
1644 return TRUE;
1647 /***********************************************************************
1648 * FtpCommandW (WININET.@)
1650 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1651 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1653 FIXME("%p %d 0x%08lx %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1654 debugstr_w(lpszCommand), dwContext, phFtpCommand);
1656 return TRUE;
1659 /***********************************************************************
1660 * FTP_Connect (internal)
1662 * Connect to a ftp server
1664 * RETURNS
1665 * HINTERNET a session handle on success
1666 * NULL on failure
1668 * NOTES:
1670 * Windows uses 'anonymous' as the username, when given a NULL username
1671 * and a NULL password. The password is first looked up in:
1673 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
1675 * If this entry is not present it uses the current username as the password.
1679 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
1680 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
1681 LPCWSTR lpszPassword, DWORD dwFlags, DWORD dwContext,
1682 DWORD dwInternalFlags)
1684 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
1685 'M','i','c','r','o','s','o','f','t','\\',
1686 'W','i','n','d','o','w','s','\\',
1687 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1688 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
1689 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
1690 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
1691 static const WCHAR szEmpty[] = {'\0'};
1692 struct sockaddr_in socketAddr;
1693 INT nsocket = -1;
1694 UINT sock_namelen;
1695 BOOL bSuccess = FALSE;
1696 LPWININETFTPSESSIONW lpwfs = NULL;
1697 HINTERNET handle = NULL;
1699 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
1700 hIC, debugstr_w(lpszServerName),
1701 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
1703 assert( hIC->hdr.htype == WH_HINIT );
1705 if (NULL == lpszUserName && NULL != lpszPassword)
1707 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1708 goto lerror;
1711 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
1712 if (NULL == lpwfs)
1714 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1715 goto lerror;
1718 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
1719 nServerPort = INTERNET_DEFAULT_FTP_PORT;
1721 lpwfs->hdr.htype = WH_HFTPSESSION;
1722 lpwfs->hdr.lpwhparent = WININET_AddRef( &hIC->hdr );
1723 lpwfs->hdr.dwFlags = dwFlags;
1724 lpwfs->hdr.dwContext = dwContext;
1725 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
1726 lpwfs->hdr.dwRefCount = 1;
1727 lpwfs->hdr.destroy = FTP_CloseSessionHandle;
1728 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
1729 lpwfs->download_in_progress = NULL;
1731 handle = WININET_AllocHandle( &lpwfs->hdr );
1732 if( !handle )
1734 ERR("Failed to alloc handle\n");
1735 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1736 goto lerror;
1739 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
1740 if(strchrW(hIC->lpszProxy, ' '))
1741 FIXME("Several proxies not implemented.\n");
1742 if(hIC->lpszProxyBypass)
1743 FIXME("Proxy bypass is ignored.\n");
1745 if ( !lpszUserName) {
1746 HKEY key;
1747 WCHAR szPassword[MAX_PATH];
1748 DWORD len = sizeof(szPassword);
1750 lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
1752 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
1753 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
1754 /* Nothing in the registry, get the username and use that as the password */
1755 if (!GetUserNameW(szPassword, &len)) {
1756 /* Should never get here, but use an empty password as failsafe */
1757 strcpyW(szPassword, szEmpty);
1760 RegCloseKey(key);
1762 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
1763 lpwfs->lpszPassword = WININET_strdupW(szPassword);
1765 else {
1766 lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
1768 if (lpszPassword)
1769 lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
1770 else
1771 lpwfs->lpszPassword = WININET_strdupW(szEmpty);
1774 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
1775 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
1777 INTERNET_ASYNC_RESULT iar;
1779 iar.dwResult = (DWORD)handle;
1780 iar.dwError = ERROR_SUCCESS;
1782 SendAsyncCallback(&hIC->hdr, dwContext,
1783 INTERNET_STATUS_HANDLE_CREATED, &iar,
1784 sizeof(INTERNET_ASYNC_RESULT));
1787 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
1788 (LPWSTR) lpszServerName, strlenW(lpszServerName));
1790 if (!GetAddress(lpszServerName, nServerPort, &socketAddr))
1792 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1793 goto lerror;
1796 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
1797 (LPWSTR) lpszServerName, strlenW(lpszServerName));
1799 nsocket = socket(AF_INET,SOCK_STREAM,0);
1800 if (nsocket == -1)
1802 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1803 goto lerror;
1806 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
1807 &socketAddr, sizeof(struct sockaddr_in));
1809 if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
1811 ERR("Unable to connect (%s)\n", strerror(errno));
1812 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1814 else
1816 TRACE("Connected to server\n");
1817 lpwfs->sndSocket = nsocket;
1818 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
1819 &socketAddr, sizeof(struct sockaddr_in));
1821 sock_namelen = sizeof(lpwfs->socketAddress);
1822 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
1824 if (FTP_ConnectToHost(lpwfs))
1826 TRACE("Successfully logged into server\n");
1827 bSuccess = TRUE;
1831 lerror:
1832 if (!bSuccess && nsocket == -1)
1833 closesocket(nsocket);
1835 if (!bSuccess && lpwfs)
1837 HeapFree(GetProcessHeap(), 0, lpwfs);
1838 WININET_FreeHandle( handle );
1839 handle = NULL;
1840 lpwfs = NULL;
1843 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1845 INTERNET_ASYNC_RESULT iar;
1847 iar.dwResult = (DWORD)lpwfs;
1848 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1849 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1850 &iar, sizeof(INTERNET_ASYNC_RESULT));
1853 return handle;
1857 /***********************************************************************
1858 * FTP_ConnectToHost (internal)
1860 * Connect to a ftp server
1862 * RETURNS
1863 * TRUE on success
1864 * NULL on failure
1867 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
1869 INT nResCode;
1870 BOOL bSuccess = FALSE;
1872 TRACE("\n");
1873 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1875 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
1876 goto lend;
1878 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1879 if (nResCode)
1881 /* Login successful... */
1882 if (nResCode == 230)
1883 bSuccess = TRUE;
1884 /* User name okay, need password... */
1885 else if (nResCode == 331)
1886 bSuccess = FTP_SendPassword(lpwfs);
1887 /* Need account for login... */
1888 else if (nResCode == 332)
1889 bSuccess = FTP_SendAccount(lpwfs);
1890 else
1891 FTP_SetResponseError(nResCode);
1894 TRACE("Returning %d\n", bSuccess);
1895 lend:
1896 return bSuccess;
1900 /***********************************************************************
1901 * FTP_SendCommandA (internal)
1903 * Send command to server
1905 * RETURNS
1906 * TRUE on success
1907 * NULL on failure
1910 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
1911 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext)
1913 DWORD len;
1914 CHAR *buf;
1915 DWORD nBytesSent = 0;
1916 int nRC = 0;
1917 DWORD dwParamLen;
1919 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
1921 if (lpfnStatusCB)
1923 HINTERNET hHandle = WININET_FindHandle( hdr );
1924 if( hHandle )
1926 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
1927 WININET_Release( hdr );
1931 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
1932 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
1933 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
1935 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1936 return FALSE;
1938 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
1939 dwParamLen ? lpszParam : "", szCRLF);
1941 TRACE("Sending (%s) len(%ld)\n", buf, len);
1942 while((nBytesSent < len) && (nRC != -1))
1944 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
1945 nBytesSent += nRC;
1948 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
1950 if (lpfnStatusCB)
1952 HINTERNET hHandle = WININET_FindHandle( hdr );
1953 if( hHandle )
1955 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_REQUEST_SENT,
1956 &nBytesSent, sizeof(DWORD));
1957 WININET_Release( hdr );
1961 TRACE("Sent %ld bytes\n", nBytesSent);
1962 return (nRC != -1);
1965 /***********************************************************************
1966 * FTP_SendCommand (internal)
1968 * Send command to server
1970 * RETURNS
1971 * TRUE on success
1972 * NULL on failure
1975 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
1976 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext)
1978 BOOL ret;
1979 LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
1980 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
1981 HeapFree(GetProcessHeap(), 0, lpszParamA);
1982 return ret;
1985 /***********************************************************************
1986 * FTP_ReceiveResponse (internal)
1988 * Receive response from server
1990 * RETURNS
1991 * Reply code on success
1992 * 0 on failure
1995 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD dwContext)
1997 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
1998 DWORD nRecv;
1999 INT rc = 0;
2000 char firstprefix[5];
2001 BOOL multiline = FALSE;
2002 LPWININETAPPINFOW hIC = NULL;
2004 TRACE("socket(%d)\n", lpwfs->sndSocket);
2006 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
2007 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2009 while(1)
2011 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2012 goto lerror;
2014 if (nRecv >= 3)
2016 if(!multiline)
2018 if(lpszResponse[3] != '-')
2019 break;
2020 else
2021 { /* Start of multiline repsonse. Loop until we get "nnn " */
2022 multiline = TRUE;
2023 memcpy(firstprefix, lpszResponse, 3);
2024 firstprefix[3] = ' ';
2025 firstprefix[4] = '\0';
2028 else
2030 if(!memcmp(firstprefix, lpszResponse, 4))
2031 break;
2036 if (nRecv >= 3)
2038 rc = atoi(lpszResponse);
2040 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2041 &nRecv, sizeof(DWORD));
2044 lerror:
2045 TRACE("return %d\n", rc);
2046 return rc;
2050 /***********************************************************************
2051 * FTP_SendPassword (internal)
2053 * Send password to ftp server
2055 * RETURNS
2056 * TRUE on success
2057 * NULL on failure
2060 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2062 INT nResCode;
2063 BOOL bSuccess = FALSE;
2065 TRACE("\n");
2066 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2067 goto lend;
2069 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2070 if (nResCode)
2072 TRACE("Received reply code %d\n", nResCode);
2073 /* Login successful... */
2074 if (nResCode == 230)
2075 bSuccess = TRUE;
2076 /* Command not implemented, superfluous at the server site... */
2077 /* Need account for login... */
2078 else if (nResCode == 332)
2079 bSuccess = FTP_SendAccount(lpwfs);
2080 else
2081 FTP_SetResponseError(nResCode);
2084 lend:
2085 TRACE("Returning %d\n", bSuccess);
2086 return bSuccess;
2090 /***********************************************************************
2091 * FTP_SendAccount (internal)
2095 * RETURNS
2096 * TRUE on success
2097 * FALSE on failure
2100 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2102 INT nResCode;
2103 BOOL bSuccess = FALSE;
2105 TRACE("\n");
2106 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2107 goto lend;
2109 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2110 if (nResCode)
2111 bSuccess = TRUE;
2112 else
2113 FTP_SetResponseError(nResCode);
2115 lend:
2116 return bSuccess;
2120 /***********************************************************************
2121 * FTP_SendStore (internal)
2123 * Send request to upload file to ftp server
2125 * RETURNS
2126 * TRUE on success
2127 * FALSE on failure
2130 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2132 INT nResCode;
2133 BOOL bSuccess = FALSE;
2135 TRACE("\n");
2136 if (!FTP_InitListenSocket(lpwfs))
2137 goto lend;
2139 if (!FTP_SendType(lpwfs, dwType))
2140 goto lend;
2142 if (!FTP_SendPortOrPasv(lpwfs))
2143 goto lend;
2145 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2146 goto lend;
2147 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2148 if (nResCode)
2150 if (nResCode == 150 || nResCode == 125)
2151 bSuccess = TRUE;
2152 else
2153 FTP_SetResponseError(nResCode);
2156 lend:
2157 if (!bSuccess && lpwfs->lstnSocket != -1)
2159 closesocket(lpwfs->lstnSocket);
2160 lpwfs->lstnSocket = -1;
2163 return bSuccess;
2167 /***********************************************************************
2168 * FTP_InitListenSocket (internal)
2170 * Create a socket to listen for server response
2172 * RETURNS
2173 * TRUE on success
2174 * FALSE on failure
2177 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2179 BOOL bSuccess = FALSE;
2180 size_t namelen = sizeof(struct sockaddr_in);
2182 TRACE("\n");
2184 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2185 if (lpwfs->lstnSocket == -1)
2187 TRACE("Unable to create listening socket\n");
2188 goto lend;
2191 /* We obtain our ip addr from the name of the command channel socket */
2192 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2194 /* and get the system to assign us a port */
2195 lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
2197 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2199 TRACE("Unable to bind socket\n");
2200 goto lend;
2203 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2205 TRACE("listen failed\n");
2206 goto lend;
2209 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2210 bSuccess = TRUE;
2212 lend:
2213 if (!bSuccess && lpwfs->lstnSocket == -1)
2215 closesocket(lpwfs->lstnSocket);
2216 lpwfs->lstnSocket = -1;
2219 return bSuccess;
2223 /***********************************************************************
2224 * FTP_SendType (internal)
2226 * Tell server type of data being transferred
2228 * RETURNS
2229 * TRUE on success
2230 * FALSE on failure
2232 * W98SE doesn't cache the type that's currently set
2233 * (i.e. it sends it always),
2234 * so we probably don't want to do that either.
2236 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2238 INT nResCode;
2239 WCHAR type[] = { 'I','\0' };
2240 BOOL bSuccess = FALSE;
2242 TRACE("\n");
2243 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2244 type[0] = 'A';
2246 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2247 goto lend;
2249 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2250 if (nResCode)
2252 if (nResCode == 2)
2253 bSuccess = TRUE;
2254 else
2255 FTP_SetResponseError(nResCode);
2258 lend:
2259 return bSuccess;
2262 /***********************************************************************
2263 * FTP_GetFileSize (internal)
2265 * Retrieves from the server the size of the given file
2267 * RETURNS
2268 * TRUE on success
2269 * FALSE on failure
2272 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2274 INT nResCode;
2275 BOOL bSuccess = FALSE;
2277 TRACE("\n");
2279 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2280 goto lend;
2282 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2283 if (nResCode)
2285 if (nResCode == 213) {
2286 /* Now parses the output to get the actual file size */
2287 int i;
2288 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2290 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2291 if (lpszResponseBuffer[i] == '\0') return FALSE;
2292 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2294 bSuccess = TRUE;
2295 } else {
2296 FTP_SetResponseError(nResCode);
2300 lend:
2301 return bSuccess;
2305 /***********************************************************************
2306 * FTP_SendPort (internal)
2308 * Tell server which port to use
2310 * RETURNS
2311 * TRUE on success
2312 * FALSE on failure
2315 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2317 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2318 INT nResCode;
2319 WCHAR szIPAddress[64];
2320 BOOL bSuccess = FALSE;
2321 TRACE("\n");
2323 sprintfW(szIPAddress, szIPFormat,
2324 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2325 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2326 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2327 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2328 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2329 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2331 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2332 goto lend;
2334 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2335 if (nResCode)
2337 if (nResCode == 200)
2338 bSuccess = TRUE;
2339 else
2340 FTP_SetResponseError(nResCode);
2343 lend:
2344 return bSuccess;
2348 /***********************************************************************
2349 * FTP_DoPassive (internal)
2351 * Tell server that we want to do passive transfers
2352 * and connect data socket
2354 * RETURNS
2355 * TRUE on success
2356 * FALSE on failure
2359 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2361 INT nResCode;
2362 BOOL bSuccess = FALSE;
2364 TRACE("\n");
2365 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2366 goto lend;
2368 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2369 if (nResCode)
2371 if (nResCode == 227)
2373 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2374 LPSTR p;
2375 int f[6];
2376 int i;
2377 char *pAddr, *pPort;
2378 INT nsocket = -1;
2379 struct sockaddr_in dataSocketAddress;
2381 p = lpszResponseBuffer+4; /* skip status code */
2383 /* do a very strict check; we can improve that later. */
2385 if (strncmp(p, "Entering Passive Mode", 21))
2387 ERR("unknown response '%.*s', aborting\n", 21, p);
2388 goto lend;
2390 p += 21; /* skip string */
2391 if ((*p++ != ' ') || (*p++ != '('))
2393 ERR("unknown response format, aborting\n");
2394 goto lend;
2397 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
2398 &f[4], &f[5]) != 6)
2400 ERR("unknown response address format '%s', aborting\n", p);
2401 goto lend;
2403 for (i=0; i < 6; i++)
2404 f[i] = f[i] & 0xff;
2406 dataSocketAddress = lpwfs->socketAddress;
2407 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2408 pPort = (char *)&(dataSocketAddress.sin_port);
2409 pAddr[0] = f[0];
2410 pAddr[1] = f[1];
2411 pAddr[2] = f[2];
2412 pAddr[3] = f[3];
2413 pPort[0] = f[4];
2414 pPort[1] = f[5];
2416 nsocket = socket(AF_INET,SOCK_STREAM,0);
2417 if (nsocket == -1)
2418 goto lend;
2420 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2422 ERR("can't connect passive FTP data port.\n");
2423 goto lend;
2425 lpwfs->pasvSocket = nsocket;
2426 bSuccess = TRUE;
2428 else
2429 FTP_SetResponseError(nResCode);
2432 lend:
2433 return bSuccess;
2437 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2439 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2441 if (!FTP_DoPassive(lpwfs))
2442 return FALSE;
2444 else
2446 if (!FTP_SendPort(lpwfs))
2447 return FALSE;
2449 return TRUE;
2453 /***********************************************************************
2454 * FTP_GetDataSocket (internal)
2456 * Either accepts an incoming data socket connection from the server
2457 * or just returns the already opened socket after a PASV command
2458 * in case of passive FTP.
2461 * RETURNS
2462 * TRUE on success
2463 * FALSE on failure
2466 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
2468 struct sockaddr_in saddr;
2469 size_t addrlen = sizeof(struct sockaddr);
2471 TRACE("\n");
2472 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2474 *nDataSocket = lpwfs->pasvSocket;
2476 else
2478 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
2479 closesocket(lpwfs->lstnSocket);
2480 lpwfs->lstnSocket = -1;
2482 return *nDataSocket != -1;
2486 /***********************************************************************
2487 * FTP_SendData (internal)
2489 * Send data to the server
2491 * RETURNS
2492 * TRUE on success
2493 * FALSE on failure
2496 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
2498 BY_HANDLE_FILE_INFORMATION fi;
2499 DWORD nBytesRead = 0;
2500 DWORD nBytesSent = 0;
2501 DWORD nTotalSent = 0;
2502 DWORD nBytesToSend, nLen;
2503 int nRC = 1;
2504 time_t s_long_time, e_long_time;
2505 LONG nSeconds;
2506 CHAR *lpszBuffer;
2508 TRACE("\n");
2509 lpszBuffer = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR)*DATA_PACKET_SIZE);
2510 memset(lpszBuffer, 0, sizeof(CHAR)*DATA_PACKET_SIZE);
2512 /* Get the size of the file. */
2513 GetFileInformationByHandle(hFile, &fi);
2514 time(&s_long_time);
2518 nBytesToSend = nBytesRead - nBytesSent;
2520 if (nBytesToSend <= 0)
2522 /* Read data from file. */
2523 nBytesSent = 0;
2524 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
2525 ERR("Failed reading from file\n");
2527 if (nBytesRead > 0)
2528 nBytesToSend = nBytesRead;
2529 else
2530 break;
2533 nLen = DATA_PACKET_SIZE < nBytesToSend ?
2534 DATA_PACKET_SIZE : nBytesToSend;
2535 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
2537 if (nRC != -1)
2539 nBytesSent += nRC;
2540 nTotalSent += nRC;
2543 /* Do some computation to display the status. */
2544 time(&e_long_time);
2545 nSeconds = e_long_time - s_long_time;
2546 if( nSeconds / 60 > 0 )
2548 TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld min %ld sec estimated remaining time %ld sec\n",
2549 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
2550 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
2552 else
2554 TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld sec estimated remaining time %ld sec\n",
2555 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
2556 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
2558 } while (nRC != -1);
2560 TRACE("file transfer complete!\n");
2562 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2564 return nTotalSent;
2568 /***********************************************************************
2569 * FTP_SendRetrieve (internal)
2571 * Send request to retrieve a file
2573 * RETURNS
2574 * Number of bytes to be received on success
2575 * 0 on failure
2578 static DWORD FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2580 INT nResCode;
2581 DWORD nResult = 0;
2583 TRACE("\n");
2584 if (!FTP_InitListenSocket(lpwfs))
2585 goto lend;
2587 if (!FTP_SendType(lpwfs, dwType))
2588 goto lend;
2590 if (!FTP_SendPortOrPasv(lpwfs))
2591 goto lend;
2593 if (!FTP_GetFileSize(lpwfs, lpszRemoteFile, &nResult))
2594 goto lend;
2596 TRACE("Waiting to receive %ld bytes\n", nResult);
2598 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0))
2599 goto lend;
2601 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2602 if ((nResCode != 125) && (nResCode != 150)) {
2603 /* That means that we got an error getting the file. */
2604 nResult = 0;
2607 lend:
2608 if (0 == nResult && lpwfs->lstnSocket != -1)
2610 closesocket(lpwfs->lstnSocket);
2611 lpwfs->lstnSocket = -1;
2614 return nResult;
2618 /***********************************************************************
2619 * FTP_RetrieveData (internal)
2621 * Retrieve data from server
2623 * RETURNS
2624 * TRUE on success
2625 * FALSE on failure
2628 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile)
2630 DWORD nBytesWritten;
2631 DWORD nBytesReceived = 0;
2632 INT nRC = 0;
2633 CHAR *lpszBuffer;
2635 TRACE("\n");
2637 if (INVALID_HANDLE_VALUE == hFile)
2638 return FALSE;
2640 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2641 if (NULL == lpszBuffer)
2643 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2644 return FALSE;
2647 while (nBytesReceived < nBytes && nRC != -1)
2649 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
2650 if (nRC != -1)
2652 /* other side closed socket. */
2653 if (nRC == 0)
2654 goto recv_end;
2655 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
2656 nBytesReceived += nRC;
2659 TRACE("%ld bytes of %ld (%ld%%)\r", nBytesReceived, nBytes,
2660 nBytesReceived * 100 / nBytes);
2663 TRACE("Data transfer complete\n");
2664 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2666 recv_end:
2667 return (nRC != -1);
2671 /***********************************************************************
2672 * FTP_CloseSessionHandle (internal)
2674 * Deallocate session handle
2676 * RETURNS
2677 * TRUE on success
2678 * FALSE on failure
2681 static void FTP_CloseSessionHandle(LPWININETHANDLEHEADER hdr)
2683 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2685 TRACE("\n");
2687 if (lpwfs->download_in_progress != NULL)
2688 lpwfs->download_in_progress->session_deleted = TRUE;
2690 if (lpwfs->sndSocket != -1)
2691 closesocket(lpwfs->sndSocket);
2693 if (lpwfs->lstnSocket != -1)
2694 closesocket(lpwfs->lstnSocket);
2696 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2697 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2698 HeapFree(GetProcessHeap(), 0, lpwfs);
2702 /***********************************************************************
2703 * FTP_CloseFindNextHandle (internal)
2705 * Deallocate session handle
2707 * RETURNS
2708 * TRUE on success
2709 * FALSE on failure
2712 static void FTP_CloseFindNextHandle(LPWININETHANDLEHEADER hdr)
2714 LPWININETFINDNEXTW lpwfn = (LPWININETFINDNEXTW) hdr;
2715 DWORD i;
2717 TRACE("\n");
2719 for (i = 0; i < lpwfn->size; i++)
2721 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
2724 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
2725 HeapFree(GetProcessHeap(), 0, lpwfn);
2728 /***********************************************************************
2729 * FTP_CloseFileTransferHandle (internal)
2731 * Closes the file transfer handle. This also 'cleans' the data queue of
2732 * the 'transfer conplete' message (this is a bit of a hack though :-/ )
2735 static void FTP_CloseFileTransferHandle(LPWININETHANDLEHEADER hdr)
2737 LPWININETFILE lpwh = (LPWININETFILE) hdr;
2738 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) lpwh->hdr.lpwhparent;
2739 INT nResCode;
2741 TRACE("\n");
2743 if (!lpwh->session_deleted)
2744 lpwfs->download_in_progress = NULL;
2746 /* This just serves to flush the control socket of any spurrious lines written
2747 to it (like '226 Transfer complete.').
2749 Wonder what to do if the server sends us an error code though...
2751 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2753 if (lpwh->nDataSocket != -1)
2754 closesocket(lpwh->nDataSocket);
2756 HeapFree(GetProcessHeap(), 0, lpwh);
2759 /***********************************************************************
2760 * FTP_ReceiveFileList (internal)
2762 * Read file list from server
2764 * RETURNS
2765 * Handle to file list on success
2766 * NULL on failure
2769 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
2770 LPWIN32_FIND_DATAW lpFindFileData, DWORD dwContext)
2772 DWORD dwSize = 0;
2773 LPFILEPROPERTIESW lpafp = NULL;
2774 LPWININETFINDNEXTW lpwfn = NULL;
2775 HINTERNET handle = 0;
2777 TRACE("(%p,%d,%s,%p,%ld)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
2779 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
2781 if(lpFindFileData)
2782 FTP_ConvertFileProp(lpafp, lpFindFileData);
2784 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFINDNEXTW));
2785 if (lpwfn)
2787 lpwfn->hdr.htype = WH_HFINDNEXT;
2788 lpwfn->hdr.lpwhparent = WININET_AddRef( &lpwfs->hdr );
2789 lpwfn->hdr.dwContext = dwContext;
2790 lpwfn->hdr.dwRefCount = 1;
2791 lpwfn->hdr.destroy = FTP_CloseFindNextHandle;
2792 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
2793 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
2794 lpwfn->size = dwSize;
2795 lpwfn->lpafp = lpafp;
2797 handle = WININET_AllocHandle( &lpwfn->hdr );
2801 if( lpwfn )
2802 WININET_Release( &lpwfn->hdr );
2804 TRACE("Matched %ld files\n", dwSize);
2805 return handle;
2809 /***********************************************************************
2810 * FTP_ConvertFileProp (internal)
2812 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
2814 * RETURNS
2815 * TRUE on success
2816 * FALSE on failure
2819 BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
2821 BOOL bSuccess = FALSE;
2823 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
2825 if (lpafp)
2827 /* Convert 'Unix' time to Windows time */
2828 RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
2829 (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
2830 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
2831 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
2833 /* Not all fields are filled in */
2834 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
2835 lpFindFileData->nFileSizeLow = lpafp->nSize;
2837 if (lpafp->bIsDirectory)
2838 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
2840 if (lpafp->lpszName)
2841 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
2843 bSuccess = TRUE;
2846 return bSuccess;
2849 /***********************************************************************
2850 * FTP_ParseNextFile (internal)
2852 * Parse the next line in file listing
2854 * RETURNS
2855 * TRUE on success
2856 * FALSE on failure
2858 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
2860 static const char szSpace[] = " \t";
2861 DWORD nBufLen;
2862 char *pszLine;
2863 char *pszToken;
2864 char *pszTmp;
2865 BOOL found = FALSE;
2866 int i;
2868 lpfp->lpszName = NULL;
2869 do {
2870 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
2871 return FALSE;
2873 pszToken = strtok(pszLine, szSpace);
2874 /* ls format
2875 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
2877 * For instance:
2878 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
2880 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
2881 if(!FTP_ParsePermission(pszToken, lpfp))
2882 lpfp->bIsDirectory = FALSE;
2883 for(i=0; i<=3; i++) {
2884 if(!(pszToken = strtok(NULL, szSpace)))
2885 break;
2887 if(!pszToken) continue;
2888 if(lpfp->bIsDirectory) {
2889 TRACE("Is directory\n");
2890 lpfp->nSize = 0;
2892 else {
2893 TRACE("Size: %s\n", pszToken);
2894 lpfp->nSize = atol(pszToken);
2897 lpfp->tmLastModified.tm_sec = 0;
2898 lpfp->tmLastModified.tm_min = 0;
2899 lpfp->tmLastModified.tm_hour = 0;
2900 lpfp->tmLastModified.tm_mday = 0;
2901 lpfp->tmLastModified.tm_mon = 0;
2902 lpfp->tmLastModified.tm_year = 0;
2904 /* Determine month */
2905 pszToken = strtok(NULL, szSpace);
2906 if(!pszToken) continue;
2907 if(strlen(pszToken) >= 3) {
2908 pszToken[3] = 0;
2909 if((pszTmp = StrStrIA(szMonths, pszToken)))
2910 lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
2912 /* Determine day */
2913 pszToken = strtok(NULL, szSpace);
2914 if(!pszToken) continue;
2915 lpfp->tmLastModified.tm_mday = atoi(pszToken);
2916 /* Determine time or year */
2917 pszToken = strtok(NULL, szSpace);
2918 if(!pszToken) continue;
2919 if((pszTmp = strchr(pszToken, ':'))) {
2920 struct tm* apTM;
2921 time_t aTime;
2922 *pszTmp = 0;
2923 pszTmp++;
2924 lpfp->tmLastModified.tm_min = atoi(pszTmp);
2925 lpfp->tmLastModified.tm_hour = atoi(pszToken);
2926 time(&aTime);
2927 apTM = localtime(&aTime);
2928 lpfp->tmLastModified.tm_year = apTM->tm_year;
2930 else {
2931 lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
2932 lpfp->tmLastModified.tm_hour = 12;
2934 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
2935 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
2936 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
2937 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
2939 pszToken = strtok(NULL, szSpace);
2940 if(!pszToken) continue;
2941 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
2942 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
2944 /* NT way of parsing ... :
2946 07-13-03 08:55PM <DIR> sakpatch
2947 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
2949 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
2950 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
2952 sscanf(pszToken, "%d-%d-%d",
2953 &lpfp->tmLastModified.tm_mon,
2954 &lpfp->tmLastModified.tm_mday,
2955 &lpfp->tmLastModified.tm_year);
2957 /* Hacky and bad Y2K protection :-) */
2958 if (lpfp->tmLastModified.tm_year < 70)
2959 lpfp->tmLastModified.tm_year += 100;
2961 pszToken = strtok(NULL, szSpace);
2962 if(!pszToken) continue;
2963 sscanf(pszToken, "%d:%d",
2964 &lpfp->tmLastModified.tm_hour,
2965 &lpfp->tmLastModified.tm_min);
2966 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
2967 lpfp->tmLastModified.tm_hour += 12;
2969 lpfp->tmLastModified.tm_sec = 0;
2971 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
2972 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
2973 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
2974 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
2976 pszToken = strtok(NULL, szSpace);
2977 if(!pszToken) continue;
2978 if(!strcasecmp(pszToken, "<DIR>")) {
2979 lpfp->bIsDirectory = TRUE;
2980 lpfp->nSize = 0;
2981 TRACE("Is directory\n");
2983 else {
2984 lpfp->bIsDirectory = FALSE;
2985 lpfp->nSize = atol(pszToken);
2986 TRACE("Size: %ld\n", lpfp->nSize);
2989 pszToken = strtok(NULL, szSpace);
2990 if(!pszToken) continue;
2991 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
2992 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
2994 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
2995 else if(pszToken[0] == '+') {
2996 FIXME("EPLF Format not implemented\n");
2999 if(lpfp->lpszName) {
3000 if((lpszSearchFile == NULL) ||
3001 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3002 found = TRUE;
3003 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3005 else {
3006 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3007 lpfp->lpszName = NULL;
3010 } while(!found);
3011 return TRUE;
3014 /***********************************************************************
3015 * FTP_ParseDirectory (internal)
3017 * Parse string of directory information
3019 * RETURNS
3020 * TRUE on success
3021 * FALSE on failure
3023 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3024 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3026 BOOL bSuccess = TRUE;
3027 INT sizeFilePropArray = 500;/*20; */
3028 INT indexFilePropArray = -1;
3030 TRACE("\n");
3032 /* Allocate intial file properties array */
3033 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3034 if (!*lpafp)
3035 return FALSE;
3037 do {
3038 if (indexFilePropArray+1 >= sizeFilePropArray)
3040 LPFILEPROPERTIESW tmpafp;
3042 sizeFilePropArray *= 2;
3043 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3044 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3045 if (NULL == tmpafp)
3047 bSuccess = FALSE;
3048 break;
3051 *lpafp = tmpafp;
3053 indexFilePropArray++;
3054 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3056 if (bSuccess && indexFilePropArray)
3058 if (indexFilePropArray < sizeFilePropArray - 1)
3060 LPFILEPROPERTIESW tmpafp;
3062 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3063 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3064 if (NULL == tmpafp)
3065 *lpafp = tmpafp;
3067 *dwfp = indexFilePropArray;
3069 else
3071 HeapFree(GetProcessHeap(), 0, *lpafp);
3072 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3073 bSuccess = FALSE;
3076 return bSuccess;
3080 /***********************************************************************
3081 * FTP_ParsePermission (internal)
3083 * Parse permission string of directory information
3085 * RETURNS
3086 * TRUE on success
3087 * FALSE on failure
3090 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3092 BOOL bSuccess = TRUE;
3093 unsigned short nPermission = 0;
3094 INT nPos = 1;
3095 INT nLast = 9;
3097 TRACE("\n");
3098 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3100 bSuccess = FALSE;
3101 return bSuccess;
3104 lpfp->bIsDirectory = (*lpszPermission == 'd');
3107 switch (nPos)
3109 case 1:
3110 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3111 break;
3112 case 2:
3113 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3114 break;
3115 case 3:
3116 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3117 break;
3118 case 4:
3119 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3120 break;
3121 case 5:
3122 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3123 break;
3124 case 6:
3125 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3126 break;
3127 case 7:
3128 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3129 break;
3130 case 8:
3131 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3132 break;
3133 case 9:
3134 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3135 break;
3137 nPos++;
3138 }while (nPos <= nLast);
3140 lpfp->permissions = nPermission;
3141 return bSuccess;
3145 /***********************************************************************
3146 * FTP_SetResponseError (internal)
3148 * Set the appropriate error code for a given response from the server
3150 * RETURNS
3153 static DWORD FTP_SetResponseError(DWORD dwResponse)
3155 DWORD dwCode = 0;
3157 switch(dwResponse)
3159 case 421: /* Service not available - Server may be shutting down. */
3160 dwCode = ERROR_INTERNET_TIMEOUT;
3161 break;
3163 case 425: /* Cannot open data connection. */
3164 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3165 break;
3167 case 426: /* Connection closed, transer aborted. */
3168 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3169 break;
3171 case 500: /* Syntax error. Command unrecognized. */
3172 case 501: /* Syntax error. Error in parameters or arguments. */
3173 dwCode = ERROR_INTERNET_INCORRECT_FORMAT;
3174 break;
3176 case 530: /* Not logged in. Login incorrect. */
3177 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3178 break;
3180 case 550: /* File action not taken. File not found or no access. */
3181 dwCode = ERROR_INTERNET_ITEM_NOT_FOUND;
3182 break;
3184 case 450: /* File action not taken. File may be busy. */
3185 case 451: /* Action aborted. Server error. */
3186 case 452: /* Action not taken. Insufficient storage space on server. */
3187 case 502: /* Command not implemented. */
3188 case 503: /* Bad sequence of command. */
3189 case 504: /* Command not implemented for that parameter. */
3190 case 532: /* Need account for storing files */
3191 case 551: /* Requested action aborted. Page type unknown */
3192 case 552: /* Action aborted. Exceeded storage allocation */
3193 case 553: /* Action not taken. File name not allowed. */
3195 default:
3196 dwCode = ERROR_INTERNET_INTERNAL_ERROR;
3197 break;
3200 INTERNET_SetLastError(dwCode);
3201 return dwCode;