wininet/ftp: Remove unneeded (double) checks.
[wine/gsoc_dplay.git] / dlls / wininet / ftp.c
blob2f8483864f6f76e43e87e865c3aa79cac1f597dd
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 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
185 struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
186 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
188 TRACE("%p\n", lpwfs);
190 FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
191 req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
193 HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
194 HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
197 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
198 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
200 LPWININETFTPSESSIONW lpwfs;
201 LPWININETAPPINFOW hIC = NULL;
202 BOOL r = FALSE;
204 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
205 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
207 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
208 goto lend;
211 hIC = lpwfs->lpAppInfo;
212 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
214 WORKREQUEST workRequest;
215 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
217 workRequest.asyncproc = AsyncFtpPutFileProc;
218 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
219 req->lpszLocalFile = WININET_strdupW(lpszLocalFile);
220 req->lpszNewRemoteFile = WININET_strdupW(lpszNewRemoteFile);
221 req->dwFlags = dwFlags;
222 req->dwContext = dwContext;
224 r = INTERNET_AsyncCall(&workRequest);
226 else
228 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
229 lpszNewRemoteFile, dwFlags, dwContext);
232 lend:
233 if( lpwfs )
234 WININET_Release( &lpwfs->hdr );
236 return r;
239 /***********************************************************************
240 * FTP_FtpPutFileW (Internal)
242 * Uploads a file to the FTP server
244 * RETURNS
245 * TRUE on success
246 * FALSE on failure
249 BOOL WINAPI FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
250 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
252 HANDLE hFile = NULL;
253 BOOL bSuccess = FALSE;
254 LPWININETAPPINFOW hIC = NULL;
255 INT nResCode;
257 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
259 if (!lpszLocalFile || !lpszNewRemoteFile)
261 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
262 return FALSE;
265 /* Clear any error information */
266 INTERNET_SetLastError(0);
267 hIC = lpwfs->lpAppInfo;
269 /* Open file to be uploaded */
270 if (INVALID_HANDLE_VALUE ==
271 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
273 INTERNET_SetLastError(ERROR_FILE_NOT_FOUND);
274 goto lend;
277 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
279 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
281 INT nDataSocket;
283 /* Get data socket to server */
284 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
286 FTP_SendData(lpwfs, nDataSocket, hFile);
287 closesocket(nDataSocket);
288 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
289 if (nResCode)
291 if (nResCode == 226)
292 bSuccess = TRUE;
293 else
294 FTP_SetResponseError(nResCode);
299 lend:
300 if (lpwfs->lstnSocket != -1)
301 closesocket(lpwfs->lstnSocket);
303 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
305 INTERNET_ASYNC_RESULT iar;
307 iar.dwResult = (DWORD)bSuccess;
308 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
309 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
310 &iar, sizeof(INTERNET_ASYNC_RESULT));
313 if (hFile)
314 CloseHandle(hFile);
316 return bSuccess;
320 /***********************************************************************
321 * FtpSetCurrentDirectoryA (WININET.@)
323 * Change the working directory on the FTP server
325 * RETURNS
326 * TRUE on success
327 * FALSE on failure
330 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
332 LPWSTR lpwzDirectory;
333 BOOL ret;
335 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
336 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
337 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
338 return ret;
342 /***********************************************************************
343 * FtpSetCurrentDirectoryW (WININET.@)
345 * Change the working directory on the FTP server
347 * RETURNS
348 * TRUE on success
349 * FALSE on failure
352 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
354 struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
355 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
357 TRACE("%p\n", lpwfs);
359 FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
360 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
363 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
365 LPWININETFTPSESSIONW lpwfs = NULL;
366 LPWININETAPPINFOW hIC = NULL;
367 BOOL r = FALSE;
369 if (!lpszDirectory)
371 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
372 goto lend;
375 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
376 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
378 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
379 goto lend;
382 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
384 hIC = lpwfs->lpAppInfo;
385 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
387 WORKREQUEST workRequest;
388 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
390 workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
391 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
392 req = &workRequest.u.FtpSetCurrentDirectoryW;
393 req->lpszDirectory = WININET_strdupW(lpszDirectory);
395 r = INTERNET_AsyncCall(&workRequest);
397 else
399 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
402 lend:
403 if( lpwfs )
404 WININET_Release( &lpwfs->hdr );
406 return r;
410 /***********************************************************************
411 * FTP_FtpSetCurrentDirectoryW (Internal)
413 * Change the working directory on the FTP server
415 * RETURNS
416 * TRUE on success
417 * FALSE on failure
420 BOOL WINAPI FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
422 INT nResCode;
423 LPWININETAPPINFOW hIC = NULL;
424 DWORD bSuccess = FALSE;
426 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
428 /* Clear any error information */
429 INTERNET_SetLastError(0);
431 hIC = lpwfs->lpAppInfo;
432 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
433 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
434 goto lend;
436 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
438 if (nResCode)
440 if (nResCode == 250)
441 bSuccess = TRUE;
442 else
443 FTP_SetResponseError(nResCode);
446 lend:
447 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
449 INTERNET_ASYNC_RESULT iar;
451 iar.dwResult = (DWORD)bSuccess;
452 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
453 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
454 &iar, sizeof(INTERNET_ASYNC_RESULT));
456 return bSuccess;
460 /***********************************************************************
461 * FtpCreateDirectoryA (WININET.@)
463 * Create new directory on the FTP server
465 * RETURNS
466 * TRUE on success
467 * FALSE on failure
470 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
472 LPWSTR lpwzDirectory;
473 BOOL ret;
475 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
476 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
477 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
478 return ret;
482 /***********************************************************************
483 * FtpCreateDirectoryW (WININET.@)
485 * Create new directory on the FTP server
487 * RETURNS
488 * TRUE on success
489 * FALSE on failure
492 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
494 struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
495 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
497 TRACE(" %p\n", lpwfs);
499 FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
500 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
503 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
505 LPWININETFTPSESSIONW lpwfs;
506 LPWININETAPPINFOW hIC = NULL;
507 BOOL r = FALSE;
509 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
510 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
512 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
513 goto lend;
516 hIC = lpwfs->lpAppInfo;
517 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
519 WORKREQUEST workRequest;
520 struct WORKREQ_FTPCREATEDIRECTORYW *req;
522 workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
523 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
524 req = &workRequest.u.FtpCreateDirectoryW;
525 req->lpszDirectory = WININET_strdupW(lpszDirectory);
527 r = INTERNET_AsyncCall(&workRequest);
529 else
531 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
533 lend:
534 if( lpwfs )
535 WININET_Release( &lpwfs->hdr );
537 return r;
541 /***********************************************************************
542 * FTP_FtpCreateDirectoryW (Internal)
544 * Create new directory on the FTP server
546 * RETURNS
547 * TRUE on success
548 * FALSE on failure
551 BOOL WINAPI FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
553 INT nResCode;
554 BOOL bSuccess = FALSE;
555 LPWININETAPPINFOW hIC = NULL;
557 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
559 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
561 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
562 return FALSE;
565 /* Clear any error information */
566 INTERNET_SetLastError(0);
568 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
569 goto lend;
571 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
572 if (nResCode)
574 if (nResCode == 257)
575 bSuccess = TRUE;
576 else
577 FTP_SetResponseError(nResCode);
580 lend:
581 hIC = lpwfs->lpAppInfo;
582 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
584 INTERNET_ASYNC_RESULT iar;
586 iar.dwResult = (DWORD)bSuccess;
587 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
588 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
589 &iar, sizeof(INTERNET_ASYNC_RESULT));
592 return bSuccess;
595 /***********************************************************************
596 * FtpFindFirstFileA (WININET.@)
598 * Search the specified directory
600 * RETURNS
601 * HINTERNET on success
602 * NULL on failure
605 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
606 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD dwContext)
608 LPWSTR lpwzSearchFile;
609 WIN32_FIND_DATAW wfd;
610 LPWIN32_FIND_DATAW lpFindFileDataW;
611 HINTERNET ret;
613 lpwzSearchFile = lpszSearchFile?WININET_strdup_AtoW(lpszSearchFile):NULL;
614 lpFindFileDataW = lpFindFileData?&wfd:NULL;
615 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
616 HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
618 if(lpFindFileData) {
619 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
621 return ret;
625 /***********************************************************************
626 * FtpFindFirstFileW (WININET.@)
628 * Search the specified directory
630 * RETURNS
631 * HINTERNET on success
632 * NULL on failure
635 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
637 struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
638 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
640 TRACE("%p\n", lpwfs);
642 FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
643 req->lpFindFileData, req->dwFlags, req->dwContext);
644 HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
647 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
648 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD dwContext)
650 LPWININETFTPSESSIONW lpwfs;
651 LPWININETAPPINFOW hIC = NULL;
652 HINTERNET r = NULL;
654 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
655 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
657 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
658 goto lend;
661 hIC = lpwfs->lpAppInfo;
662 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
664 WORKREQUEST workRequest;
665 struct WORKREQ_FTPFINDFIRSTFILEW *req;
667 workRequest.asyncproc = AsyncFtpFindFirstFileProc;
668 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
669 req = &workRequest.u.FtpFindFirstFileW;
670 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : WININET_strdupW(lpszSearchFile);
671 req->lpFindFileData = lpFindFileData;
672 req->dwFlags = dwFlags;
673 req->dwContext= dwContext;
675 INTERNET_AsyncCall(&workRequest);
676 r = NULL;
678 else
680 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
681 dwFlags, dwContext);
683 lend:
684 if( lpwfs )
685 WININET_Release( &lpwfs->hdr );
687 return r;
691 /***********************************************************************
692 * FTP_FtpFindFirstFileW (Internal)
694 * Search the specified directory
696 * RETURNS
697 * HINTERNET on success
698 * NULL on failure
701 HINTERNET WINAPI FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
702 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD dwContext)
704 INT nResCode;
705 LPWININETAPPINFOW hIC = NULL;
706 HINTERNET hFindNext = NULL;
708 TRACE("\n");
710 assert(WH_HFTPSESSION == lpwfs->hdr.htype);
712 /* Clear any error information */
713 INTERNET_SetLastError(0);
715 if (!FTP_InitListenSocket(lpwfs))
716 goto lend;
718 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
719 goto lend;
721 if (!FTP_SendPortOrPasv(lpwfs))
722 goto lend;
724 hIC = lpwfs->lpAppInfo;
725 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
726 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
727 goto lend;
729 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
730 if (nResCode)
732 if (nResCode == 125 || nResCode == 150)
734 INT nDataSocket;
736 /* Get data socket to server */
737 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
739 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
740 closesocket(nDataSocket);
741 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
742 if (nResCode != 226 && nResCode != 250)
743 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
746 else
747 FTP_SetResponseError(nResCode);
750 lend:
751 if (lpwfs->lstnSocket != -1)
752 closesocket(lpwfs->lstnSocket);
754 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
756 INTERNET_ASYNC_RESULT iar;
758 if (hFindNext)
760 iar.dwResult = (DWORD)hFindNext;
761 iar.dwError = ERROR_SUCCESS;
762 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
763 &iar, sizeof(INTERNET_ASYNC_RESULT));
766 iar.dwResult = (DWORD)hFindNext;
767 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
768 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
769 &iar, sizeof(INTERNET_ASYNC_RESULT));
772 return hFindNext;
776 /***********************************************************************
777 * FtpGetCurrentDirectoryA (WININET.@)
779 * Retrieves the current directory
781 * RETURNS
782 * TRUE on success
783 * FALSE on failure
786 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
787 LPDWORD lpdwCurrentDirectory)
789 WCHAR *dir = NULL;
790 DWORD len;
791 BOOL ret;
793 if(lpdwCurrentDirectory) {
794 len = *lpdwCurrentDirectory;
795 if(lpszCurrentDirectory)
797 dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
798 if (NULL == dir)
800 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
801 return FALSE;
805 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
806 if(lpdwCurrentDirectory) {
807 *lpdwCurrentDirectory = len;
808 if(lpszCurrentDirectory) {
809 WideCharToMultiByte(CP_ACP, 0, dir, len, lpszCurrentDirectory, *lpdwCurrentDirectory, NULL, NULL);
810 HeapFree(GetProcessHeap(), 0, dir);
813 return ret;
817 /***********************************************************************
818 * FtpGetCurrentDirectoryW (WININET.@)
820 * Retrieves the current directory
822 * RETURNS
823 * TRUE on success
824 * FALSE on failure
827 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
829 struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
830 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
832 TRACE("%p\n", lpwfs);
834 FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
837 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
838 LPDWORD lpdwCurrentDirectory)
840 LPWININETFTPSESSIONW lpwfs;
841 LPWININETAPPINFOW hIC = NULL;
842 BOOL r = FALSE;
844 TRACE("len(%d)\n", *lpdwCurrentDirectory);
846 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
847 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
849 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
850 goto lend;
853 hIC = lpwfs->lpAppInfo;
854 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
856 WORKREQUEST workRequest;
857 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
859 workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
860 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
861 req = &workRequest.u.FtpGetCurrentDirectoryW;
862 req->lpszDirectory = lpszCurrentDirectory;
863 req->lpdwDirectory = lpdwCurrentDirectory;
865 r = INTERNET_AsyncCall(&workRequest);
867 else
869 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
870 lpdwCurrentDirectory);
873 lend:
874 if( lpwfs )
875 WININET_Release( &lpwfs->hdr );
877 return r;
881 /***********************************************************************
882 * FTP_FtpGetCurrentDirectoryA (Internal)
884 * Retrieves the current directory
886 * RETURNS
887 * TRUE on success
888 * FALSE on failure
891 BOOL WINAPI FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
892 LPDWORD lpdwCurrentDirectory)
894 INT nResCode;
895 LPWININETAPPINFOW hIC = NULL;
896 DWORD bSuccess = FALSE;
898 TRACE("len(%d)\n", *lpdwCurrentDirectory);
900 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
902 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
903 return FALSE;
906 /* Clear any error information */
907 INTERNET_SetLastError(0);
909 ZeroMemory(lpszCurrentDirectory, *lpdwCurrentDirectory);
911 hIC = lpwfs->lpAppInfo;
912 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
913 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
914 goto lend;
916 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
917 if (nResCode)
919 if (nResCode == 257) /* Extract directory name */
921 DWORD firstpos, lastpos, len;
922 LPWSTR lpszResponseBuffer = WININET_strdup_AtoW(INTERNET_GetResponseBuffer());
924 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
926 if ('"' == lpszResponseBuffer[lastpos])
928 if (!firstpos)
929 firstpos = lastpos;
930 else
931 break;
935 len = lastpos - firstpos - 1;
936 lstrcpynW(lpszCurrentDirectory, &lpszResponseBuffer[firstpos+1], *lpdwCurrentDirectory);
937 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
938 *lpdwCurrentDirectory = len;
939 bSuccess = TRUE;
941 else
942 FTP_SetResponseError(nResCode);
945 lend:
946 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
948 INTERNET_ASYNC_RESULT iar;
950 iar.dwResult = (DWORD)bSuccess;
951 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
952 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
953 &iar, sizeof(INTERNET_ASYNC_RESULT));
956 return (DWORD) bSuccess;
959 /***********************************************************************
960 * FtpOpenFileA (WININET.@)
962 * Open a remote file for writing or reading
964 * RETURNS
965 * HINTERNET handle on success
966 * NULL on failure
969 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
970 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
971 DWORD dwContext)
973 LPWSTR lpwzFileName;
974 HINTERNET ret;
976 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
977 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
978 HeapFree(GetProcessHeap(), 0, lpwzFileName);
979 return ret;
983 /***********************************************************************
984 * FtpOpenFileW (WININET.@)
986 * Open a remote file for writing or reading
988 * RETURNS
989 * HINTERNET handle on success
990 * NULL on failure
993 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
995 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
996 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
998 TRACE("%p\n", lpwfs);
1000 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1001 req->dwAccess, req->dwFlags, req->dwContext);
1002 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1005 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1006 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1007 DWORD dwContext)
1009 LPWININETFTPSESSIONW lpwfs;
1010 LPWININETAPPINFOW hIC = NULL;
1011 HINTERNET r = NULL;
1013 TRACE("(%p,%s,0x%08x,0x%08x,0x%08x)\n", hFtpSession,
1014 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1016 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1017 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1019 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1020 goto lend;
1023 if (lpwfs->download_in_progress != NULL) {
1024 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1025 goto lend;
1027 hIC = lpwfs->lpAppInfo;
1028 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1030 WORKREQUEST workRequest;
1031 struct WORKREQ_FTPOPENFILEW *req;
1033 workRequest.asyncproc = AsyncFtpOpenFileProc;
1034 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1035 req = &workRequest.u.FtpOpenFileW;
1036 req->lpszFilename = WININET_strdupW(lpszFileName);
1037 req->dwAccess = fdwAccess;
1038 req->dwFlags = dwFlags;
1039 req->dwContext = dwContext;
1041 INTERNET_AsyncCall(&workRequest);
1042 r = NULL;
1044 else
1046 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1049 lend:
1050 if( lpwfs )
1051 WININET_Release( &lpwfs->hdr );
1053 return r;
1057 /***********************************************************************
1058 * FTP_FtpOpenFileW (Internal)
1060 * Open a remote file for writing or reading
1062 * RETURNS
1063 * HINTERNET handle on success
1064 * NULL on failure
1067 HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
1068 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1069 DWORD dwContext)
1071 INT nDataSocket;
1072 BOOL bSuccess = FALSE;
1073 LPWININETFTPFILE lpwh = NULL;
1074 LPWININETAPPINFOW hIC = NULL;
1075 HINTERNET handle = NULL;
1077 TRACE("\n");
1079 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1081 /* Clear any error information */
1082 INTERNET_SetLastError(0);
1084 if (GENERIC_READ == fdwAccess)
1086 /* Set up socket to retrieve data */
1087 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1089 else if (GENERIC_WRITE == fdwAccess)
1091 /* Set up socket to send data */
1092 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1095 /* Get data socket to server */
1096 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1098 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPFILE));
1099 lpwh->hdr.htype = WH_HFILE;
1100 lpwh->hdr.dwFlags = dwFlags;
1101 lpwh->hdr.dwContext = dwContext;
1102 lpwh->hdr.dwRefCount = 1;
1103 lpwh->hdr.destroy = FTP_CloseFileTransferHandle;
1104 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1105 lpwh->nDataSocket = nDataSocket;
1106 lpwh->session_deleted = FALSE;
1108 WININET_AddRef( &lpwfs->hdr );
1109 lpwh->lpFtpSession = lpwfs;
1111 handle = WININET_AllocHandle( &lpwh->hdr );
1112 if( !handle )
1113 goto lend;
1115 /* Indicate that a download is currently in progress */
1116 lpwfs->download_in_progress = lpwh;
1119 if (lpwfs->lstnSocket != -1)
1120 closesocket(lpwfs->lstnSocket);
1122 hIC = lpwfs->lpAppInfo;
1123 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1125 INTERNET_ASYNC_RESULT iar;
1127 if (lpwh)
1129 iar.dwResult = (DWORD)handle;
1130 iar.dwError = ERROR_SUCCESS;
1131 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1132 &iar, sizeof(INTERNET_ASYNC_RESULT));
1135 iar.dwResult = (DWORD)bSuccess;
1136 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1137 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1138 &iar, sizeof(INTERNET_ASYNC_RESULT));
1141 lend:
1142 if( lpwh )
1143 WININET_Release( &lpwh->hdr );
1145 return handle;
1149 /***********************************************************************
1150 * FtpGetFileA (WININET.@)
1152 * Retrieve file from the FTP server
1154 * RETURNS
1155 * TRUE on success
1156 * FALSE on failure
1159 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1160 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1161 DWORD dwContext)
1163 LPWSTR lpwzRemoteFile;
1164 LPWSTR lpwzNewFile;
1165 BOOL ret;
1167 lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
1168 lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
1169 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1170 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1171 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1172 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1173 return ret;
1177 /***********************************************************************
1178 * FtpGetFileW (WININET.@)
1180 * Retrieve file from the FTP server
1182 * RETURNS
1183 * TRUE on success
1184 * FALSE on failure
1187 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1189 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1190 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1192 TRACE("%p\n", lpwfs);
1194 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1195 req->lpszNewFile, req->fFailIfExists,
1196 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1197 HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1198 HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1201 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1202 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1203 DWORD dwContext)
1205 LPWININETFTPSESSIONW lpwfs;
1206 LPWININETAPPINFOW hIC = NULL;
1207 BOOL r = FALSE;
1209 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
1210 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1212 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1213 goto lend;
1216 if (lpwfs->download_in_progress != NULL) {
1217 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1218 goto lend;
1221 hIC = lpwfs->lpAppInfo;
1222 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1224 WORKREQUEST workRequest;
1225 struct WORKREQ_FTPGETFILEW *req;
1227 workRequest.asyncproc = AsyncFtpGetFileProc;
1228 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1229 req = &workRequest.u.FtpGetFileW;
1230 req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
1231 req->lpszNewFile = WININET_strdupW(lpszNewFile);
1232 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1233 req->fFailIfExists = fFailIfExists;
1234 req->dwFlags = dwInternetFlags;
1235 req->dwContext = dwContext;
1237 r = INTERNET_AsyncCall(&workRequest);
1239 else
1241 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1242 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1245 lend:
1246 if( lpwfs )
1247 WININET_Release( &lpwfs->hdr );
1249 return r;
1253 /***********************************************************************
1254 * FTP_FtpGetFileW (Internal)
1256 * Retrieve file from the FTP server
1258 * RETURNS
1259 * TRUE on success
1260 * FALSE on failure
1263 BOOL WINAPI FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1264 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1265 DWORD dwContext)
1267 DWORD nBytes;
1268 BOOL bSuccess = FALSE;
1269 HANDLE hFile;
1270 LPWININETAPPINFOW hIC = NULL;
1272 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1274 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1276 /* Clear any error information */
1277 INTERNET_SetLastError(0);
1279 /* Ensure we can write to lpszNewfile by opening it */
1280 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1281 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1282 if (INVALID_HANDLE_VALUE == hFile)
1283 goto lend;
1285 /* Set up socket to retrieve data */
1286 nBytes = FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags);
1288 if (nBytes > 0)
1290 INT nDataSocket;
1292 /* Get data socket to server */
1293 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1295 INT nResCode;
1297 /* Receive data */
1298 FTP_RetrieveFileData(lpwfs, nDataSocket, nBytes, hFile);
1299 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1300 if (nResCode)
1302 if (nResCode == 226)
1303 bSuccess = TRUE;
1304 else
1305 FTP_SetResponseError(nResCode);
1307 closesocket(nDataSocket);
1311 lend:
1312 if (lpwfs->lstnSocket != -1)
1313 closesocket(lpwfs->lstnSocket);
1315 if (hFile)
1316 CloseHandle(hFile);
1318 hIC = lpwfs->lpAppInfo;
1319 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1321 INTERNET_ASYNC_RESULT iar;
1323 iar.dwResult = (DWORD)bSuccess;
1324 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1325 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1326 &iar, sizeof(INTERNET_ASYNC_RESULT));
1329 return bSuccess;
1332 /***********************************************************************
1333 * FtpGetFileSize (WININET.@)
1335 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1337 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1339 if (lpdwFileSizeHigh)
1340 *lpdwFileSizeHigh = 0;
1342 return 0;
1345 /***********************************************************************
1346 * FtpDeleteFileA (WININET.@)
1348 * Delete a file on the ftp server
1350 * RETURNS
1351 * TRUE on success
1352 * FALSE on failure
1355 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1357 LPWSTR lpwzFileName;
1358 BOOL ret;
1360 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1361 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1362 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1363 return ret;
1366 /***********************************************************************
1367 * FtpDeleteFileW (WININET.@)
1369 * Delete a file on the ftp server
1371 * RETURNS
1372 * TRUE on success
1373 * FALSE on failure
1376 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1378 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1379 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1381 TRACE("%p\n", lpwfs);
1383 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1384 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1387 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1389 LPWININETFTPSESSIONW lpwfs;
1390 LPWININETAPPINFOW hIC = NULL;
1391 BOOL r = FALSE;
1393 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1394 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1396 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1397 goto lend;
1400 hIC = lpwfs->lpAppInfo;
1401 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1403 WORKREQUEST workRequest;
1404 struct WORKREQ_FTPDELETEFILEW *req;
1406 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1407 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1408 req = &workRequest.u.FtpDeleteFileW;
1409 req->lpszFilename = WININET_strdupW(lpszFileName);
1411 r = INTERNET_AsyncCall(&workRequest);
1413 else
1415 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1418 lend:
1419 if( lpwfs )
1420 WININET_Release( &lpwfs->hdr );
1422 return r;
1425 /***********************************************************************
1426 * FTP_FtpDeleteFileW (Internal)
1428 * Delete a file on the ftp server
1430 * RETURNS
1431 * TRUE on success
1432 * FALSE on failure
1435 BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName)
1437 INT nResCode;
1438 BOOL bSuccess = FALSE;
1439 LPWININETAPPINFOW hIC = NULL;
1441 TRACE("%p\n", lpwfs);
1443 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1445 /* Clear any error information */
1446 INTERNET_SetLastError(0);
1448 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1449 goto lend;
1451 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1452 if (nResCode)
1454 if (nResCode == 250)
1455 bSuccess = TRUE;
1456 else
1457 FTP_SetResponseError(nResCode);
1459 lend:
1460 hIC = lpwfs->lpAppInfo;
1461 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1463 INTERNET_ASYNC_RESULT iar;
1465 iar.dwResult = (DWORD)bSuccess;
1466 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1467 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1468 &iar, sizeof(INTERNET_ASYNC_RESULT));
1471 return bSuccess;
1475 /***********************************************************************
1476 * FtpRemoveDirectoryA (WININET.@)
1478 * Remove a directory on the ftp server
1480 * RETURNS
1481 * TRUE on success
1482 * FALSE on failure
1485 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1487 LPWSTR lpwzDirectory;
1488 BOOL ret;
1490 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
1491 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1492 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1493 return ret;
1496 /***********************************************************************
1497 * FtpRemoveDirectoryW (WININET.@)
1499 * Remove a directory on the ftp server
1501 * RETURNS
1502 * TRUE on success
1503 * FALSE on failure
1506 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1508 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1509 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1511 TRACE("%p\n", lpwfs);
1513 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1514 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1517 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1519 LPWININETFTPSESSIONW lpwfs;
1520 LPWININETAPPINFOW hIC = NULL;
1521 BOOL r = FALSE;
1523 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1524 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1526 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1527 goto lend;
1530 hIC = lpwfs->lpAppInfo;
1531 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1533 WORKREQUEST workRequest;
1534 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1536 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1537 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1538 req = &workRequest.u.FtpRemoveDirectoryW;
1539 req->lpszDirectory = WININET_strdupW(lpszDirectory);
1541 r = INTERNET_AsyncCall(&workRequest);
1543 else
1545 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1548 lend:
1549 if( lpwfs )
1550 WININET_Release( &lpwfs->hdr );
1552 return r;
1555 /***********************************************************************
1556 * FTP_FtpRemoveDirectoryW (Internal)
1558 * Remove a directory on the ftp server
1560 * RETURNS
1561 * TRUE on success
1562 * FALSE on failure
1565 BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
1567 INT nResCode;
1568 BOOL bSuccess = FALSE;
1569 LPWININETAPPINFOW hIC = NULL;
1571 TRACE("\n");
1573 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1575 /* Clear any error information */
1576 INTERNET_SetLastError(0);
1578 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1579 goto lend;
1581 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1582 if (nResCode)
1584 if (nResCode == 250)
1585 bSuccess = TRUE;
1586 else
1587 FTP_SetResponseError(nResCode);
1590 lend:
1591 hIC = lpwfs->lpAppInfo;
1592 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1594 INTERNET_ASYNC_RESULT iar;
1596 iar.dwResult = (DWORD)bSuccess;
1597 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1598 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1599 &iar, sizeof(INTERNET_ASYNC_RESULT));
1602 return bSuccess;
1606 /***********************************************************************
1607 * FtpRenameFileA (WININET.@)
1609 * Rename a file on the ftp server
1611 * RETURNS
1612 * TRUE on success
1613 * FALSE on failure
1616 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1618 LPWSTR lpwzSrc;
1619 LPWSTR lpwzDest;
1620 BOOL ret;
1622 lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL;
1623 lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL;
1624 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1625 HeapFree(GetProcessHeap(), 0, lpwzSrc);
1626 HeapFree(GetProcessHeap(), 0, lpwzDest);
1627 return ret;
1630 /***********************************************************************
1631 * FtpRenameFileW (WININET.@)
1633 * Rename a file on the ftp server
1635 * RETURNS
1636 * TRUE on success
1637 * FALSE on failure
1640 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
1642 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
1643 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1645 TRACE("%p\n", lpwfs);
1647 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
1648 HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
1649 HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
1652 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
1654 LPWININETFTPSESSIONW lpwfs;
1655 LPWININETAPPINFOW hIC = NULL;
1656 BOOL r = FALSE;
1658 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1659 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1661 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1662 goto lend;
1665 hIC = lpwfs->lpAppInfo;
1666 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1668 WORKREQUEST workRequest;
1669 struct WORKREQ_FTPRENAMEFILEW *req;
1671 workRequest.asyncproc = AsyncFtpRenameFileProc;
1672 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1673 req = &workRequest.u.FtpRenameFileW;
1674 req->lpszSrcFile = WININET_strdupW(lpszSrc);
1675 req->lpszDestFile = WININET_strdupW(lpszDest);
1677 r = INTERNET_AsyncCall(&workRequest);
1679 else
1681 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
1684 lend:
1685 if( lpwfs )
1686 WININET_Release( &lpwfs->hdr );
1688 return r;
1691 /***********************************************************************
1692 * FTP_FtpRenameFileW (Internal)
1694 * Rename a file on the ftp server
1696 * RETURNS
1697 * TRUE on success
1698 * FALSE on failure
1701 BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs,
1702 LPCWSTR lpszSrc, LPCWSTR lpszDest)
1704 INT nResCode;
1705 BOOL bSuccess = FALSE;
1706 LPWININETAPPINFOW hIC = NULL;
1708 TRACE("\n");
1710 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1712 /* Clear any error information */
1713 INTERNET_SetLastError(0);
1715 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
1716 goto lend;
1718 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1719 if (nResCode == 350)
1721 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
1722 goto lend;
1724 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1727 if (nResCode == 250)
1728 bSuccess = TRUE;
1729 else
1730 FTP_SetResponseError(nResCode);
1732 lend:
1733 hIC = lpwfs->lpAppInfo;
1734 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1736 INTERNET_ASYNC_RESULT iar;
1738 iar.dwResult = (DWORD)bSuccess;
1739 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1740 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1741 &iar, sizeof(INTERNET_ASYNC_RESULT));
1744 return bSuccess;
1747 /***********************************************************************
1748 * FtpCommandA (WININET.@)
1750 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1751 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1753 FIXME("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1754 debugstr_a(lpszCommand), dwContext, phFtpCommand);
1756 return TRUE;
1759 /***********************************************************************
1760 * FtpCommandW (WININET.@)
1762 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1763 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1765 FIXME("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1766 debugstr_w(lpszCommand), dwContext, phFtpCommand);
1768 return TRUE;
1771 /***********************************************************************
1772 * FTP_Connect (internal)
1774 * Connect to a ftp server
1776 * RETURNS
1777 * HINTERNET a session handle on success
1778 * NULL on failure
1780 * NOTES:
1782 * Windows uses 'anonymous' as the username, when given a NULL username
1783 * and a NULL password. The password is first looked up in:
1785 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
1787 * If this entry is not present it uses the current username as the password.
1791 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
1792 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
1793 LPCWSTR lpszPassword, DWORD dwFlags, DWORD dwContext,
1794 DWORD dwInternalFlags)
1796 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
1797 'M','i','c','r','o','s','o','f','t','\\',
1798 'W','i','n','d','o','w','s','\\',
1799 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1800 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
1801 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
1802 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
1803 static const WCHAR szEmpty[] = {'\0'};
1804 struct sockaddr_in socketAddr;
1805 INT nsocket = -1;
1806 UINT sock_namelen;
1807 BOOL bSuccess = FALSE;
1808 LPWININETFTPSESSIONW lpwfs = NULL;
1809 HINTERNET handle = NULL;
1811 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
1812 hIC, debugstr_w(lpszServerName),
1813 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
1815 assert( hIC->hdr.htype == WH_HINIT );
1817 if (NULL == lpszUserName && NULL != lpszPassword)
1819 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1820 goto lerror;
1823 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
1824 if (NULL == lpwfs)
1826 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1827 goto lerror;
1830 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
1831 nServerPort = INTERNET_DEFAULT_FTP_PORT;
1833 lpwfs->hdr.htype = WH_HFTPSESSION;
1834 lpwfs->hdr.dwFlags = dwFlags;
1835 lpwfs->hdr.dwContext = dwContext;
1836 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
1837 lpwfs->hdr.dwRefCount = 1;
1838 lpwfs->hdr.destroy = FTP_CloseSessionHandle;
1839 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
1840 lpwfs->download_in_progress = NULL;
1842 WININET_AddRef( &hIC->hdr );
1843 lpwfs->lpAppInfo = hIC;
1845 handle = WININET_AllocHandle( &lpwfs->hdr );
1846 if( !handle )
1848 ERR("Failed to alloc handle\n");
1849 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1850 goto lerror;
1853 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
1854 if(strchrW(hIC->lpszProxy, ' '))
1855 FIXME("Several proxies not implemented.\n");
1856 if(hIC->lpszProxyBypass)
1857 FIXME("Proxy bypass is ignored.\n");
1859 if ( !lpszUserName) {
1860 HKEY key;
1861 WCHAR szPassword[MAX_PATH];
1862 DWORD len = sizeof(szPassword);
1864 lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
1866 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
1867 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
1868 /* Nothing in the registry, get the username and use that as the password */
1869 if (!GetUserNameW(szPassword, &len)) {
1870 /* Should never get here, but use an empty password as failsafe */
1871 strcpyW(szPassword, szEmpty);
1874 RegCloseKey(key);
1876 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
1877 lpwfs->lpszPassword = WININET_strdupW(szPassword);
1879 else {
1880 lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
1882 if (lpszPassword)
1883 lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
1884 else
1885 lpwfs->lpszPassword = WININET_strdupW(szEmpty);
1888 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
1889 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
1891 INTERNET_ASYNC_RESULT iar;
1893 iar.dwResult = (DWORD)handle;
1894 iar.dwError = ERROR_SUCCESS;
1896 SendAsyncCallback(&hIC->hdr, dwContext,
1897 INTERNET_STATUS_HANDLE_CREATED, &iar,
1898 sizeof(INTERNET_ASYNC_RESULT));
1901 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
1902 (LPWSTR) lpszServerName, strlenW(lpszServerName));
1904 if (!GetAddress(lpszServerName, nServerPort, &socketAddr))
1906 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1907 goto lerror;
1910 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
1911 (LPWSTR) lpszServerName, strlenW(lpszServerName));
1913 nsocket = socket(AF_INET,SOCK_STREAM,0);
1914 if (nsocket == -1)
1916 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1917 goto lerror;
1920 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
1921 &socketAddr, sizeof(struct sockaddr_in));
1923 if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
1925 ERR("Unable to connect (%s)\n", strerror(errno));
1926 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1928 else
1930 TRACE("Connected to server\n");
1931 lpwfs->sndSocket = nsocket;
1932 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
1933 &socketAddr, sizeof(struct sockaddr_in));
1935 sock_namelen = sizeof(lpwfs->socketAddress);
1936 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
1938 if (FTP_ConnectToHost(lpwfs))
1940 TRACE("Successfully logged into server\n");
1941 bSuccess = TRUE;
1945 lerror:
1946 if (!bSuccess && nsocket == -1)
1947 closesocket(nsocket);
1949 if (!bSuccess && lpwfs)
1951 HeapFree(GetProcessHeap(), 0, lpwfs);
1952 WININET_FreeHandle( handle );
1953 handle = NULL;
1954 lpwfs = NULL;
1957 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1959 INTERNET_ASYNC_RESULT iar;
1961 iar.dwResult = (DWORD)lpwfs;
1962 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1963 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1964 &iar, sizeof(INTERNET_ASYNC_RESULT));
1967 return handle;
1971 /***********************************************************************
1972 * FTP_ConnectToHost (internal)
1974 * Connect to a ftp server
1976 * RETURNS
1977 * TRUE on success
1978 * NULL on failure
1981 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
1983 INT nResCode;
1984 BOOL bSuccess = FALSE;
1986 TRACE("\n");
1987 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1989 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
1990 goto lend;
1992 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1993 if (nResCode)
1995 /* Login successful... */
1996 if (nResCode == 230)
1997 bSuccess = TRUE;
1998 /* User name okay, need password... */
1999 else if (nResCode == 331)
2000 bSuccess = FTP_SendPassword(lpwfs);
2001 /* Need account for login... */
2002 else if (nResCode == 332)
2003 bSuccess = FTP_SendAccount(lpwfs);
2004 else
2005 FTP_SetResponseError(nResCode);
2008 TRACE("Returning %d\n", bSuccess);
2009 lend:
2010 return bSuccess;
2014 /***********************************************************************
2015 * FTP_SendCommandA (internal)
2017 * Send command to server
2019 * RETURNS
2020 * TRUE on success
2021 * NULL on failure
2024 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2025 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext)
2027 DWORD len;
2028 CHAR *buf;
2029 DWORD nBytesSent = 0;
2030 int nRC = 0;
2031 DWORD dwParamLen;
2033 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2035 if (lpfnStatusCB)
2037 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2040 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2041 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2042 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2044 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2045 return FALSE;
2047 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2048 dwParamLen ? lpszParam : "", szCRLF);
2050 TRACE("Sending (%s) len(%d)\n", buf, len);
2051 while((nBytesSent < len) && (nRC != -1))
2053 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2054 nBytesSent += nRC;
2057 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2059 if (lpfnStatusCB)
2061 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2062 &nBytesSent, sizeof(DWORD));
2065 TRACE("Sent %d bytes\n", nBytesSent);
2066 return (nRC != -1);
2069 /***********************************************************************
2070 * FTP_SendCommand (internal)
2072 * Send command to server
2074 * RETURNS
2075 * TRUE on success
2076 * NULL on failure
2079 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2080 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext)
2082 BOOL ret;
2083 LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
2084 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2085 HeapFree(GetProcessHeap(), 0, lpszParamA);
2086 return ret;
2089 /***********************************************************************
2090 * FTP_ReceiveResponse (internal)
2092 * Receive response from server
2094 * RETURNS
2095 * Reply code on success
2096 * 0 on failure
2099 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD dwContext)
2101 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2102 DWORD nRecv;
2103 INT rc = 0;
2104 char firstprefix[5];
2105 BOOL multiline = FALSE;
2106 LPWININETAPPINFOW hIC = NULL;
2108 TRACE("socket(%d)\n", lpwfs->sndSocket);
2110 hIC = lpwfs->lpAppInfo;
2111 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2113 while(1)
2115 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2116 goto lerror;
2118 if (nRecv >= 3)
2120 if(!multiline)
2122 if(lpszResponse[3] != '-')
2123 break;
2124 else
2125 { /* Start of multiline repsonse. Loop until we get "nnn " */
2126 multiline = TRUE;
2127 memcpy(firstprefix, lpszResponse, 3);
2128 firstprefix[3] = ' ';
2129 firstprefix[4] = '\0';
2132 else
2134 if(!memcmp(firstprefix, lpszResponse, 4))
2135 break;
2140 if (nRecv >= 3)
2142 rc = atoi(lpszResponse);
2144 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2145 &nRecv, sizeof(DWORD));
2148 lerror:
2149 TRACE("return %d\n", rc);
2150 return rc;
2154 /***********************************************************************
2155 * FTP_SendPassword (internal)
2157 * Send password to ftp server
2159 * RETURNS
2160 * TRUE on success
2161 * NULL on failure
2164 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2166 INT nResCode;
2167 BOOL bSuccess = FALSE;
2169 TRACE("\n");
2170 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2171 goto lend;
2173 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2174 if (nResCode)
2176 TRACE("Received reply code %d\n", nResCode);
2177 /* Login successful... */
2178 if (nResCode == 230)
2179 bSuccess = TRUE;
2180 /* Command not implemented, superfluous at the server site... */
2181 /* Need account for login... */
2182 else if (nResCode == 332)
2183 bSuccess = FTP_SendAccount(lpwfs);
2184 else
2185 FTP_SetResponseError(nResCode);
2188 lend:
2189 TRACE("Returning %d\n", bSuccess);
2190 return bSuccess;
2194 /***********************************************************************
2195 * FTP_SendAccount (internal)
2199 * RETURNS
2200 * TRUE on success
2201 * FALSE on failure
2204 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2206 INT nResCode;
2207 BOOL bSuccess = FALSE;
2209 TRACE("\n");
2210 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2211 goto lend;
2213 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2214 if (nResCode)
2215 bSuccess = TRUE;
2216 else
2217 FTP_SetResponseError(nResCode);
2219 lend:
2220 return bSuccess;
2224 /***********************************************************************
2225 * FTP_SendStore (internal)
2227 * Send request to upload file to ftp server
2229 * RETURNS
2230 * TRUE on success
2231 * FALSE on failure
2234 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2236 INT nResCode;
2237 BOOL bSuccess = FALSE;
2239 TRACE("\n");
2240 if (!FTP_InitListenSocket(lpwfs))
2241 goto lend;
2243 if (!FTP_SendType(lpwfs, dwType))
2244 goto lend;
2246 if (!FTP_SendPortOrPasv(lpwfs))
2247 goto lend;
2249 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2250 goto lend;
2251 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2252 if (nResCode)
2254 if (nResCode == 150 || nResCode == 125)
2255 bSuccess = TRUE;
2256 else
2257 FTP_SetResponseError(nResCode);
2260 lend:
2261 if (!bSuccess && lpwfs->lstnSocket != -1)
2263 closesocket(lpwfs->lstnSocket);
2264 lpwfs->lstnSocket = -1;
2267 return bSuccess;
2271 /***********************************************************************
2272 * FTP_InitListenSocket (internal)
2274 * Create a socket to listen for server response
2276 * RETURNS
2277 * TRUE on success
2278 * FALSE on failure
2281 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2283 BOOL bSuccess = FALSE;
2284 socklen_t namelen = sizeof(struct sockaddr_in);
2286 TRACE("\n");
2288 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2289 if (lpwfs->lstnSocket == -1)
2291 TRACE("Unable to create listening socket\n");
2292 goto lend;
2295 /* We obtain our ip addr from the name of the command channel socket */
2296 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2298 /* and get the system to assign us a port */
2299 lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
2301 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2303 TRACE("Unable to bind socket\n");
2304 goto lend;
2307 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2309 TRACE("listen failed\n");
2310 goto lend;
2313 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2314 bSuccess = TRUE;
2316 lend:
2317 if (!bSuccess && lpwfs->lstnSocket == -1)
2319 closesocket(lpwfs->lstnSocket);
2320 lpwfs->lstnSocket = -1;
2323 return bSuccess;
2327 /***********************************************************************
2328 * FTP_SendType (internal)
2330 * Tell server type of data being transferred
2332 * RETURNS
2333 * TRUE on success
2334 * FALSE on failure
2336 * W98SE doesn't cache the type that's currently set
2337 * (i.e. it sends it always),
2338 * so we probably don't want to do that either.
2340 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2342 INT nResCode;
2343 WCHAR type[] = { 'I','\0' };
2344 BOOL bSuccess = FALSE;
2346 TRACE("\n");
2347 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2348 type[0] = 'A';
2350 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2351 goto lend;
2353 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2354 if (nResCode)
2356 if (nResCode == 2)
2357 bSuccess = TRUE;
2358 else
2359 FTP_SetResponseError(nResCode);
2362 lend:
2363 return bSuccess;
2366 /***********************************************************************
2367 * FTP_GetFileSize (internal)
2369 * Retrieves from the server the size of the given file
2371 * RETURNS
2372 * TRUE on success
2373 * FALSE on failure
2376 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2378 INT nResCode;
2379 BOOL bSuccess = FALSE;
2381 TRACE("\n");
2383 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2384 goto lend;
2386 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2387 if (nResCode)
2389 if (nResCode == 213) {
2390 /* Now parses the output to get the actual file size */
2391 int i;
2392 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2394 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2395 if (lpszResponseBuffer[i] == '\0') return FALSE;
2396 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2398 bSuccess = TRUE;
2399 } else {
2400 FTP_SetResponseError(nResCode);
2404 lend:
2405 return bSuccess;
2409 /***********************************************************************
2410 * FTP_SendPort (internal)
2412 * Tell server which port to use
2414 * RETURNS
2415 * TRUE on success
2416 * FALSE on failure
2419 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2421 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2422 INT nResCode;
2423 WCHAR szIPAddress[64];
2424 BOOL bSuccess = FALSE;
2425 TRACE("\n");
2427 sprintfW(szIPAddress, szIPFormat,
2428 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2429 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2430 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2431 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2432 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2433 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2435 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2436 goto lend;
2438 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2439 if (nResCode)
2441 if (nResCode == 200)
2442 bSuccess = TRUE;
2443 else
2444 FTP_SetResponseError(nResCode);
2447 lend:
2448 return bSuccess;
2452 /***********************************************************************
2453 * FTP_DoPassive (internal)
2455 * Tell server that we want to do passive transfers
2456 * and connect data socket
2458 * RETURNS
2459 * TRUE on success
2460 * FALSE on failure
2463 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2465 INT nResCode;
2466 BOOL bSuccess = FALSE;
2468 TRACE("\n");
2469 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2470 goto lend;
2472 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2473 if (nResCode)
2475 if (nResCode == 227)
2477 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2478 LPSTR p;
2479 int f[6];
2480 int i;
2481 char *pAddr, *pPort;
2482 INT nsocket = -1;
2483 struct sockaddr_in dataSocketAddress;
2485 p = lpszResponseBuffer+4; /* skip status code */
2487 /* do a very strict check; we can improve that later. */
2489 if (strncmp(p, "Entering Passive Mode", 21))
2491 ERR("unknown response '%.*s', aborting\n", 21, p);
2492 goto lend;
2494 p += 21; /* skip string */
2495 if ((*p++ != ' ') || (*p++ != '('))
2497 ERR("unknown response format, aborting\n");
2498 goto lend;
2501 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
2502 &f[4], &f[5]) != 6)
2504 ERR("unknown response address format '%s', aborting\n", p);
2505 goto lend;
2507 for (i=0; i < 6; i++)
2508 f[i] = f[i] & 0xff;
2510 dataSocketAddress = lpwfs->socketAddress;
2511 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2512 pPort = (char *)&(dataSocketAddress.sin_port);
2513 pAddr[0] = f[0];
2514 pAddr[1] = f[1];
2515 pAddr[2] = f[2];
2516 pAddr[3] = f[3];
2517 pPort[0] = f[4];
2518 pPort[1] = f[5];
2520 nsocket = socket(AF_INET,SOCK_STREAM,0);
2521 if (nsocket == -1)
2522 goto lend;
2524 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2526 ERR("can't connect passive FTP data port.\n");
2527 closesocket(nsocket);
2528 goto lend;
2530 lpwfs->pasvSocket = nsocket;
2531 bSuccess = TRUE;
2533 else
2534 FTP_SetResponseError(nResCode);
2537 lend:
2538 return bSuccess;
2542 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2544 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2546 if (!FTP_DoPassive(lpwfs))
2547 return FALSE;
2549 else
2551 if (!FTP_SendPort(lpwfs))
2552 return FALSE;
2554 return TRUE;
2558 /***********************************************************************
2559 * FTP_GetDataSocket (internal)
2561 * Either accepts an incoming data socket connection from the server
2562 * or just returns the already opened socket after a PASV command
2563 * in case of passive FTP.
2566 * RETURNS
2567 * TRUE on success
2568 * FALSE on failure
2571 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
2573 struct sockaddr_in saddr;
2574 socklen_t addrlen = sizeof(struct sockaddr);
2576 TRACE("\n");
2577 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2579 *nDataSocket = lpwfs->pasvSocket;
2581 else
2583 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
2584 closesocket(lpwfs->lstnSocket);
2585 lpwfs->lstnSocket = -1;
2587 return *nDataSocket != -1;
2591 /***********************************************************************
2592 * FTP_SendData (internal)
2594 * Send data to the server
2596 * RETURNS
2597 * TRUE on success
2598 * FALSE on failure
2601 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
2603 BY_HANDLE_FILE_INFORMATION fi;
2604 DWORD nBytesRead = 0;
2605 DWORD nBytesSent = 0;
2606 DWORD nTotalSent = 0;
2607 DWORD nBytesToSend, nLen;
2608 int nRC = 1;
2609 time_t s_long_time, e_long_time;
2610 LONG nSeconds;
2611 CHAR *lpszBuffer;
2613 TRACE("\n");
2614 lpszBuffer = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR)*DATA_PACKET_SIZE);
2615 memset(lpszBuffer, 0, sizeof(CHAR)*DATA_PACKET_SIZE);
2617 /* Get the size of the file. */
2618 GetFileInformationByHandle(hFile, &fi);
2619 time(&s_long_time);
2623 nBytesToSend = nBytesRead - nBytesSent;
2625 if (nBytesToSend <= 0)
2627 /* Read data from file. */
2628 nBytesSent = 0;
2629 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
2630 ERR("Failed reading from file\n");
2632 if (nBytesRead > 0)
2633 nBytesToSend = nBytesRead;
2634 else
2635 break;
2638 nLen = DATA_PACKET_SIZE < nBytesToSend ?
2639 DATA_PACKET_SIZE : nBytesToSend;
2640 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
2642 if (nRC != -1)
2644 nBytesSent += nRC;
2645 nTotalSent += nRC;
2648 /* Do some computation to display the status. */
2649 time(&e_long_time);
2650 nSeconds = e_long_time - s_long_time;
2651 if( nSeconds / 60 > 0 )
2653 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
2654 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
2655 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
2657 else
2659 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
2660 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
2661 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
2663 } while (nRC != -1);
2665 TRACE("file transfer complete!\n");
2667 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2669 return nTotalSent;
2673 /***********************************************************************
2674 * FTP_SendRetrieve (internal)
2676 * Send request to retrieve a file
2678 * RETURNS
2679 * Number of bytes to be received on success
2680 * 0 on failure
2683 static DWORD FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2685 INT nResCode;
2686 DWORD nResult = 0;
2688 TRACE("\n");
2689 if (!FTP_InitListenSocket(lpwfs))
2690 goto lend;
2692 if (!FTP_SendType(lpwfs, dwType))
2693 goto lend;
2695 if (!FTP_SendPortOrPasv(lpwfs))
2696 goto lend;
2698 if (!FTP_GetFileSize(lpwfs, lpszRemoteFile, &nResult))
2699 goto lend;
2701 TRACE("Waiting to receive %d bytes\n", nResult);
2703 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0))
2704 goto lend;
2706 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2707 if ((nResCode != 125) && (nResCode != 150)) {
2708 /* That means that we got an error getting the file. */
2709 nResult = 0;
2712 lend:
2713 if (0 == nResult && lpwfs->lstnSocket != -1)
2715 closesocket(lpwfs->lstnSocket);
2716 lpwfs->lstnSocket = -1;
2719 return nResult;
2723 /***********************************************************************
2724 * FTP_RetrieveData (internal)
2726 * Retrieve data from server
2728 * RETURNS
2729 * TRUE on success
2730 * FALSE on failure
2733 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile)
2735 DWORD nBytesWritten;
2736 DWORD nBytesReceived = 0;
2737 INT nRC = 0;
2738 CHAR *lpszBuffer;
2740 TRACE("\n");
2742 if (INVALID_HANDLE_VALUE == hFile)
2743 return FALSE;
2745 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2746 if (NULL == lpszBuffer)
2748 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2749 return FALSE;
2752 while (nBytesReceived < nBytes && nRC != -1)
2754 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
2755 if (nRC != -1)
2757 /* other side closed socket. */
2758 if (nRC == 0)
2759 goto recv_end;
2760 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
2761 nBytesReceived += nRC;
2764 TRACE("%d bytes of %d (%d%%)\r", nBytesReceived, nBytes,
2765 nBytesReceived * 100 / nBytes);
2768 TRACE("Data transfer complete\n");
2769 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2771 recv_end:
2772 return (nRC != -1);
2776 /***********************************************************************
2777 * FTP_CloseSessionHandle (internal)
2779 * Deallocate session handle
2781 * RETURNS
2782 * TRUE on success
2783 * FALSE on failure
2786 static void FTP_CloseSessionHandle(LPWININETHANDLEHEADER hdr)
2788 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2790 TRACE("\n");
2792 WININET_Release(&lpwfs->lpAppInfo->hdr);
2794 if (lpwfs->download_in_progress != NULL)
2795 lpwfs->download_in_progress->session_deleted = TRUE;
2797 if (lpwfs->sndSocket != -1)
2798 closesocket(lpwfs->sndSocket);
2800 if (lpwfs->lstnSocket != -1)
2801 closesocket(lpwfs->lstnSocket);
2803 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2804 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2805 HeapFree(GetProcessHeap(), 0, lpwfs);
2809 /***********************************************************************
2810 * FTP_FindNextFileW (Internal)
2812 * Continues a file search from a previous call to FindFirstFile
2814 * RETURNS
2815 * TRUE on success
2816 * FALSE on failure
2819 BOOL WINAPI FTP_FindNextFileW(LPWININETFTPFINDNEXTW lpwh, LPVOID lpvFindData)
2821 BOOL bSuccess = TRUE;
2822 LPWIN32_FIND_DATAW lpFindFileData;
2824 TRACE("index(%d) size(%d)\n", lpwh->index, lpwh->size);
2826 assert (lpwh->hdr.htype == WH_HFTPFINDNEXT);
2828 /* Clear any error information */
2829 INTERNET_SetLastError(0);
2831 lpFindFileData = (LPWIN32_FIND_DATAW) lpvFindData;
2832 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
2834 if (lpwh->index >= lpwh->size)
2836 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
2837 bSuccess = FALSE;
2838 goto lend;
2841 FTP_ConvertFileProp(&lpwh->lpafp[lpwh->index], lpFindFileData);
2842 lpwh->index++;
2844 TRACE("\nName: %s\nSize: %d\n", debugstr_w(lpFindFileData->cFileName), lpFindFileData->nFileSizeLow);
2846 lend:
2848 if (lpwh->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2850 INTERNET_ASYNC_RESULT iar;
2852 iar.dwResult = (DWORD)bSuccess;
2853 iar.dwError = iar.dwError = bSuccess ? ERROR_SUCCESS :
2854 INTERNET_GetLastError();
2856 INTERNET_SendCallback(&lpwh->hdr, lpwh->hdr.dwContext,
2857 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2858 sizeof(INTERNET_ASYNC_RESULT));
2861 return bSuccess;
2865 /***********************************************************************
2866 * FTP_CloseFindNextHandle (internal)
2868 * Deallocate session handle
2870 * RETURNS
2871 * TRUE on success
2872 * FALSE on failure
2875 static void FTP_CloseFindNextHandle(LPWININETHANDLEHEADER hdr)
2877 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
2878 DWORD i;
2880 TRACE("\n");
2882 WININET_Release(&lpwfn->lpFtpSession->hdr);
2884 for (i = 0; i < lpwfn->size; i++)
2886 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
2889 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
2890 HeapFree(GetProcessHeap(), 0, lpwfn);
2893 /***********************************************************************
2894 * FTP_CloseFileTransferHandle (internal)
2896 * Closes the file transfer handle. This also 'cleans' the data queue of
2897 * the 'transfer conplete' message (this is a bit of a hack though :-/ )
2900 static void FTP_CloseFileTransferHandle(LPWININETHANDLEHEADER hdr)
2902 LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
2903 LPWININETFTPSESSIONW lpwfs = lpwh->lpFtpSession;
2904 INT nResCode;
2906 TRACE("\n");
2908 WININET_Release(&lpwh->lpFtpSession->hdr);
2910 if (!lpwh->session_deleted)
2911 lpwfs->download_in_progress = NULL;
2913 /* This just serves to flush the control socket of any spurrious lines written
2914 to it (like '226 Transfer complete.').
2916 Wonder what to do if the server sends us an error code though...
2918 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2920 if (lpwh->nDataSocket != -1)
2921 closesocket(lpwh->nDataSocket);
2923 HeapFree(GetProcessHeap(), 0, lpwh);
2926 /***********************************************************************
2927 * FTP_ReceiveFileList (internal)
2929 * Read file list from server
2931 * RETURNS
2932 * Handle to file list on success
2933 * NULL on failure
2936 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
2937 LPWIN32_FIND_DATAW lpFindFileData, DWORD dwContext)
2939 DWORD dwSize = 0;
2940 LPFILEPROPERTIESW lpafp = NULL;
2941 LPWININETFTPFINDNEXTW lpwfn = NULL;
2942 HINTERNET handle = 0;
2944 TRACE("(%p,%d,%s,%p,%d)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
2946 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
2948 if(lpFindFileData)
2949 FTP_ConvertFileProp(lpafp, lpFindFileData);
2951 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
2952 if (lpwfn)
2954 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
2955 lpwfn->hdr.dwContext = dwContext;
2956 lpwfn->hdr.dwRefCount = 1;
2957 lpwfn->hdr.destroy = FTP_CloseFindNextHandle;
2958 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
2959 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
2960 lpwfn->size = dwSize;
2961 lpwfn->lpafp = lpafp;
2963 WININET_AddRef( &lpwfs->hdr );
2964 lpwfn->lpFtpSession = lpwfs;
2966 handle = WININET_AllocHandle( &lpwfn->hdr );
2970 if( lpwfn )
2971 WININET_Release( &lpwfn->hdr );
2973 TRACE("Matched %d files\n", dwSize);
2974 return handle;
2978 /***********************************************************************
2979 * FTP_ConvertFileProp (internal)
2981 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
2983 * RETURNS
2984 * TRUE on success
2985 * FALSE on failure
2988 BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
2990 BOOL bSuccess = FALSE;
2992 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
2994 if (lpafp)
2996 /* Convert 'Unix' time to Windows time */
2997 RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
2998 (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
2999 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3000 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3002 /* Not all fields are filled in */
3003 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3004 lpFindFileData->nFileSizeLow = lpafp->nSize;
3006 if (lpafp->bIsDirectory)
3007 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3009 if (lpafp->lpszName)
3010 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3012 bSuccess = TRUE;
3015 return bSuccess;
3018 /***********************************************************************
3019 * FTP_ParseNextFile (internal)
3021 * Parse the next line in file listing
3023 * RETURNS
3024 * TRUE on success
3025 * FALSE on failure
3027 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3029 static const char szSpace[] = " \t";
3030 DWORD nBufLen;
3031 char *pszLine;
3032 char *pszToken;
3033 char *pszTmp;
3034 BOOL found = FALSE;
3035 int i;
3037 lpfp->lpszName = NULL;
3038 do {
3039 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3040 return FALSE;
3042 pszToken = strtok(pszLine, szSpace);
3043 /* ls format
3044 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3046 * For instance:
3047 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3049 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3050 if(!FTP_ParsePermission(pszToken, lpfp))
3051 lpfp->bIsDirectory = FALSE;
3052 for(i=0; i<=3; i++) {
3053 if(!(pszToken = strtok(NULL, szSpace)))
3054 break;
3056 if(!pszToken) continue;
3057 if(lpfp->bIsDirectory) {
3058 TRACE("Is directory\n");
3059 lpfp->nSize = 0;
3061 else {
3062 TRACE("Size: %s\n", pszToken);
3063 lpfp->nSize = atol(pszToken);
3066 lpfp->tmLastModified.tm_sec = 0;
3067 lpfp->tmLastModified.tm_min = 0;
3068 lpfp->tmLastModified.tm_hour = 0;
3069 lpfp->tmLastModified.tm_mday = 0;
3070 lpfp->tmLastModified.tm_mon = 0;
3071 lpfp->tmLastModified.tm_year = 0;
3073 /* Determine month */
3074 pszToken = strtok(NULL, szSpace);
3075 if(!pszToken) continue;
3076 if(strlen(pszToken) >= 3) {
3077 pszToken[3] = 0;
3078 if((pszTmp = StrStrIA(szMonths, pszToken)))
3079 lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
3081 /* Determine day */
3082 pszToken = strtok(NULL, szSpace);
3083 if(!pszToken) continue;
3084 lpfp->tmLastModified.tm_mday = atoi(pszToken);
3085 /* Determine time or year */
3086 pszToken = strtok(NULL, szSpace);
3087 if(!pszToken) continue;
3088 if((pszTmp = strchr(pszToken, ':'))) {
3089 struct tm* apTM;
3090 time_t aTime;
3091 *pszTmp = 0;
3092 pszTmp++;
3093 lpfp->tmLastModified.tm_min = atoi(pszTmp);
3094 lpfp->tmLastModified.tm_hour = atoi(pszToken);
3095 time(&aTime);
3096 apTM = localtime(&aTime);
3097 lpfp->tmLastModified.tm_year = apTM->tm_year;
3099 else {
3100 lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
3101 lpfp->tmLastModified.tm_hour = 12;
3103 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3104 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3105 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3106 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3108 pszToken = strtok(NULL, szSpace);
3109 if(!pszToken) continue;
3110 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3111 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3113 /* NT way of parsing ... :
3115 07-13-03 08:55PM <DIR> sakpatch
3116 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3118 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3119 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3121 sscanf(pszToken, "%d-%d-%d",
3122 &lpfp->tmLastModified.tm_mon,
3123 &lpfp->tmLastModified.tm_mday,
3124 &lpfp->tmLastModified.tm_year);
3126 /* Hacky and bad Y2K protection :-) */
3127 if (lpfp->tmLastModified.tm_year < 70)
3128 lpfp->tmLastModified.tm_year += 100;
3130 pszToken = strtok(NULL, szSpace);
3131 if(!pszToken) continue;
3132 sscanf(pszToken, "%d:%d",
3133 &lpfp->tmLastModified.tm_hour,
3134 &lpfp->tmLastModified.tm_min);
3135 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3136 lpfp->tmLastModified.tm_hour += 12;
3138 lpfp->tmLastModified.tm_sec = 0;
3140 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3141 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3142 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3143 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3145 pszToken = strtok(NULL, szSpace);
3146 if(!pszToken) continue;
3147 if(!strcasecmp(pszToken, "<DIR>")) {
3148 lpfp->bIsDirectory = TRUE;
3149 lpfp->nSize = 0;
3150 TRACE("Is directory\n");
3152 else {
3153 lpfp->bIsDirectory = FALSE;
3154 lpfp->nSize = atol(pszToken);
3155 TRACE("Size: %d\n", lpfp->nSize);
3158 pszToken = strtok(NULL, szSpace);
3159 if(!pszToken) continue;
3160 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3161 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3163 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3164 else if(pszToken[0] == '+') {
3165 FIXME("EPLF Format not implemented\n");
3168 if(lpfp->lpszName) {
3169 if((lpszSearchFile == NULL) ||
3170 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3171 found = TRUE;
3172 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3174 else {
3175 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3176 lpfp->lpszName = NULL;
3179 } while(!found);
3180 return TRUE;
3183 /***********************************************************************
3184 * FTP_ParseDirectory (internal)
3186 * Parse string of directory information
3188 * RETURNS
3189 * TRUE on success
3190 * FALSE on failure
3192 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3193 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3195 BOOL bSuccess = TRUE;
3196 INT sizeFilePropArray = 500;/*20; */
3197 INT indexFilePropArray = -1;
3199 TRACE("\n");
3201 /* Allocate intial file properties array */
3202 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3203 if (!*lpafp)
3204 return FALSE;
3206 do {
3207 if (indexFilePropArray+1 >= sizeFilePropArray)
3209 LPFILEPROPERTIESW tmpafp;
3211 sizeFilePropArray *= 2;
3212 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3213 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3214 if (NULL == tmpafp)
3216 bSuccess = FALSE;
3217 break;
3220 *lpafp = tmpafp;
3222 indexFilePropArray++;
3223 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3225 if (bSuccess && indexFilePropArray)
3227 if (indexFilePropArray < sizeFilePropArray - 1)
3229 LPFILEPROPERTIESW tmpafp;
3231 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3232 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3233 if (NULL == tmpafp)
3234 *lpafp = tmpafp;
3236 *dwfp = indexFilePropArray;
3238 else
3240 HeapFree(GetProcessHeap(), 0, *lpafp);
3241 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3242 bSuccess = FALSE;
3245 return bSuccess;
3249 /***********************************************************************
3250 * FTP_ParsePermission (internal)
3252 * Parse permission string of directory information
3254 * RETURNS
3255 * TRUE on success
3256 * FALSE on failure
3259 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3261 BOOL bSuccess = TRUE;
3262 unsigned short nPermission = 0;
3263 INT nPos = 1;
3264 INT nLast = 9;
3266 TRACE("\n");
3267 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3269 bSuccess = FALSE;
3270 return bSuccess;
3273 lpfp->bIsDirectory = (*lpszPermission == 'd');
3276 switch (nPos)
3278 case 1:
3279 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3280 break;
3281 case 2:
3282 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3283 break;
3284 case 3:
3285 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3286 break;
3287 case 4:
3288 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3289 break;
3290 case 5:
3291 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3292 break;
3293 case 6:
3294 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3295 break;
3296 case 7:
3297 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3298 break;
3299 case 8:
3300 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3301 break;
3302 case 9:
3303 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3304 break;
3306 nPos++;
3307 }while (nPos <= nLast);
3309 lpfp->permissions = nPermission;
3310 return bSuccess;
3314 /***********************************************************************
3315 * FTP_SetResponseError (internal)
3317 * Set the appropriate error code for a given response from the server
3319 * RETURNS
3322 static DWORD FTP_SetResponseError(DWORD dwResponse)
3324 DWORD dwCode = 0;
3326 switch(dwResponse)
3328 case 421: /* Service not available - Server may be shutting down. */
3329 dwCode = ERROR_INTERNET_TIMEOUT;
3330 break;
3332 case 425: /* Cannot open data connection. */
3333 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3334 break;
3336 case 426: /* Connection closed, transer aborted. */
3337 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3338 break;
3340 case 500: /* Syntax error. Command unrecognized. */
3341 case 501: /* Syntax error. Error in parameters or arguments. */
3342 dwCode = ERROR_INTERNET_INCORRECT_FORMAT;
3343 break;
3345 case 530: /* Not logged in. Login incorrect. */
3346 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3347 break;
3349 case 550: /* File action not taken. File not found or no access. */
3350 dwCode = ERROR_INTERNET_ITEM_NOT_FOUND;
3351 break;
3353 case 450: /* File action not taken. File may be busy. */
3354 case 451: /* Action aborted. Server error. */
3355 case 452: /* Action not taken. Insufficient storage space on server. */
3356 case 502: /* Command not implemented. */
3357 case 503: /* Bad sequence of command. */
3358 case 504: /* Command not implemented for that parameter. */
3359 case 532: /* Need account for storing files */
3360 case 551: /* Requested action aborted. Page type unknown */
3361 case 552: /* Action aborted. Exceeded storage allocation */
3362 case 553: /* Action not taken. File name not allowed. */
3364 default:
3365 dwCode = ERROR_INTERNET_INTERNAL_ERROR;
3366 break;
3369 INTERNET_SetLastError(dwCode);
3370 return dwCode;