wininet: Call SendCallback in destructor instead of InternetCloseHandle.
[wine/testsucceed.git] / dlls / wininet / ftp.c
blobded0155e103d0343a06caa66ca668a0055779969
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 HINTERNET hHandle = WININET_FindHandle( hdr );
1928 if( hHandle )
1930 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
1931 WININET_Release( hdr );
1935 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
1936 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
1937 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
1939 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1940 return FALSE;
1942 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
1943 dwParamLen ? lpszParam : "", szCRLF);
1945 TRACE("Sending (%s) len(%d)\n", buf, len);
1946 while((nBytesSent < len) && (nRC != -1))
1948 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
1949 nBytesSent += nRC;
1952 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
1954 if (lpfnStatusCB)
1956 HINTERNET hHandle = WININET_FindHandle( hdr );
1957 if( hHandle )
1959 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_REQUEST_SENT,
1960 &nBytesSent, sizeof(DWORD));
1961 WININET_Release( hdr );
1965 TRACE("Sent %d bytes\n", nBytesSent);
1966 return (nRC != -1);
1969 /***********************************************************************
1970 * FTP_SendCommand (internal)
1972 * Send command to server
1974 * RETURNS
1975 * TRUE on success
1976 * NULL on failure
1979 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
1980 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext)
1982 BOOL ret;
1983 LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
1984 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
1985 HeapFree(GetProcessHeap(), 0, lpszParamA);
1986 return ret;
1989 /***********************************************************************
1990 * FTP_ReceiveResponse (internal)
1992 * Receive response from server
1994 * RETURNS
1995 * Reply code on success
1996 * 0 on failure
1999 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD dwContext)
2001 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2002 DWORD nRecv;
2003 INT rc = 0;
2004 char firstprefix[5];
2005 BOOL multiline = FALSE;
2006 LPWININETAPPINFOW hIC = NULL;
2008 TRACE("socket(%d)\n", lpwfs->sndSocket);
2010 hIC = lpwfs->lpAppInfo;
2011 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2013 while(1)
2015 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2016 goto lerror;
2018 if (nRecv >= 3)
2020 if(!multiline)
2022 if(lpszResponse[3] != '-')
2023 break;
2024 else
2025 { /* Start of multiline repsonse. Loop until we get "nnn " */
2026 multiline = TRUE;
2027 memcpy(firstprefix, lpszResponse, 3);
2028 firstprefix[3] = ' ';
2029 firstprefix[4] = '\0';
2032 else
2034 if(!memcmp(firstprefix, lpszResponse, 4))
2035 break;
2040 if (nRecv >= 3)
2042 rc = atoi(lpszResponse);
2044 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2045 &nRecv, sizeof(DWORD));
2048 lerror:
2049 TRACE("return %d\n", rc);
2050 return rc;
2054 /***********************************************************************
2055 * FTP_SendPassword (internal)
2057 * Send password to ftp server
2059 * RETURNS
2060 * TRUE on success
2061 * NULL on failure
2064 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2066 INT nResCode;
2067 BOOL bSuccess = FALSE;
2069 TRACE("\n");
2070 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2071 goto lend;
2073 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2074 if (nResCode)
2076 TRACE("Received reply code %d\n", nResCode);
2077 /* Login successful... */
2078 if (nResCode == 230)
2079 bSuccess = TRUE;
2080 /* Command not implemented, superfluous at the server site... */
2081 /* Need account for login... */
2082 else if (nResCode == 332)
2083 bSuccess = FTP_SendAccount(lpwfs);
2084 else
2085 FTP_SetResponseError(nResCode);
2088 lend:
2089 TRACE("Returning %d\n", bSuccess);
2090 return bSuccess;
2094 /***********************************************************************
2095 * FTP_SendAccount (internal)
2099 * RETURNS
2100 * TRUE on success
2101 * FALSE on failure
2104 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2106 INT nResCode;
2107 BOOL bSuccess = FALSE;
2109 TRACE("\n");
2110 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2111 goto lend;
2113 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2114 if (nResCode)
2115 bSuccess = TRUE;
2116 else
2117 FTP_SetResponseError(nResCode);
2119 lend:
2120 return bSuccess;
2124 /***********************************************************************
2125 * FTP_SendStore (internal)
2127 * Send request to upload file to ftp server
2129 * RETURNS
2130 * TRUE on success
2131 * FALSE on failure
2134 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2136 INT nResCode;
2137 BOOL bSuccess = FALSE;
2139 TRACE("\n");
2140 if (!FTP_InitListenSocket(lpwfs))
2141 goto lend;
2143 if (!FTP_SendType(lpwfs, dwType))
2144 goto lend;
2146 if (!FTP_SendPortOrPasv(lpwfs))
2147 goto lend;
2149 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2150 goto lend;
2151 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2152 if (nResCode)
2154 if (nResCode == 150 || nResCode == 125)
2155 bSuccess = TRUE;
2156 else
2157 FTP_SetResponseError(nResCode);
2160 lend:
2161 if (!bSuccess && lpwfs->lstnSocket != -1)
2163 closesocket(lpwfs->lstnSocket);
2164 lpwfs->lstnSocket = -1;
2167 return bSuccess;
2171 /***********************************************************************
2172 * FTP_InitListenSocket (internal)
2174 * Create a socket to listen for server response
2176 * RETURNS
2177 * TRUE on success
2178 * FALSE on failure
2181 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2183 BOOL bSuccess = FALSE;
2184 size_t namelen = sizeof(struct sockaddr_in);
2186 TRACE("\n");
2188 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2189 if (lpwfs->lstnSocket == -1)
2191 TRACE("Unable to create listening socket\n");
2192 goto lend;
2195 /* We obtain our ip addr from the name of the command channel socket */
2196 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2198 /* and get the system to assign us a port */
2199 lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
2201 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2203 TRACE("Unable to bind socket\n");
2204 goto lend;
2207 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2209 TRACE("listen failed\n");
2210 goto lend;
2213 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2214 bSuccess = TRUE;
2216 lend:
2217 if (!bSuccess && lpwfs->lstnSocket == -1)
2219 closesocket(lpwfs->lstnSocket);
2220 lpwfs->lstnSocket = -1;
2223 return bSuccess;
2227 /***********************************************************************
2228 * FTP_SendType (internal)
2230 * Tell server type of data being transferred
2232 * RETURNS
2233 * TRUE on success
2234 * FALSE on failure
2236 * W98SE doesn't cache the type that's currently set
2237 * (i.e. it sends it always),
2238 * so we probably don't want to do that either.
2240 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2242 INT nResCode;
2243 WCHAR type[] = { 'I','\0' };
2244 BOOL bSuccess = FALSE;
2246 TRACE("\n");
2247 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2248 type[0] = 'A';
2250 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2251 goto lend;
2253 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2254 if (nResCode)
2256 if (nResCode == 2)
2257 bSuccess = TRUE;
2258 else
2259 FTP_SetResponseError(nResCode);
2262 lend:
2263 return bSuccess;
2266 /***********************************************************************
2267 * FTP_GetFileSize (internal)
2269 * Retrieves from the server the size of the given file
2271 * RETURNS
2272 * TRUE on success
2273 * FALSE on failure
2276 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2278 INT nResCode;
2279 BOOL bSuccess = FALSE;
2281 TRACE("\n");
2283 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2284 goto lend;
2286 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2287 if (nResCode)
2289 if (nResCode == 213) {
2290 /* Now parses the output to get the actual file size */
2291 int i;
2292 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2294 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2295 if (lpszResponseBuffer[i] == '\0') return FALSE;
2296 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2298 bSuccess = TRUE;
2299 } else {
2300 FTP_SetResponseError(nResCode);
2304 lend:
2305 return bSuccess;
2309 /***********************************************************************
2310 * FTP_SendPort (internal)
2312 * Tell server which port to use
2314 * RETURNS
2315 * TRUE on success
2316 * FALSE on failure
2319 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2321 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2322 INT nResCode;
2323 WCHAR szIPAddress[64];
2324 BOOL bSuccess = FALSE;
2325 TRACE("\n");
2327 sprintfW(szIPAddress, szIPFormat,
2328 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2329 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2330 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2331 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2332 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2333 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2335 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2336 goto lend;
2338 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2339 if (nResCode)
2341 if (nResCode == 200)
2342 bSuccess = TRUE;
2343 else
2344 FTP_SetResponseError(nResCode);
2347 lend:
2348 return bSuccess;
2352 /***********************************************************************
2353 * FTP_DoPassive (internal)
2355 * Tell server that we want to do passive transfers
2356 * and connect data socket
2358 * RETURNS
2359 * TRUE on success
2360 * FALSE on failure
2363 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2365 INT nResCode;
2366 BOOL bSuccess = FALSE;
2368 TRACE("\n");
2369 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2370 goto lend;
2372 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2373 if (nResCode)
2375 if (nResCode == 227)
2377 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2378 LPSTR p;
2379 int f[6];
2380 int i;
2381 char *pAddr, *pPort;
2382 INT nsocket = -1;
2383 struct sockaddr_in dataSocketAddress;
2385 p = lpszResponseBuffer+4; /* skip status code */
2387 /* do a very strict check; we can improve that later. */
2389 if (strncmp(p, "Entering Passive Mode", 21))
2391 ERR("unknown response '%.*s', aborting\n", 21, p);
2392 goto lend;
2394 p += 21; /* skip string */
2395 if ((*p++ != ' ') || (*p++ != '('))
2397 ERR("unknown response format, aborting\n");
2398 goto lend;
2401 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
2402 &f[4], &f[5]) != 6)
2404 ERR("unknown response address format '%s', aborting\n", p);
2405 goto lend;
2407 for (i=0; i < 6; i++)
2408 f[i] = f[i] & 0xff;
2410 dataSocketAddress = lpwfs->socketAddress;
2411 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2412 pPort = (char *)&(dataSocketAddress.sin_port);
2413 pAddr[0] = f[0];
2414 pAddr[1] = f[1];
2415 pAddr[2] = f[2];
2416 pAddr[3] = f[3];
2417 pPort[0] = f[4];
2418 pPort[1] = f[5];
2420 nsocket = socket(AF_INET,SOCK_STREAM,0);
2421 if (nsocket == -1)
2422 goto lend;
2424 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2426 ERR("can't connect passive FTP data port.\n");
2427 closesocket(nsocket);
2428 goto lend;
2430 lpwfs->pasvSocket = nsocket;
2431 bSuccess = TRUE;
2433 else
2434 FTP_SetResponseError(nResCode);
2437 lend:
2438 return bSuccess;
2442 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2444 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2446 if (!FTP_DoPassive(lpwfs))
2447 return FALSE;
2449 else
2451 if (!FTP_SendPort(lpwfs))
2452 return FALSE;
2454 return TRUE;
2458 /***********************************************************************
2459 * FTP_GetDataSocket (internal)
2461 * Either accepts an incoming data socket connection from the server
2462 * or just returns the already opened socket after a PASV command
2463 * in case of passive FTP.
2466 * RETURNS
2467 * TRUE on success
2468 * FALSE on failure
2471 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
2473 struct sockaddr_in saddr;
2474 size_t addrlen = sizeof(struct sockaddr);
2476 TRACE("\n");
2477 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2479 *nDataSocket = lpwfs->pasvSocket;
2481 else
2483 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
2484 closesocket(lpwfs->lstnSocket);
2485 lpwfs->lstnSocket = -1;
2487 return *nDataSocket != -1;
2491 /***********************************************************************
2492 * FTP_SendData (internal)
2494 * Send data to the server
2496 * RETURNS
2497 * TRUE on success
2498 * FALSE on failure
2501 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
2503 BY_HANDLE_FILE_INFORMATION fi;
2504 DWORD nBytesRead = 0;
2505 DWORD nBytesSent = 0;
2506 DWORD nTotalSent = 0;
2507 DWORD nBytesToSend, nLen;
2508 int nRC = 1;
2509 time_t s_long_time, e_long_time;
2510 LONG nSeconds;
2511 CHAR *lpszBuffer;
2513 TRACE("\n");
2514 lpszBuffer = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR)*DATA_PACKET_SIZE);
2515 memset(lpszBuffer, 0, sizeof(CHAR)*DATA_PACKET_SIZE);
2517 /* Get the size of the file. */
2518 GetFileInformationByHandle(hFile, &fi);
2519 time(&s_long_time);
2523 nBytesToSend = nBytesRead - nBytesSent;
2525 if (nBytesToSend <= 0)
2527 /* Read data from file. */
2528 nBytesSent = 0;
2529 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
2530 ERR("Failed reading from file\n");
2532 if (nBytesRead > 0)
2533 nBytesToSend = nBytesRead;
2534 else
2535 break;
2538 nLen = DATA_PACKET_SIZE < nBytesToSend ?
2539 DATA_PACKET_SIZE : nBytesToSend;
2540 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
2542 if (nRC != -1)
2544 nBytesSent += nRC;
2545 nTotalSent += nRC;
2548 /* Do some computation to display the status. */
2549 time(&e_long_time);
2550 nSeconds = e_long_time - s_long_time;
2551 if( nSeconds / 60 > 0 )
2553 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
2554 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
2555 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
2557 else
2559 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
2560 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
2561 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
2563 } while (nRC != -1);
2565 TRACE("file transfer complete!\n");
2567 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2569 return nTotalSent;
2573 /***********************************************************************
2574 * FTP_SendRetrieve (internal)
2576 * Send request to retrieve a file
2578 * RETURNS
2579 * Number of bytes to be received on success
2580 * 0 on failure
2583 static DWORD FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2585 INT nResCode;
2586 DWORD nResult = 0;
2588 TRACE("\n");
2589 if (!FTP_InitListenSocket(lpwfs))
2590 goto lend;
2592 if (!FTP_SendType(lpwfs, dwType))
2593 goto lend;
2595 if (!FTP_SendPortOrPasv(lpwfs))
2596 goto lend;
2598 if (!FTP_GetFileSize(lpwfs, lpszRemoteFile, &nResult))
2599 goto lend;
2601 TRACE("Waiting to receive %d bytes\n", nResult);
2603 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0))
2604 goto lend;
2606 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2607 if ((nResCode != 125) && (nResCode != 150)) {
2608 /* That means that we got an error getting the file. */
2609 nResult = 0;
2612 lend:
2613 if (0 == nResult && lpwfs->lstnSocket != -1)
2615 closesocket(lpwfs->lstnSocket);
2616 lpwfs->lstnSocket = -1;
2619 return nResult;
2623 /***********************************************************************
2624 * FTP_RetrieveData (internal)
2626 * Retrieve data from server
2628 * RETURNS
2629 * TRUE on success
2630 * FALSE on failure
2633 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile)
2635 DWORD nBytesWritten;
2636 DWORD nBytesReceived = 0;
2637 INT nRC = 0;
2638 CHAR *lpszBuffer;
2640 TRACE("\n");
2642 if (INVALID_HANDLE_VALUE == hFile)
2643 return FALSE;
2645 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2646 if (NULL == lpszBuffer)
2648 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2649 return FALSE;
2652 while (nBytesReceived < nBytes && nRC != -1)
2654 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
2655 if (nRC != -1)
2657 /* other side closed socket. */
2658 if (nRC == 0)
2659 goto recv_end;
2660 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
2661 nBytesReceived += nRC;
2664 TRACE("%d bytes of %d (%d%%)\r", nBytesReceived, nBytes,
2665 nBytesReceived * 100 / nBytes);
2668 TRACE("Data transfer complete\n");
2669 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2671 recv_end:
2672 return (nRC != -1);
2676 /***********************************************************************
2677 * FTP_CloseSessionHandle (internal)
2679 * Deallocate session handle
2681 * RETURNS
2682 * TRUE on success
2683 * FALSE on failure
2686 static void FTP_CloseSessionHandle(LPWININETHANDLEHEADER hdr)
2688 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2690 TRACE("\n");
2692 INTERNET_SendCallback(hdr, hdr->dwContext,
2693 INTERNET_STATUS_HANDLE_CLOSING, &hdr->hInternet,
2694 sizeof(HINTERNET));
2696 WININET_Release(&lpwfs->lpAppInfo->hdr);
2698 if (lpwfs->download_in_progress != NULL)
2699 lpwfs->download_in_progress->session_deleted = TRUE;
2701 if (lpwfs->sndSocket != -1)
2702 closesocket(lpwfs->sndSocket);
2704 if (lpwfs->lstnSocket != -1)
2705 closesocket(lpwfs->lstnSocket);
2707 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2708 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2709 HeapFree(GetProcessHeap(), 0, lpwfs);
2713 /***********************************************************************
2714 * FTP_FindNextFileW (Internal)
2716 * Continues a file search from a previous call to FindFirstFile
2718 * RETURNS
2719 * TRUE on success
2720 * FALSE on failure
2723 BOOL WINAPI FTP_FindNextFileW(LPWININETFTPFINDNEXTW lpwh, LPVOID lpvFindData)
2725 BOOL bSuccess = TRUE;
2726 LPWIN32_FIND_DATAW lpFindFileData;
2728 TRACE("index(%d) size(%d)\n", lpwh->index, lpwh->size);
2730 assert (lpwh->hdr.htype == WH_HFTPFINDNEXT);
2732 /* Clear any error information */
2733 INTERNET_SetLastError(0);
2735 lpFindFileData = (LPWIN32_FIND_DATAW) lpvFindData;
2736 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
2738 if (lpwh->index >= lpwh->size)
2740 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
2741 bSuccess = FALSE;
2742 goto lend;
2745 FTP_ConvertFileProp(&lpwh->lpafp[lpwh->index], lpFindFileData);
2746 lpwh->index++;
2748 TRACE("\nName: %s\nSize: %d\n", debugstr_w(lpFindFileData->cFileName), lpFindFileData->nFileSizeLow);
2750 lend:
2752 if (lpwh->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2754 INTERNET_ASYNC_RESULT iar;
2756 iar.dwResult = (DWORD)bSuccess;
2757 iar.dwError = iar.dwError = bSuccess ? ERROR_SUCCESS :
2758 INTERNET_GetLastError();
2760 INTERNET_SendCallback(&lpwh->hdr, lpwh->hdr.dwContext,
2761 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2762 sizeof(INTERNET_ASYNC_RESULT));
2765 return bSuccess;
2769 /***********************************************************************
2770 * FTP_CloseFindNextHandle (internal)
2772 * Deallocate session handle
2774 * RETURNS
2775 * TRUE on success
2776 * FALSE on failure
2779 static void FTP_CloseFindNextHandle(LPWININETHANDLEHEADER hdr)
2781 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
2782 DWORD i;
2784 TRACE("\n");
2786 INTERNET_SendCallback(hdr, hdr->dwContext,
2787 INTERNET_STATUS_HANDLE_CLOSING, &hdr->hInternet,
2788 sizeof(HINTERNET));
2790 WININET_Release(&lpwfn->lpFtpSession->hdr);
2792 for (i = 0; i < lpwfn->size; i++)
2794 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
2797 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
2798 HeapFree(GetProcessHeap(), 0, lpwfn);
2801 /***********************************************************************
2802 * FTP_CloseFileTransferHandle (internal)
2804 * Closes the file transfer handle. This also 'cleans' the data queue of
2805 * the 'transfer conplete' message (this is a bit of a hack though :-/ )
2808 static void FTP_CloseFileTransferHandle(LPWININETHANDLEHEADER hdr)
2810 LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
2811 LPWININETFTPSESSIONW lpwfs = lpwh->lpFtpSession;
2812 INT nResCode;
2814 TRACE("\n");
2816 INTERNET_SendCallback(hdr, hdr->dwContext,
2817 INTERNET_STATUS_HANDLE_CLOSING, &hdr->hInternet,
2818 sizeof(HINTERNET));
2820 WININET_Release(&lpwh->lpFtpSession->hdr);
2822 if (!lpwh->session_deleted)
2823 lpwfs->download_in_progress = NULL;
2825 /* This just serves to flush the control socket of any spurrious lines written
2826 to it (like '226 Transfer complete.').
2828 Wonder what to do if the server sends us an error code though...
2830 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2832 if (lpwh->nDataSocket != -1)
2833 closesocket(lpwh->nDataSocket);
2835 HeapFree(GetProcessHeap(), 0, lpwh);
2838 /***********************************************************************
2839 * FTP_ReceiveFileList (internal)
2841 * Read file list from server
2843 * RETURNS
2844 * Handle to file list on success
2845 * NULL on failure
2848 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
2849 LPWIN32_FIND_DATAW lpFindFileData, DWORD dwContext)
2851 DWORD dwSize = 0;
2852 LPFILEPROPERTIESW lpafp = NULL;
2853 LPWININETFTPFINDNEXTW lpwfn = NULL;
2854 HINTERNET handle = 0;
2856 TRACE("(%p,%d,%s,%p,%d)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
2858 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
2860 if(lpFindFileData)
2861 FTP_ConvertFileProp(lpafp, lpFindFileData);
2863 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
2864 if (lpwfn)
2866 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
2867 lpwfn->hdr.dwContext = dwContext;
2868 lpwfn->hdr.dwRefCount = 1;
2869 lpwfn->hdr.destroy = FTP_CloseFindNextHandle;
2870 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
2871 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
2872 lpwfn->size = dwSize;
2873 lpwfn->lpafp = lpafp;
2875 WININET_AddRef( &lpwfs->hdr );
2876 lpwfn->lpFtpSession = lpwfs;
2878 handle = WININET_AllocHandle( &lpwfn->hdr );
2882 if( lpwfn )
2883 WININET_Release( &lpwfn->hdr );
2885 TRACE("Matched %d files\n", dwSize);
2886 return handle;
2890 /***********************************************************************
2891 * FTP_ConvertFileProp (internal)
2893 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
2895 * RETURNS
2896 * TRUE on success
2897 * FALSE on failure
2900 BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
2902 BOOL bSuccess = FALSE;
2904 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
2906 if (lpafp)
2908 /* Convert 'Unix' time to Windows time */
2909 RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
2910 (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
2911 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
2912 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
2914 /* Not all fields are filled in */
2915 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
2916 lpFindFileData->nFileSizeLow = lpafp->nSize;
2918 if (lpafp->bIsDirectory)
2919 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
2921 if (lpafp->lpszName)
2922 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
2924 bSuccess = TRUE;
2927 return bSuccess;
2930 /***********************************************************************
2931 * FTP_ParseNextFile (internal)
2933 * Parse the next line in file listing
2935 * RETURNS
2936 * TRUE on success
2937 * FALSE on failure
2939 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
2941 static const char szSpace[] = " \t";
2942 DWORD nBufLen;
2943 char *pszLine;
2944 char *pszToken;
2945 char *pszTmp;
2946 BOOL found = FALSE;
2947 int i;
2949 lpfp->lpszName = NULL;
2950 do {
2951 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
2952 return FALSE;
2954 pszToken = strtok(pszLine, szSpace);
2955 /* ls format
2956 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
2958 * For instance:
2959 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
2961 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
2962 if(!FTP_ParsePermission(pszToken, lpfp))
2963 lpfp->bIsDirectory = FALSE;
2964 for(i=0; i<=3; i++) {
2965 if(!(pszToken = strtok(NULL, szSpace)))
2966 break;
2968 if(!pszToken) continue;
2969 if(lpfp->bIsDirectory) {
2970 TRACE("Is directory\n");
2971 lpfp->nSize = 0;
2973 else {
2974 TRACE("Size: %s\n", pszToken);
2975 lpfp->nSize = atol(pszToken);
2978 lpfp->tmLastModified.tm_sec = 0;
2979 lpfp->tmLastModified.tm_min = 0;
2980 lpfp->tmLastModified.tm_hour = 0;
2981 lpfp->tmLastModified.tm_mday = 0;
2982 lpfp->tmLastModified.tm_mon = 0;
2983 lpfp->tmLastModified.tm_year = 0;
2985 /* Determine month */
2986 pszToken = strtok(NULL, szSpace);
2987 if(!pszToken) continue;
2988 if(strlen(pszToken) >= 3) {
2989 pszToken[3] = 0;
2990 if((pszTmp = StrStrIA(szMonths, pszToken)))
2991 lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
2993 /* Determine day */
2994 pszToken = strtok(NULL, szSpace);
2995 if(!pszToken) continue;
2996 lpfp->tmLastModified.tm_mday = atoi(pszToken);
2997 /* Determine time or year */
2998 pszToken = strtok(NULL, szSpace);
2999 if(!pszToken) continue;
3000 if((pszTmp = strchr(pszToken, ':'))) {
3001 struct tm* apTM;
3002 time_t aTime;
3003 *pszTmp = 0;
3004 pszTmp++;
3005 lpfp->tmLastModified.tm_min = atoi(pszTmp);
3006 lpfp->tmLastModified.tm_hour = atoi(pszToken);
3007 time(&aTime);
3008 apTM = localtime(&aTime);
3009 lpfp->tmLastModified.tm_year = apTM->tm_year;
3011 else {
3012 lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
3013 lpfp->tmLastModified.tm_hour = 12;
3015 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3016 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3017 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3018 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3020 pszToken = strtok(NULL, szSpace);
3021 if(!pszToken) continue;
3022 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3023 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3025 /* NT way of parsing ... :
3027 07-13-03 08:55PM <DIR> sakpatch
3028 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3030 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3031 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3033 sscanf(pszToken, "%d-%d-%d",
3034 &lpfp->tmLastModified.tm_mon,
3035 &lpfp->tmLastModified.tm_mday,
3036 &lpfp->tmLastModified.tm_year);
3038 /* Hacky and bad Y2K protection :-) */
3039 if (lpfp->tmLastModified.tm_year < 70)
3040 lpfp->tmLastModified.tm_year += 100;
3042 pszToken = strtok(NULL, szSpace);
3043 if(!pszToken) continue;
3044 sscanf(pszToken, "%d:%d",
3045 &lpfp->tmLastModified.tm_hour,
3046 &lpfp->tmLastModified.tm_min);
3047 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3048 lpfp->tmLastModified.tm_hour += 12;
3050 lpfp->tmLastModified.tm_sec = 0;
3052 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3053 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3054 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3055 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3057 pszToken = strtok(NULL, szSpace);
3058 if(!pszToken) continue;
3059 if(!strcasecmp(pszToken, "<DIR>")) {
3060 lpfp->bIsDirectory = TRUE;
3061 lpfp->nSize = 0;
3062 TRACE("Is directory\n");
3064 else {
3065 lpfp->bIsDirectory = FALSE;
3066 lpfp->nSize = atol(pszToken);
3067 TRACE("Size: %d\n", lpfp->nSize);
3070 pszToken = strtok(NULL, szSpace);
3071 if(!pszToken) continue;
3072 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3073 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3075 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3076 else if(pszToken[0] == '+') {
3077 FIXME("EPLF Format not implemented\n");
3080 if(lpfp->lpszName) {
3081 if((lpszSearchFile == NULL) ||
3082 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3083 found = TRUE;
3084 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3086 else {
3087 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3088 lpfp->lpszName = NULL;
3091 } while(!found);
3092 return TRUE;
3095 /***********************************************************************
3096 * FTP_ParseDirectory (internal)
3098 * Parse string of directory information
3100 * RETURNS
3101 * TRUE on success
3102 * FALSE on failure
3104 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3105 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3107 BOOL bSuccess = TRUE;
3108 INT sizeFilePropArray = 500;/*20; */
3109 INT indexFilePropArray = -1;
3111 TRACE("\n");
3113 /* Allocate intial file properties array */
3114 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3115 if (!*lpafp)
3116 return FALSE;
3118 do {
3119 if (indexFilePropArray+1 >= sizeFilePropArray)
3121 LPFILEPROPERTIESW tmpafp;
3123 sizeFilePropArray *= 2;
3124 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3125 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3126 if (NULL == tmpafp)
3128 bSuccess = FALSE;
3129 break;
3132 *lpafp = tmpafp;
3134 indexFilePropArray++;
3135 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3137 if (bSuccess && indexFilePropArray)
3139 if (indexFilePropArray < sizeFilePropArray - 1)
3141 LPFILEPROPERTIESW tmpafp;
3143 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3144 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3145 if (NULL == tmpafp)
3146 *lpafp = tmpafp;
3148 *dwfp = indexFilePropArray;
3150 else
3152 HeapFree(GetProcessHeap(), 0, *lpafp);
3153 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3154 bSuccess = FALSE;
3157 return bSuccess;
3161 /***********************************************************************
3162 * FTP_ParsePermission (internal)
3164 * Parse permission string of directory information
3166 * RETURNS
3167 * TRUE on success
3168 * FALSE on failure
3171 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3173 BOOL bSuccess = TRUE;
3174 unsigned short nPermission = 0;
3175 INT nPos = 1;
3176 INT nLast = 9;
3178 TRACE("\n");
3179 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3181 bSuccess = FALSE;
3182 return bSuccess;
3185 lpfp->bIsDirectory = (*lpszPermission == 'd');
3188 switch (nPos)
3190 case 1:
3191 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3192 break;
3193 case 2:
3194 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3195 break;
3196 case 3:
3197 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3198 break;
3199 case 4:
3200 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3201 break;
3202 case 5:
3203 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3204 break;
3205 case 6:
3206 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3207 break;
3208 case 7:
3209 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3210 break;
3211 case 8:
3212 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3213 break;
3214 case 9:
3215 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3216 break;
3218 nPos++;
3219 }while (nPos <= nLast);
3221 lpfp->permissions = nPermission;
3222 return bSuccess;
3226 /***********************************************************************
3227 * FTP_SetResponseError (internal)
3229 * Set the appropriate error code for a given response from the server
3231 * RETURNS
3234 static DWORD FTP_SetResponseError(DWORD dwResponse)
3236 DWORD dwCode = 0;
3238 switch(dwResponse)
3240 case 421: /* Service not available - Server may be shutting down. */
3241 dwCode = ERROR_INTERNET_TIMEOUT;
3242 break;
3244 case 425: /* Cannot open data connection. */
3245 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3246 break;
3248 case 426: /* Connection closed, transer aborted. */
3249 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3250 break;
3252 case 500: /* Syntax error. Command unrecognized. */
3253 case 501: /* Syntax error. Error in parameters or arguments. */
3254 dwCode = ERROR_INTERNET_INCORRECT_FORMAT;
3255 break;
3257 case 530: /* Not logged in. Login incorrect. */
3258 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3259 break;
3261 case 550: /* File action not taken. File not found or no access. */
3262 dwCode = ERROR_INTERNET_ITEM_NOT_FOUND;
3263 break;
3265 case 450: /* File action not taken. File may be busy. */
3266 case 451: /* Action aborted. Server error. */
3267 case 452: /* Action not taken. Insufficient storage space on server. */
3268 case 502: /* Command not implemented. */
3269 case 503: /* Bad sequence of command. */
3270 case 504: /* Command not implemented for that parameter. */
3271 case 532: /* Need account for storing files */
3272 case 551: /* Requested action aborted. Page type unknown */
3273 case 552: /* Action aborted. Exceeded storage allocation */
3274 case 553: /* Action not taken. File name not allowed. */
3276 default:
3277 dwCode = ERROR_INTERNET_INTERNAL_ERROR;
3278 break;
3281 INTERNET_SetLastError(dwCode);
3282 return dwCode;