2 * WININET - Ftp implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2004 Mike McCormack for CodeWeavers
6 * Copyright 2004 Kevin Koltzau
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
30 #include "wine/port.h"
37 #include <sys/types.h>
38 #ifdef HAVE_SYS_SOCKET_H
39 # include <sys/socket.h>
58 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
63 #define DATA_PACKET_SIZE 0x2000
68 /* FTP commands with arguments. */
84 /* FTP commands without arguments. */
93 static const CHAR *szFtpCommands[] = {
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
157 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
158 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
160 LPWSTR lpwzLocalFile;
161 LPWSTR lpwzNewRemoteFile;
164 lpwzLocalFile = lpszLocalFile?WININET_strdup_AtoW(lpszLocalFile):NULL;
165 lpwzNewRemoteFile = lpszNewRemoteFile?WININET_strdup_AtoW(lpszNewRemoteFile):NULL;
166 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
168 HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
169 HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
173 /***********************************************************************
174 * FtpPutFileW (WININET.@)
176 * Uploads a file to the FTP server
183 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
184 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
186 LPWININETFTPSESSIONW lpwfs;
187 LPWININETAPPINFOW hIC = NULL;
190 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
191 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
193 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
197 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
198 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
200 WORKREQUEST workRequest;
201 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
203 workRequest.asyncall = FTPPUTFILEW;
204 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
205 req->lpszLocalFile = WININET_strdupW(lpszLocalFile);
206 req->lpszNewRemoteFile = WININET_strdupW(lpszNewRemoteFile);
207 req->dwFlags = dwFlags;
208 req->dwContext = dwContext;
210 r = INTERNET_AsyncCall(&workRequest);
214 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
215 lpszNewRemoteFile, dwFlags, dwContext);
220 WININET_Release( &lpwfs->hdr );
225 /***********************************************************************
226 * FTP_FtpPutFileW (Internal)
228 * Uploads a file to the FTP server
235 BOOL WINAPI FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
236 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
239 BOOL bSuccess = FALSE;
240 LPWININETAPPINFOW hIC = NULL;
243 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
245 if (!lpszLocalFile || !lpszNewRemoteFile)
247 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
251 assert( WH_HFTPSESSION == lpwfs->hdr.htype);
253 /* Clear any error information */
254 INTERNET_SetLastError(0);
255 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
257 /* Open file to be uploaded */
258 if (INVALID_HANDLE_VALUE ==
259 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
261 INTERNET_SetLastError(ERROR_FILE_NOT_FOUND);
265 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
267 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
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);
282 FTP_SetResponseError(nResCode);
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));
308 /***********************************************************************
309 * FtpSetCurrentDirectoryA (WININET.@)
311 * Change the working directory on the FTP server
318 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
320 LPWSTR lpwzDirectory;
323 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
324 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
325 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
330 /***********************************************************************
331 * FtpSetCurrentDirectoryW (WININET.@)
333 * Change the working directory on the FTP server
340 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
342 LPWININETFTPSESSIONW lpwfs = NULL;
343 LPWININETAPPINFOW hIC = NULL;
348 SetLastError(ERROR_INVALID_PARAMETER);
352 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
353 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
355 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
359 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
361 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
362 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
364 WORKREQUEST workRequest;
365 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
367 workRequest.asyncall = FTPSETCURRENTDIRECTORYW;
368 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
369 req = &workRequest.u.FtpSetCurrentDirectoryW;
370 req->lpszDirectory = WININET_strdupW(lpszDirectory);
372 r = INTERNET_AsyncCall(&workRequest);
376 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
381 WININET_Release( &lpwfs->hdr );
387 /***********************************************************************
388 * FTP_FtpSetCurrentDirectoryW (Internal)
390 * Change the working directory on the FTP server
397 BOOL WINAPI FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
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);
411 /* Clear any error information */
412 INTERNET_SetLastError(0);
414 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
415 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
416 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
419 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
426 FTP_SetResponseError(nResCode);
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));
443 /***********************************************************************
444 * FtpCreateDirectoryA (WININET.@)
446 * Create new directory on the FTP server
453 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
455 LPWSTR lpwzDirectory;
458 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
459 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
460 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
465 /***********************************************************************
466 * FtpCreateDirectoryW (WININET.@)
468 * Create new directory on the FTP server
475 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
477 LPWININETFTPSESSIONW lpwfs;
478 LPWININETAPPINFOW hIC = NULL;
481 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
482 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
484 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
488 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
489 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
491 WORKREQUEST workRequest;
492 struct WORKREQ_FTPCREATEDIRECTORYW *req;
494 workRequest.asyncall = FTPCREATEDIRECTORYW;
495 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
496 req = &workRequest.u.FtpCreateDirectoryW;
497 req->lpszDirectory = WININET_strdupW(lpszDirectory);
499 r = INTERNET_AsyncCall(&workRequest);
503 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
507 WININET_Release( &lpwfs->hdr );
513 /***********************************************************************
514 * FTP_FtpCreateDirectoryW (Internal)
516 * Create new directory on the FTP server
523 BOOL WINAPI FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
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);
537 /* Clear any error information */
538 INTERNET_SetLastError(0);
540 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
543 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
549 FTP_SetResponseError(nResCode);
553 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
554 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
556 INTERNET_ASYNC_RESULT iar;
558 iar.dwResult = (DWORD)bSuccess;
559 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
560 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
561 &iar, sizeof(INTERNET_ASYNC_RESULT));
567 /***********************************************************************
568 * FtpFindFirstFileA (WININET.@)
570 * Search the specified directory
573 * HINTERNET on success
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;
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);
591 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
597 /***********************************************************************
598 * FtpFindFirstFileW (WININET.@)
600 * Search the specified directory
603 * HINTERNET on success
607 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
608 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD dwContext)
610 LPWININETFTPSESSIONW lpwfs;
611 LPWININETAPPINFOW hIC = NULL;
614 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
615 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
617 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
621 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
622 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
624 WORKREQUEST workRequest;
625 struct WORKREQ_FTPFINDFIRSTFILEW *req;
627 workRequest.asyncall = FTPFINDFIRSTFILEW;
628 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
629 req = &workRequest.u.FtpFindFirstFileW;
630 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : WININET_strdupW(lpszSearchFile);
631 req->lpFindFileData = lpFindFileData;
632 req->dwFlags = dwFlags;
633 req->dwContext= dwContext;
635 INTERNET_AsyncCall(&workRequest);
640 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
645 WININET_Release( &lpwfs->hdr );
651 /***********************************************************************
652 * FTP_FtpFindFirstFileW (Internal)
654 * Search the specified directory
657 * HINTERNET on success
661 HINTERNET WINAPI FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
662 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD dwContext)
665 LPWININETAPPINFOW hIC = NULL;
666 HINTERNET hFindNext = NULL;
670 assert(WH_HFTPSESSION == lpwfs->hdr.htype);
672 /* Clear any error information */
673 INTERNET_SetLastError(0);
675 if (!FTP_InitListenSocket(lpwfs))
678 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
681 if (!FTP_SendPortOrPasv(lpwfs))
684 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
685 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
686 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
689 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
692 if (nResCode == 125 || nResCode == 150)
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);
707 FTP_SetResponseError(nResCode);
711 if (lpwfs->lstnSocket != -1)
712 closesocket(lpwfs->lstnSocket);
714 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
716 INTERNET_ASYNC_RESULT iar;
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));
736 /***********************************************************************
737 * FtpGetCurrentDirectoryA (WININET.@)
739 * Retrieves the current directory
746 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
747 LPDWORD lpdwCurrentDirectory)
753 if(lpdwCurrentDirectory) {
754 len = *lpdwCurrentDirectory;
755 if(lpszCurrentDirectory)
757 dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
760 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
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);
777 /***********************************************************************
778 * FtpGetCurrentDirectoryW (WININET.@)
780 * Retrieves the current directory
787 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
788 LPDWORD lpdwCurrentDirectory)
790 LPWININETFTPSESSIONW lpwfs;
791 LPWININETAPPINFOW hIC = NULL;
794 TRACE("len(%ld)\n", *lpdwCurrentDirectory);
796 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
797 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
799 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
803 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
804 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
806 WORKREQUEST workRequest;
807 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
809 workRequest.asyncall = FTPGETCURRENTDIRECTORYW;
810 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
811 req = &workRequest.u.FtpGetCurrentDirectoryW;
812 req->lpszDirectory = lpszCurrentDirectory;
813 req->lpdwDirectory = lpdwCurrentDirectory;
815 r = INTERNET_AsyncCall(&workRequest);
819 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
820 lpdwCurrentDirectory);
825 WININET_Release( &lpwfs->hdr );
831 /***********************************************************************
832 * FTP_FtpGetCurrentDirectoryA (Internal)
834 * Retrieves the current directory
841 BOOL WINAPI FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
842 LPDWORD lpdwCurrentDirectory)
845 LPWININETAPPINFOW hIC = NULL;
846 DWORD bSuccess = FALSE;
848 TRACE("len(%ld)\n", *lpdwCurrentDirectory);
850 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
852 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
856 /* Clear any error information */
857 INTERNET_SetLastError(0);
859 ZeroMemory(lpszCurrentDirectory, *lpdwCurrentDirectory);
861 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
862 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
863 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
866 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
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])
885 len = lastpos - firstpos - 1;
886 lstrcpynW(lpszCurrentDirectory, &lpszResponseBuffer[firstpos+1], *lpdwCurrentDirectory);
887 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
888 *lpdwCurrentDirectory = len;
892 FTP_SetResponseError(nResCode);
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
915 * HINTERNET handle on success
919 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
920 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
926 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
927 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
928 HeapFree(GetProcessHeap(), 0, lpwzFileName);
933 /***********************************************************************
934 * FtpOpenFileW (WININET.@)
936 * Open a remote file for writing or reading
939 * HINTERNET handle on success
943 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
944 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
947 LPWININETFTPSESSIONW lpwfs;
948 LPWININETAPPINFOW hIC = NULL;
951 TRACE("(%p,%s,0x%08lx,0x%08lx,0x%08lx)\n", hFtpSession,
952 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
954 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
955 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
957 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
961 if (lpwfs->download_in_progress != NULL) {
962 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
965 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
966 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
968 WORKREQUEST workRequest;
969 struct WORKREQ_FTPOPENFILEW *req;
971 workRequest.asyncall = FTPOPENFILEW;
972 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
973 req = &workRequest.u.FtpOpenFileW;
974 req->lpszFilename = WININET_strdupW(lpszFileName);
975 req->dwAccess = fdwAccess;
976 req->dwFlags = dwFlags;
977 req->dwContext = dwContext;
979 INTERNET_AsyncCall(&workRequest);
984 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
989 WININET_Release( &lpwfs->hdr );
995 /***********************************************************************
996 * FTP_FtpOpenFileW (Internal)
998 * Open a remote file for writing or reading
1001 * HINTERNET handle on success
1005 HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
1006 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1010 BOOL bSuccess = FALSE;
1011 LPWININETFILE lpwh = NULL;
1012 LPWININETAPPINFOW hIC = NULL;
1013 HINTERNET handle = NULL;
1017 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1019 /* Clear any error information */
1020 INTERNET_SetLastError(0);
1022 if (GENERIC_READ == fdwAccess)
1024 /* Set up socket to retrieve data */
1025 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1027 else if (GENERIC_WRITE == fdwAccess)
1029 /* Set up socket to send data */
1030 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1033 /* Get data socket to server */
1034 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1036 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFILE));
1037 lpwh->hdr.htype = WH_HFILE;
1038 lpwh->hdr.dwFlags = dwFlags;
1039 lpwh->hdr.dwContext = dwContext;
1040 lpwh->hdr.lpwhparent = WININET_AddRef( &lpwfs->hdr );
1041 lpwh->hdr.dwRefCount = 1;
1042 lpwh->hdr.destroy = FTP_CloseFileTransferHandle;
1043 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1044 lpwh->nDataSocket = nDataSocket;
1045 lpwh->session_deleted = FALSE;
1047 handle = WININET_AllocHandle( &lpwh->hdr );
1051 /* Indicate that a download is currently in progress */
1052 lpwfs->download_in_progress = lpwh;
1055 if (lpwfs->lstnSocket != -1)
1056 closesocket(lpwfs->lstnSocket);
1058 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1059 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1061 INTERNET_ASYNC_RESULT iar;
1065 iar.dwResult = (DWORD)handle;
1066 iar.dwError = ERROR_SUCCESS;
1067 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1068 &iar, sizeof(INTERNET_ASYNC_RESULT));
1071 iar.dwResult = (DWORD)bSuccess;
1072 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1073 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1074 &iar, sizeof(INTERNET_ASYNC_RESULT));
1079 WININET_Release( &lpwh->hdr );
1085 /***********************************************************************
1086 * FtpGetFileA (WININET.@)
1088 * Retrieve file from the FTP server
1095 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1096 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1099 LPWSTR lpwzRemoteFile;
1103 lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
1104 lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
1105 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1106 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1107 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1108 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1113 /***********************************************************************
1114 * FtpGetFileW (WININET.@)
1116 * Retrieve file from the FTP server
1123 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1124 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1127 LPWININETFTPSESSIONW lpwfs;
1128 LPWININETAPPINFOW hIC = NULL;
1131 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
1132 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1134 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1138 if (lpwfs->download_in_progress != NULL) {
1139 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1143 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1144 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1146 WORKREQUEST workRequest;
1147 struct WORKREQ_FTPGETFILEW *req;
1149 workRequest.asyncall = FTPGETFILEW;
1150 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1151 req = &workRequest.u.FtpGetFileW;
1152 req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
1153 req->lpszNewFile = WININET_strdupW(lpszNewFile);
1154 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1155 req->fFailIfExists = fFailIfExists;
1156 req->dwFlags = dwInternetFlags;
1157 req->dwContext = dwContext;
1159 r = INTERNET_AsyncCall(&workRequest);
1163 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1164 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1169 WININET_Release( &lpwfs->hdr );
1175 /***********************************************************************
1176 * FTP_FtpGetFileW (Internal)
1178 * Retrieve file from the FTP server
1185 BOOL WINAPI FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1186 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1190 BOOL bSuccess = FALSE;
1192 LPWININETAPPINFOW hIC = NULL;
1194 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1196 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1198 /* Clear any error information */
1199 INTERNET_SetLastError(0);
1201 /* Ensure we can write to lpszNewfile by opening it */
1202 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1203 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1204 if (INVALID_HANDLE_VALUE == hFile)
1207 /* Set up socket to retrieve data */
1208 nBytes = FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags);
1214 /* Get data socket to server */
1215 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1220 FTP_RetrieveFileData(lpwfs, nDataSocket, nBytes, hFile);
1221 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1224 if (nResCode == 226)
1227 FTP_SetResponseError(nResCode);
1229 closesocket(nDataSocket);
1234 if (lpwfs->lstnSocket != -1)
1235 closesocket(lpwfs->lstnSocket);
1240 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1241 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1243 INTERNET_ASYNC_RESULT iar;
1245 iar.dwResult = (DWORD)bSuccess;
1246 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1247 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1248 &iar, sizeof(INTERNET_ASYNC_RESULT));
1254 /***********************************************************************
1255 * FtpGetFileSize (WININET.@)
1257 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1259 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1261 if (lpdwFileSizeHigh)
1262 *lpdwFileSizeHigh = 0;
1267 /***********************************************************************
1268 * FtpDeleteFileA (WININET.@)
1270 * Delete a file on the ftp server
1277 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1279 LPWSTR lpwzFileName;
1282 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1283 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1284 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1288 /***********************************************************************
1289 * FtpDeleteFileW (WININET.@)
1291 * Delete a file on the ftp server
1298 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1300 LPWININETFTPSESSIONW lpwfs;
1301 LPWININETAPPINFOW hIC = NULL;
1304 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1305 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1307 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1311 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1312 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1314 WORKREQUEST workRequest;
1315 struct WORKREQ_FTPDELETEFILEW *req;
1317 workRequest.asyncall = FTPDELETEFILEW;
1318 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1319 req = &workRequest.u.FtpDeleteFileW;
1320 req->lpszFilename = WININET_strdupW(lpszFileName);
1322 r = INTERNET_AsyncCall(&workRequest);
1326 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1331 WININET_Release( &lpwfs->hdr );
1336 /***********************************************************************
1337 * FTP_FtpDeleteFileW (Internal)
1339 * Delete a file on the ftp server
1346 BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName)
1349 BOOL bSuccess = FALSE;
1350 LPWININETAPPINFOW hIC = NULL;
1352 TRACE("%p\n", lpwfs);
1354 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1356 /* Clear any error information */
1357 INTERNET_SetLastError(0);
1359 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1362 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1365 if (nResCode == 250)
1368 FTP_SetResponseError(nResCode);
1371 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1372 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1374 INTERNET_ASYNC_RESULT iar;
1376 iar.dwResult = (DWORD)bSuccess;
1377 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1378 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1379 &iar, sizeof(INTERNET_ASYNC_RESULT));
1386 /***********************************************************************
1387 * FtpRemoveDirectoryA (WININET.@)
1389 * Remove a directory on the ftp server
1396 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1398 LPWSTR lpwzDirectory;
1401 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
1402 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1403 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1407 /***********************************************************************
1408 * FtpRemoveDirectoryW (WININET.@)
1410 * Remove a directory on the ftp server
1417 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1419 LPWININETFTPSESSIONW lpwfs;
1420 LPWININETAPPINFOW hIC = NULL;
1423 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1424 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1426 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1430 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1431 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1433 WORKREQUEST workRequest;
1434 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1436 workRequest.asyncall = FTPREMOVEDIRECTORYW;
1437 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1438 req = &workRequest.u.FtpRemoveDirectoryW;
1439 req->lpszDirectory = WININET_strdupW(lpszDirectory);
1441 r = INTERNET_AsyncCall(&workRequest);
1445 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1450 WININET_Release( &lpwfs->hdr );
1455 /***********************************************************************
1456 * FTP_FtpRemoveDirectoryW (Internal)
1458 * Remove a directory on the ftp server
1465 BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
1468 BOOL bSuccess = FALSE;
1469 LPWININETAPPINFOW hIC = NULL;
1473 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1475 /* Clear any error information */
1476 INTERNET_SetLastError(0);
1478 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1481 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1484 if (nResCode == 250)
1487 FTP_SetResponseError(nResCode);
1491 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1492 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1494 INTERNET_ASYNC_RESULT iar;
1496 iar.dwResult = (DWORD)bSuccess;
1497 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1498 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1499 &iar, sizeof(INTERNET_ASYNC_RESULT));
1506 /***********************************************************************
1507 * FtpRenameFileA (WININET.@)
1509 * Rename a file on the ftp server
1516 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1522 lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL;
1523 lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL;
1524 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1525 HeapFree(GetProcessHeap(), 0, lpwzSrc);
1526 HeapFree(GetProcessHeap(), 0, lpwzDest);
1530 /***********************************************************************
1531 * FtpRenameFileW (WININET.@)
1533 * Rename a file on the ftp server
1540 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
1542 LPWININETFTPSESSIONW lpwfs;
1543 LPWININETAPPINFOW hIC = NULL;
1546 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1547 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1549 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1553 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1554 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1556 WORKREQUEST workRequest;
1557 struct WORKREQ_FTPRENAMEFILEW *req;
1559 workRequest.asyncall = FTPRENAMEFILEW;
1560 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1561 req = &workRequest.u.FtpRenameFileW;
1562 req->lpszSrcFile = WININET_strdupW(lpszSrc);
1563 req->lpszDestFile = WININET_strdupW(lpszDest);
1565 r = INTERNET_AsyncCall(&workRequest);
1569 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
1574 WININET_Release( &lpwfs->hdr );
1579 /***********************************************************************
1580 * FTP_FtpRenameFileW (Internal)
1582 * Rename a file on the ftp server
1589 BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs,
1590 LPCWSTR lpszSrc, LPCWSTR lpszDest)
1593 BOOL bSuccess = FALSE;
1594 LPWININETAPPINFOW hIC = NULL;
1598 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1600 /* Clear any error information */
1601 INTERNET_SetLastError(0);
1603 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
1606 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1607 if (nResCode == 350)
1609 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
1612 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1615 if (nResCode == 250)
1618 FTP_SetResponseError(nResCode);
1621 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1622 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1624 INTERNET_ASYNC_RESULT iar;
1626 iar.dwResult = (DWORD)bSuccess;
1627 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1628 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1629 &iar, sizeof(INTERNET_ASYNC_RESULT));
1635 /***********************************************************************
1636 * FtpCommandA (WININET.@)
1638 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1639 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1641 FIXME("%p %d 0x%08lx %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1642 debugstr_a(lpszCommand), dwContext, phFtpCommand);
1647 /***********************************************************************
1648 * FtpCommandW (WININET.@)
1650 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1651 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1653 FIXME("%p %d 0x%08lx %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1654 debugstr_w(lpszCommand), dwContext, phFtpCommand);
1659 /***********************************************************************
1660 * FTP_Connect (internal)
1662 * Connect to a ftp server
1665 * HINTERNET a session handle on success
1670 * Windows uses 'anonymous' as the username, when given a NULL username
1671 * and a NULL password. The password is first looked up in:
1673 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
1675 * If this entry is not present it uses the current username as the password.
1679 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
1680 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
1681 LPCWSTR lpszPassword, DWORD dwFlags, DWORD dwContext,
1682 DWORD dwInternalFlags)
1684 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
1685 'M','i','c','r','o','s','o','f','t','\\',
1686 'W','i','n','d','o','w','s','\\',
1687 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1688 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
1689 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
1690 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
1691 static const WCHAR szEmpty[] = {'\0'};
1692 struct sockaddr_in socketAddr;
1695 BOOL bSuccess = FALSE;
1696 LPWININETFTPSESSIONW lpwfs = NULL;
1697 HINTERNET handle = NULL;
1699 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
1700 hIC, debugstr_w(lpszServerName),
1701 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
1703 assert( hIC->hdr.htype == WH_HINIT );
1705 if (NULL == lpszUserName && NULL != lpszPassword)
1707 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1711 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
1714 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1718 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
1719 nServerPort = INTERNET_DEFAULT_FTP_PORT;
1721 lpwfs->hdr.htype = WH_HFTPSESSION;
1722 lpwfs->hdr.lpwhparent = WININET_AddRef( &hIC->hdr );
1723 lpwfs->hdr.dwFlags = dwFlags;
1724 lpwfs->hdr.dwContext = dwContext;
1725 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
1726 lpwfs->hdr.dwRefCount = 1;
1727 lpwfs->hdr.destroy = FTP_CloseSessionHandle;
1728 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
1729 lpwfs->download_in_progress = NULL;
1731 handle = WININET_AllocHandle( &lpwfs->hdr );
1734 ERR("Failed to alloc handle\n");
1735 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1739 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
1740 if(strchrW(hIC->lpszProxy, ' '))
1741 FIXME("Several proxies not implemented.\n");
1742 if(hIC->lpszProxyBypass)
1743 FIXME("Proxy bypass is ignored.\n");
1745 if ( !lpszUserName) {
1747 WCHAR szPassword[MAX_PATH];
1748 DWORD len = sizeof(szPassword);
1750 lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
1752 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
1753 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
1754 /* Nothing in the registry, get the username and use that as the password */
1755 if (!GetUserNameW(szPassword, &len)) {
1756 /* Should never get here, but use an empty password as failsafe */
1757 strcpyW(szPassword, szEmpty);
1762 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
1763 lpwfs->lpszPassword = WININET_strdupW(szPassword);
1766 lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
1769 lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
1771 lpwfs->lpszPassword = WININET_strdupW(szEmpty);
1774 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
1775 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
1777 INTERNET_ASYNC_RESULT iar;
1779 iar.dwResult = (DWORD)handle;
1780 iar.dwError = ERROR_SUCCESS;
1782 SendAsyncCallback(&hIC->hdr, dwContext,
1783 INTERNET_STATUS_HANDLE_CREATED, &iar,
1784 sizeof(INTERNET_ASYNC_RESULT));
1787 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
1788 (LPWSTR) lpszServerName, strlenW(lpszServerName));
1790 if (!GetAddress(lpszServerName, nServerPort, &socketAddr))
1792 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1796 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
1797 (LPWSTR) lpszServerName, strlenW(lpszServerName));
1799 nsocket = socket(AF_INET,SOCK_STREAM,0);
1802 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1806 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
1807 &socketAddr, sizeof(struct sockaddr_in));
1809 if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
1811 ERR("Unable to connect (%s)\n", strerror(errno));
1812 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1816 TRACE("Connected to server\n");
1817 lpwfs->sndSocket = nsocket;
1818 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
1819 &socketAddr, sizeof(struct sockaddr_in));
1821 sock_namelen = sizeof(lpwfs->socketAddress);
1822 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
1824 if (FTP_ConnectToHost(lpwfs))
1826 TRACE("Successfully logged into server\n");
1832 if (!bSuccess && nsocket == -1)
1833 closesocket(nsocket);
1835 if (!bSuccess && lpwfs)
1837 HeapFree(GetProcessHeap(), 0, lpwfs);
1838 WININET_FreeHandle( handle );
1843 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1845 INTERNET_ASYNC_RESULT iar;
1847 iar.dwResult = (DWORD)lpwfs;
1848 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1849 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1850 &iar, sizeof(INTERNET_ASYNC_RESULT));
1857 /***********************************************************************
1858 * FTP_ConnectToHost (internal)
1860 * Connect to a ftp server
1867 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
1870 BOOL bSuccess = FALSE;
1873 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1875 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
1878 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1881 /* Login successful... */
1882 if (nResCode == 230)
1884 /* User name okay, need password... */
1885 else if (nResCode == 331)
1886 bSuccess = FTP_SendPassword(lpwfs);
1887 /* Need account for login... */
1888 else if (nResCode == 332)
1889 bSuccess = FTP_SendAccount(lpwfs);
1891 FTP_SetResponseError(nResCode);
1894 TRACE("Returning %d\n", bSuccess);
1900 /***********************************************************************
1901 * FTP_SendCommandA (internal)
1903 * Send command to server
1910 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
1911 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext)
1915 DWORD nBytesSent = 0;
1919 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
1923 HINTERNET hHandle = WININET_FindHandle( hdr );
1926 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
1927 WININET_Release( hdr );
1931 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
1932 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
1933 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
1935 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1938 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
1939 dwParamLen ? lpszParam : "", szCRLF);
1941 TRACE("Sending (%s) len(%ld)\n", buf, len);
1942 while((nBytesSent < len) && (nRC != -1))
1944 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
1948 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
1952 HINTERNET hHandle = WININET_FindHandle( hdr );
1955 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_REQUEST_SENT,
1956 &nBytesSent, sizeof(DWORD));
1957 WININET_Release( hdr );
1961 TRACE("Sent %ld bytes\n", nBytesSent);
1965 /***********************************************************************
1966 * FTP_SendCommand (internal)
1968 * Send command to server
1975 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
1976 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext)
1979 LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
1980 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
1981 HeapFree(GetProcessHeap(), 0, lpszParamA);
1985 /***********************************************************************
1986 * FTP_ReceiveResponse (internal)
1988 * Receive response from server
1991 * Reply code on success
1995 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD dwContext)
1997 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2000 char firstprefix[5];
2001 BOOL multiline = FALSE;
2002 LPWININETAPPINFOW hIC = NULL;
2004 TRACE("socket(%d)\n", lpwfs->sndSocket);
2006 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
2007 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2011 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2018 if(lpszResponse[3] != '-')
2021 { /* Start of multiline repsonse. Loop until we get "nnn " */
2023 memcpy(firstprefix, lpszResponse, 3);
2024 firstprefix[3] = ' ';
2025 firstprefix[4] = '\0';
2030 if(!memcmp(firstprefix, lpszResponse, 4))
2038 rc = atoi(lpszResponse);
2040 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2041 &nRecv, sizeof(DWORD));
2045 TRACE("return %d\n", rc);
2050 /***********************************************************************
2051 * FTP_SendPassword (internal)
2053 * Send password to ftp server
2060 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2063 BOOL bSuccess = FALSE;
2066 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2069 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2072 TRACE("Received reply code %d\n", nResCode);
2073 /* Login successful... */
2074 if (nResCode == 230)
2076 /* Command not implemented, superfluous at the server site... */
2077 /* Need account for login... */
2078 else if (nResCode == 332)
2079 bSuccess = FTP_SendAccount(lpwfs);
2081 FTP_SetResponseError(nResCode);
2085 TRACE("Returning %d\n", bSuccess);
2090 /***********************************************************************
2091 * FTP_SendAccount (internal)
2100 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2103 BOOL bSuccess = FALSE;
2106 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2109 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2113 FTP_SetResponseError(nResCode);
2120 /***********************************************************************
2121 * FTP_SendStore (internal)
2123 * Send request to upload file to ftp server
2130 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2133 BOOL bSuccess = FALSE;
2136 if (!FTP_InitListenSocket(lpwfs))
2139 if (!FTP_SendType(lpwfs, dwType))
2142 if (!FTP_SendPortOrPasv(lpwfs))
2145 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2147 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2150 if (nResCode == 150 || nResCode == 125)
2153 FTP_SetResponseError(nResCode);
2157 if (!bSuccess && lpwfs->lstnSocket != -1)
2159 closesocket(lpwfs->lstnSocket);
2160 lpwfs->lstnSocket = -1;
2167 /***********************************************************************
2168 * FTP_InitListenSocket (internal)
2170 * Create a socket to listen for server response
2177 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2179 BOOL bSuccess = FALSE;
2180 size_t namelen = sizeof(struct sockaddr_in);
2184 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2185 if (lpwfs->lstnSocket == -1)
2187 TRACE("Unable to create listening socket\n");
2191 /* We obtain our ip addr from the name of the command channel socket */
2192 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2194 /* and get the system to assign us a port */
2195 lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
2197 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2199 TRACE("Unable to bind socket\n");
2203 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2205 TRACE("listen failed\n");
2209 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2213 if (!bSuccess && lpwfs->lstnSocket == -1)
2215 closesocket(lpwfs->lstnSocket);
2216 lpwfs->lstnSocket = -1;
2223 /***********************************************************************
2224 * FTP_SendType (internal)
2226 * Tell server type of data being transferred
2232 * W98SE doesn't cache the type that's currently set
2233 * (i.e. it sends it always),
2234 * so we probably don't want to do that either.
2236 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2239 WCHAR type[] = { 'I','\0' };
2240 BOOL bSuccess = FALSE;
2243 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2246 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2249 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2255 FTP_SetResponseError(nResCode);
2262 /***********************************************************************
2263 * FTP_GetFileSize (internal)
2265 * Retrieves from the server the size of the given file
2272 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2275 BOOL bSuccess = FALSE;
2279 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2282 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2285 if (nResCode == 213) {
2286 /* Now parses the output to get the actual file size */
2288 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2290 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2291 if (lpszResponseBuffer[i] == '\0') return FALSE;
2292 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2296 FTP_SetResponseError(nResCode);
2305 /***********************************************************************
2306 * FTP_SendPort (internal)
2308 * Tell server which port to use
2315 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2317 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2319 WCHAR szIPAddress[64];
2320 BOOL bSuccess = FALSE;
2323 sprintfW(szIPAddress, szIPFormat,
2324 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2325 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2326 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2327 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2328 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2329 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2331 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2334 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2337 if (nResCode == 200)
2340 FTP_SetResponseError(nResCode);
2348 /***********************************************************************
2349 * FTP_DoPassive (internal)
2351 * Tell server that we want to do passive transfers
2352 * and connect data socket
2359 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2362 BOOL bSuccess = FALSE;
2365 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2368 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2371 if (nResCode == 227)
2373 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2377 char *pAddr, *pPort;
2379 struct sockaddr_in dataSocketAddress;
2381 p = lpszResponseBuffer+4; /* skip status code */
2383 /* do a very strict check; we can improve that later. */
2385 if (strncmp(p, "Entering Passive Mode", 21))
2387 ERR("unknown response '%.*s', aborting\n", 21, p);
2390 p += 21; /* skip string */
2391 if ((*p++ != ' ') || (*p++ != '('))
2393 ERR("unknown response format, aborting\n");
2397 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
2400 ERR("unknown response address format '%s', aborting\n", p);
2403 for (i=0; i < 6; i++)
2406 dataSocketAddress = lpwfs->socketAddress;
2407 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2408 pPort = (char *)&(dataSocketAddress.sin_port);
2416 nsocket = socket(AF_INET,SOCK_STREAM,0);
2420 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2422 ERR("can't connect passive FTP data port.\n");
2423 closesocket(nsocket);
2426 lpwfs->pasvSocket = nsocket;
2430 FTP_SetResponseError(nResCode);
2438 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2440 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2442 if (!FTP_DoPassive(lpwfs))
2447 if (!FTP_SendPort(lpwfs))
2454 /***********************************************************************
2455 * FTP_GetDataSocket (internal)
2457 * Either accepts an incoming data socket connection from the server
2458 * or just returns the already opened socket after a PASV command
2459 * in case of passive FTP.
2467 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
2469 struct sockaddr_in saddr;
2470 size_t addrlen = sizeof(struct sockaddr);
2473 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2475 *nDataSocket = lpwfs->pasvSocket;
2479 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
2480 closesocket(lpwfs->lstnSocket);
2481 lpwfs->lstnSocket = -1;
2483 return *nDataSocket != -1;
2487 /***********************************************************************
2488 * FTP_SendData (internal)
2490 * Send data to the server
2497 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
2499 BY_HANDLE_FILE_INFORMATION fi;
2500 DWORD nBytesRead = 0;
2501 DWORD nBytesSent = 0;
2502 DWORD nTotalSent = 0;
2503 DWORD nBytesToSend, nLen;
2505 time_t s_long_time, e_long_time;
2510 lpszBuffer = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR)*DATA_PACKET_SIZE);
2511 memset(lpszBuffer, 0, sizeof(CHAR)*DATA_PACKET_SIZE);
2513 /* Get the size of the file. */
2514 GetFileInformationByHandle(hFile, &fi);
2519 nBytesToSend = nBytesRead - nBytesSent;
2521 if (nBytesToSend <= 0)
2523 /* Read data from file. */
2525 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
2526 ERR("Failed reading from file\n");
2529 nBytesToSend = nBytesRead;
2534 nLen = DATA_PACKET_SIZE < nBytesToSend ?
2535 DATA_PACKET_SIZE : nBytesToSend;
2536 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
2544 /* Do some computation to display the status. */
2546 nSeconds = e_long_time - s_long_time;
2547 if( nSeconds / 60 > 0 )
2549 TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld min %ld sec estimated remaining time %ld sec\n",
2550 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
2551 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
2555 TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld sec estimated remaining time %ld sec\n",
2556 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
2557 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
2559 } while (nRC != -1);
2561 TRACE("file transfer complete!\n");
2563 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2569 /***********************************************************************
2570 * FTP_SendRetrieve (internal)
2572 * Send request to retrieve a file
2575 * Number of bytes to be received on success
2579 static DWORD FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2585 if (!FTP_InitListenSocket(lpwfs))
2588 if (!FTP_SendType(lpwfs, dwType))
2591 if (!FTP_SendPortOrPasv(lpwfs))
2594 if (!FTP_GetFileSize(lpwfs, lpszRemoteFile, &nResult))
2597 TRACE("Waiting to receive %ld bytes\n", nResult);
2599 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0))
2602 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2603 if ((nResCode != 125) && (nResCode != 150)) {
2604 /* That means that we got an error getting the file. */
2609 if (0 == nResult && lpwfs->lstnSocket != -1)
2611 closesocket(lpwfs->lstnSocket);
2612 lpwfs->lstnSocket = -1;
2619 /***********************************************************************
2620 * FTP_RetrieveData (internal)
2622 * Retrieve data from server
2629 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile)
2631 DWORD nBytesWritten;
2632 DWORD nBytesReceived = 0;
2638 if (INVALID_HANDLE_VALUE == hFile)
2641 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2642 if (NULL == lpszBuffer)
2644 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2648 while (nBytesReceived < nBytes && nRC != -1)
2650 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
2653 /* other side closed socket. */
2656 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
2657 nBytesReceived += nRC;
2660 TRACE("%ld bytes of %ld (%ld%%)\r", nBytesReceived, nBytes,
2661 nBytesReceived * 100 / nBytes);
2664 TRACE("Data transfer complete\n");
2665 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2672 /***********************************************************************
2673 * FTP_CloseSessionHandle (internal)
2675 * Deallocate session handle
2682 static void FTP_CloseSessionHandle(LPWININETHANDLEHEADER hdr)
2684 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2688 if (lpwfs->download_in_progress != NULL)
2689 lpwfs->download_in_progress->session_deleted = TRUE;
2691 if (lpwfs->sndSocket != -1)
2692 closesocket(lpwfs->sndSocket);
2694 if (lpwfs->lstnSocket != -1)
2695 closesocket(lpwfs->lstnSocket);
2697 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2698 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2699 HeapFree(GetProcessHeap(), 0, lpwfs);
2703 /***********************************************************************
2704 * FTP_CloseFindNextHandle (internal)
2706 * Deallocate session handle
2713 static void FTP_CloseFindNextHandle(LPWININETHANDLEHEADER hdr)
2715 LPWININETFINDNEXTW lpwfn = (LPWININETFINDNEXTW) hdr;
2720 for (i = 0; i < lpwfn->size; i++)
2722 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
2725 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
2726 HeapFree(GetProcessHeap(), 0, lpwfn);
2729 /***********************************************************************
2730 * FTP_CloseFileTransferHandle (internal)
2732 * Closes the file transfer handle. This also 'cleans' the data queue of
2733 * the 'transfer conplete' message (this is a bit of a hack though :-/ )
2736 static void FTP_CloseFileTransferHandle(LPWININETHANDLEHEADER hdr)
2738 LPWININETFILE lpwh = (LPWININETFILE) hdr;
2739 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) lpwh->hdr.lpwhparent;
2744 if (!lpwh->session_deleted)
2745 lpwfs->download_in_progress = NULL;
2747 /* This just serves to flush the control socket of any spurrious lines written
2748 to it (like '226 Transfer complete.').
2750 Wonder what to do if the server sends us an error code though...
2752 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2754 if (lpwh->nDataSocket != -1)
2755 closesocket(lpwh->nDataSocket);
2757 HeapFree(GetProcessHeap(), 0, lpwh);
2760 /***********************************************************************
2761 * FTP_ReceiveFileList (internal)
2763 * Read file list from server
2766 * Handle to file list on success
2770 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
2771 LPWIN32_FIND_DATAW lpFindFileData, DWORD dwContext)
2774 LPFILEPROPERTIESW lpafp = NULL;
2775 LPWININETFINDNEXTW lpwfn = NULL;
2776 HINTERNET handle = 0;
2778 TRACE("(%p,%d,%s,%p,%ld)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
2780 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
2783 FTP_ConvertFileProp(lpafp, lpFindFileData);
2785 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFINDNEXTW));
2788 lpwfn->hdr.htype = WH_HFINDNEXT;
2789 lpwfn->hdr.lpwhparent = WININET_AddRef( &lpwfs->hdr );
2790 lpwfn->hdr.dwContext = dwContext;
2791 lpwfn->hdr.dwRefCount = 1;
2792 lpwfn->hdr.destroy = FTP_CloseFindNextHandle;
2793 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
2794 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
2795 lpwfn->size = dwSize;
2796 lpwfn->lpafp = lpafp;
2798 handle = WININET_AllocHandle( &lpwfn->hdr );
2803 WININET_Release( &lpwfn->hdr );
2805 TRACE("Matched %ld files\n", dwSize);
2810 /***********************************************************************
2811 * FTP_ConvertFileProp (internal)
2813 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
2820 BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
2822 BOOL bSuccess = FALSE;
2824 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
2828 /* Convert 'Unix' time to Windows time */
2829 RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
2830 (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
2831 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
2832 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
2834 /* Not all fields are filled in */
2835 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
2836 lpFindFileData->nFileSizeLow = lpafp->nSize;
2838 if (lpafp->bIsDirectory)
2839 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
2841 if (lpafp->lpszName)
2842 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
2850 /***********************************************************************
2851 * FTP_ParseNextFile (internal)
2853 * Parse the next line in file listing
2859 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
2861 static const char szSpace[] = " \t";
2869 lpfp->lpszName = NULL;
2871 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
2874 pszToken = strtok(pszLine, szSpace);
2876 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
2879 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
2881 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
2882 if(!FTP_ParsePermission(pszToken, lpfp))
2883 lpfp->bIsDirectory = FALSE;
2884 for(i=0; i<=3; i++) {
2885 if(!(pszToken = strtok(NULL, szSpace)))
2888 if(!pszToken) continue;
2889 if(lpfp->bIsDirectory) {
2890 TRACE("Is directory\n");
2894 TRACE("Size: %s\n", pszToken);
2895 lpfp->nSize = atol(pszToken);
2898 lpfp->tmLastModified.tm_sec = 0;
2899 lpfp->tmLastModified.tm_min = 0;
2900 lpfp->tmLastModified.tm_hour = 0;
2901 lpfp->tmLastModified.tm_mday = 0;
2902 lpfp->tmLastModified.tm_mon = 0;
2903 lpfp->tmLastModified.tm_year = 0;
2905 /* Determine month */
2906 pszToken = strtok(NULL, szSpace);
2907 if(!pszToken) continue;
2908 if(strlen(pszToken) >= 3) {
2910 if((pszTmp = StrStrIA(szMonths, pszToken)))
2911 lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
2914 pszToken = strtok(NULL, szSpace);
2915 if(!pszToken) continue;
2916 lpfp->tmLastModified.tm_mday = atoi(pszToken);
2917 /* Determine time or year */
2918 pszToken = strtok(NULL, szSpace);
2919 if(!pszToken) continue;
2920 if((pszTmp = strchr(pszToken, ':'))) {
2925 lpfp->tmLastModified.tm_min = atoi(pszTmp);
2926 lpfp->tmLastModified.tm_hour = atoi(pszToken);
2928 apTM = localtime(&aTime);
2929 lpfp->tmLastModified.tm_year = apTM->tm_year;
2932 lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
2933 lpfp->tmLastModified.tm_hour = 12;
2935 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
2936 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
2937 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
2938 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
2940 pszToken = strtok(NULL, szSpace);
2941 if(!pszToken) continue;
2942 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
2943 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
2945 /* NT way of parsing ... :
2947 07-13-03 08:55PM <DIR> sakpatch
2948 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
2950 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
2951 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
2953 sscanf(pszToken, "%d-%d-%d",
2954 &lpfp->tmLastModified.tm_mon,
2955 &lpfp->tmLastModified.tm_mday,
2956 &lpfp->tmLastModified.tm_year);
2958 /* Hacky and bad Y2K protection :-) */
2959 if (lpfp->tmLastModified.tm_year < 70)
2960 lpfp->tmLastModified.tm_year += 100;
2962 pszToken = strtok(NULL, szSpace);
2963 if(!pszToken) continue;
2964 sscanf(pszToken, "%d:%d",
2965 &lpfp->tmLastModified.tm_hour,
2966 &lpfp->tmLastModified.tm_min);
2967 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
2968 lpfp->tmLastModified.tm_hour += 12;
2970 lpfp->tmLastModified.tm_sec = 0;
2972 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
2973 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
2974 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
2975 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
2977 pszToken = strtok(NULL, szSpace);
2978 if(!pszToken) continue;
2979 if(!strcasecmp(pszToken, "<DIR>")) {
2980 lpfp->bIsDirectory = TRUE;
2982 TRACE("Is directory\n");
2985 lpfp->bIsDirectory = FALSE;
2986 lpfp->nSize = atol(pszToken);
2987 TRACE("Size: %ld\n", lpfp->nSize);
2990 pszToken = strtok(NULL, szSpace);
2991 if(!pszToken) continue;
2992 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
2993 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
2995 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
2996 else if(pszToken[0] == '+') {
2997 FIXME("EPLF Format not implemented\n");
3000 if(lpfp->lpszName) {
3001 if((lpszSearchFile == NULL) ||
3002 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3004 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3007 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3008 lpfp->lpszName = NULL;
3015 /***********************************************************************
3016 * FTP_ParseDirectory (internal)
3018 * Parse string of directory information
3024 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3025 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3027 BOOL bSuccess = TRUE;
3028 INT sizeFilePropArray = 500;/*20; */
3029 INT indexFilePropArray = -1;
3033 /* Allocate intial file properties array */
3034 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3039 if (indexFilePropArray+1 >= sizeFilePropArray)
3041 LPFILEPROPERTIESW tmpafp;
3043 sizeFilePropArray *= 2;
3044 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3045 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3054 indexFilePropArray++;
3055 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3057 if (bSuccess && indexFilePropArray)
3059 if (indexFilePropArray < sizeFilePropArray - 1)
3061 LPFILEPROPERTIESW tmpafp;
3063 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3064 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3068 *dwfp = indexFilePropArray;
3072 HeapFree(GetProcessHeap(), 0, *lpafp);
3073 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3081 /***********************************************************************
3082 * FTP_ParsePermission (internal)
3084 * Parse permission string of directory information
3091 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3093 BOOL bSuccess = TRUE;
3094 unsigned short nPermission = 0;
3099 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3105 lpfp->bIsDirectory = (*lpszPermission == 'd');
3111 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3114 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3117 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3120 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3123 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3126 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3129 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3132 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3135 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3139 }while (nPos <= nLast);
3141 lpfp->permissions = nPermission;
3146 /***********************************************************************
3147 * FTP_SetResponseError (internal)
3149 * Set the appropriate error code for a given response from the server
3154 static DWORD FTP_SetResponseError(DWORD dwResponse)
3160 case 421: /* Service not available - Server may be shutting down. */
3161 dwCode = ERROR_INTERNET_TIMEOUT;
3164 case 425: /* Cannot open data connection. */
3165 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3168 case 426: /* Connection closed, transer aborted. */
3169 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3172 case 500: /* Syntax error. Command unrecognized. */
3173 case 501: /* Syntax error. Error in parameters or arguments. */
3174 dwCode = ERROR_INTERNET_INCORRECT_FORMAT;
3177 case 530: /* Not logged in. Login incorrect. */
3178 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3181 case 550: /* File action not taken. File not found or no access. */
3182 dwCode = ERROR_INTERNET_ITEM_NOT_FOUND;
3185 case 450: /* File action not taken. File may be busy. */
3186 case 451: /* Action aborted. Server error. */
3187 case 452: /* Action not taken. Insufficient storage space on server. */
3188 case 502: /* Command not implemented. */
3189 case 503: /* Bad sequence of command. */
3190 case 504: /* Command not implemented for that parameter. */
3191 case 532: /* Need account for storing files */
3192 case 551: /* Requested action aborted. Page type unknown */
3193 case 552: /* Action aborted. Exceeded storage allocation */
3194 case 553: /* Action not taken. File name not allowed. */
3197 dwCode = ERROR_INTERNET_INTERNAL_ERROR;
3201 INTERNET_SetLastError(dwCode);