/* * WININET - Ftp implementation * * Copyright 1999 Corel Corporation * * Ulrich Czekalla * Noureddine Jemmali * * Copyright 2000 Andreas Mohr * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include "wine/port.h" #include #include #include #include #include #ifdef HAVE_SYS_SOCKET_H # include #endif #include #include #include "winbase.h" #include "wingdi.h" #include "winuser.h" #include "wininet.h" #include "winerror.h" #include "wine/debug.h" #include "internet.h" WINE_DEFAULT_DEBUG_CHANNEL(wininet); #define NOACCOUNT "noaccount" #define DATA_PACKET_SIZE 0x2000 #define szCRLF "\r\n" #define MAX_BACKLOG 5 typedef enum { /* FTP commands with arguments. */ FTP_CMD_ACCT, FTP_CMD_CWD, FTP_CMD_DELE, FTP_CMD_MKD, FTP_CMD_PASS, FTP_CMD_PORT, FTP_CMD_RETR, FTP_CMD_RMD, FTP_CMD_RNFR, FTP_CMD_RNTO, FTP_CMD_STOR, FTP_CMD_TYPE, FTP_CMD_USER, /* FTP commands without arguments. */ FTP_CMD_ABOR, FTP_CMD_LIST, FTP_CMD_NLST, FTP_CMD_PASV, FTP_CMD_PWD, FTP_CMD_QUIT, } FTP_COMMAND; static const CHAR *szFtpCommands[] = { "ACCT", "CWD", "DELE", "MKD", "PASS", "PORT", "RETR", "RMD", "RNFR", "RNTO", "STOR", "TYPE", "USER", "ABOR", "LIST", "NLST", "PASV", "PWD", "QUIT", }; static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam, INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext); BOOL FTP_SendStore(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType); BOOL FTP_GetDataSocket(LPWININETFTPSESSIONA lpwfs, LPINT nDataSocket); BOOL FTP_SendData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, HANDLE hFile); INT FTP_ReceiveResponse(INT nSocket, LPSTR lpszResponse, DWORD dwResponse, INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext); DWORD FTP_SendRetrieve(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType); BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile); BOOL FTP_InitListenSocket(LPWININETFTPSESSIONA lpwfs); BOOL FTP_ConnectToHost(LPWININETFTPSESSIONA lpwfs); BOOL FTP_SendPassword(LPWININETFTPSESSIONA lpwfs); BOOL FTP_SendAccount(LPWININETFTPSESSIONA lpwfs); BOOL FTP_SendType(LPWININETFTPSESSIONA lpwfs, DWORD dwType); BOOL FTP_SendPort(LPWININETFTPSESSIONA lpwfs); BOOL FTP_DoPassive(LPWININETFTPSESSIONA lpwfs); BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONA lpwfs); BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESA lpfp); BOOL FTP_ParseDirectory(LPWININETFTPSESSIONA lpwfs, INT nSocket, LPFILEPROPERTIESA *lpafp, LPDWORD dwfp); HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONA lpwfs, INT nSocket, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwContext); DWORD FTP_SetResponseError(DWORD dwResponse); inline static LPSTR FTP_strdup( LPCSTR str ) { LPSTR ret = HeapAlloc( GetProcessHeap(), 0, strlen(str) + 1 ); if (ret) strcpy( ret, str ); return ret; } /*********************************************************************** * FtpPutFileA (WININET.@) * * Uploads a file to the FTP server * * RETURNS * TRUE on success * FALSE on failure * */ BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile, LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext) { LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect; LPWININETAPPINFOA hIC = NULL; if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) { WORKREQUEST workRequest; workRequest.asyncall = FTPPUTFILEA; workRequest.HFTPSESSION = (DWORD)hConnect; workRequest.LPSZLOCALFILE = (DWORD)FTP_strdup(lpszLocalFile); workRequest.LPSZNEWREMOTEFILE = (DWORD)FTP_strdup(lpszNewRemoteFile); workRequest.DWFLAGS = dwFlags; workRequest.DWCONTEXT = dwContext; return INTERNET_AsyncCall(&workRequest); } else { return FTP_FtpPutFileA(hConnect, lpszLocalFile, lpszNewRemoteFile, dwFlags, dwContext); } } /*********************************************************************** * FTP_FtpPutFileA (Internal) * * Uploads a file to the FTP server * * RETURNS * TRUE on success * FALSE on failure * */ BOOL WINAPI FTP_FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile, LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext) { HANDLE hFile = (HANDLE)NULL; BOOL bSuccess = FALSE; LPWININETAPPINFOA hIC = NULL; LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect; INT nResCode; TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", lpszLocalFile, lpszNewRemoteFile); if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } /* Clear any error information */ INTERNET_SetLastError(0); /* Open file to be uploaded */ if (INVALID_HANDLE_VALUE == (hFile = CreateFileA(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0))) { INTERNET_SetLastError(ERROR_FILE_NOT_FOUND); goto lend; } hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (hIC->lpfnStatusCB) hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0); if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags)) { INT nDataSocket; /* Get data socket to server */ if (FTP_GetDataSocket(lpwfs, &nDataSocket)) { FTP_SendData(lpwfs, nDataSocket, hFile); close(nDataSocket); nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0); if (nResCode) { if (nResCode == 226) bSuccess = TRUE; else FTP_SetResponseError(nResCode); } } } lend: if (lpwfs->lstnSocket != -1) close(lpwfs->lstnSocket); if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) { INTERNET_ASYNC_RESULT iar; iar.dwResult = (DWORD)bSuccess; iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar, sizeof(INTERNET_ASYNC_RESULT)); } if (hFile) CloseHandle(hFile); return bSuccess; } /*********************************************************************** * FtpSetCurrentDirectoryA (WININET.@) * * Change the working directory on the FTP server * * RETURNS * TRUE on success * FALSE on failure * */ BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory) { LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect; LPWININETAPPINFOA hIC = NULL; if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } TRACE("lpszDirectory(%s)\n", lpszDirectory); hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) { WORKREQUEST workRequest; workRequest.asyncall = FTPSETCURRENTDIRECTORYA; workRequest.HFTPSESSION = (DWORD)hConnect; workRequest.LPSZDIRECTORY = (DWORD)FTP_strdup(lpszDirectory); return INTERNET_AsyncCall(&workRequest); } else { return FTP_FtpSetCurrentDirectoryA(hConnect, lpszDirectory); } } /*********************************************************************** * FTP_FtpSetCurrentDirectoryA (Internal) * * Change the working directory on the FTP server * * RETURNS * TRUE on success * FALSE on failure * */ BOOL WINAPI FTP_FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory) { INT nResCode; LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect; LPWININETAPPINFOA hIC = NULL; DWORD bSuccess = FALSE; TRACE("lpszDirectory(%s)\n", lpszDirectory); if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } /* Clear any error information */ INTERNET_SetLastError(0); hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory, hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext)) goto lend; nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext); if (nResCode) { if (nResCode == 250) bSuccess = TRUE; else FTP_SetResponseError(nResCode); } lend: if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) { INTERNET_ASYNC_RESULT iar; iar.dwResult = (DWORD)bSuccess; iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR; hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar, sizeof(INTERNET_ASYNC_RESULT)); } return bSuccess; } /*********************************************************************** * FtpCreateDirectoryA (WININET.@) * * Create new directory on the FTP server * * RETURNS * TRUE on success * FALSE on failure * */ BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory) { LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect; LPWININETAPPINFOA hIC = NULL; if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) { WORKREQUEST workRequest; workRequest.asyncall = FTPCREATEDIRECTORYA; workRequest.HFTPSESSION = (DWORD)hConnect; workRequest.LPSZDIRECTORY = (DWORD)FTP_strdup(lpszDirectory); return INTERNET_AsyncCall(&workRequest); } else { return FTP_FtpCreateDirectoryA(hConnect, lpszDirectory); } } /*********************************************************************** * FTP_FtpCreateDirectoryA (Internal) * * Create new directory on the FTP server * * RETURNS * TRUE on success * FALSE on failure * */ BOOL WINAPI FTP_FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory) { INT nResCode; BOOL bSuccess = FALSE; LPWININETAPPINFOA hIC = NULL; LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect; TRACE("\n"); if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } /* Clear any error information */ INTERNET_SetLastError(0); if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0)) goto lend; nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0); if (nResCode) { if (nResCode == 257) bSuccess = TRUE; else FTP_SetResponseError(nResCode); } lend: hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) { INTERNET_ASYNC_RESULT iar; iar.dwResult = (DWORD)bSuccess; iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar, sizeof(INTERNET_ASYNC_RESULT)); } return bSuccess; } /*********************************************************************** * FtpFindFirstFileA (WININET.@) * * Search the specified directory * * RETURNS * HINTERNET on success * NULL on failure * */ HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect, LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD dwContext) { LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect; LPWININETAPPINFOA hIC = NULL; if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) { WORKREQUEST workRequest; workRequest.asyncall = FTPFINDFIRSTFILEA; workRequest.HFTPSESSION = (DWORD)hConnect; workRequest.LPSZSEARCHFILE = (DWORD)FTP_strdup(lpszSearchFile); workRequest.LPFINDFILEDATA = (DWORD)lpFindFileData; workRequest.DWFLAGS = dwFlags; workRequest.DWCONTEXT= dwContext; INTERNET_AsyncCall(&workRequest); return NULL; } else { return FTP_FtpFindFirstFileA(hConnect, lpszSearchFile, lpFindFileData, dwFlags, dwContext); } } /*********************************************************************** * FTP_FtpFindFirstFileA (Internal) * * Search the specified directory * * RETURNS * HINTERNET on success * NULL on failure * */ HINTERNET WINAPI FTP_FtpFindFirstFileA(HINTERNET hConnect, LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD dwContext) { INT nResCode; LPWININETAPPINFOA hIC = NULL; LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect; LPWININETFINDNEXTA hFindNext = NULL; TRACE("\n"); if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } /* Clear any error information */ INTERNET_SetLastError(0); if (!FTP_InitListenSocket(lpwfs)) goto lend; if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII)) goto lend; if (!FTP_SendPortOrPasv(lpwfs)) goto lend; hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, lpszSearchFile, hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext)) goto lend; nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext); if (nResCode) { if (nResCode == 125 || nResCode == 150) { INT nDataSocket; /* Get data socket to server */ if (FTP_GetDataSocket(lpwfs, &nDataSocket)) { hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpFindFileData, dwContext); nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext); if (nResCode != 226 && nResCode != 250) INTERNET_SetLastError(ERROR_NO_MORE_FILES); close(nDataSocket); } } else FTP_SetResponseError(nResCode); } lend: if (lpwfs->lstnSocket != -1) close(lpwfs->lstnSocket); if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) { INTERNET_ASYNC_RESULT iar; if (hFindNext) { iar.dwResult = (DWORD)hFindNext; iar.dwError = ERROR_SUCCESS; hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED, &iar, sizeof(INTERNET_ASYNC_RESULT)); } iar.dwResult = (DWORD)hFindNext; iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError(); hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar, sizeof(INTERNET_ASYNC_RESULT)); } return (HINTERNET)hFindNext; } /*********************************************************************** * FtpGetCurrentDirectoryA (WININET.@) * * Retrieves the current directory * * RETURNS * TRUE on success * FALSE on failure * */ BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory, LPDWORD lpdwCurrentDirectory) { LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; LPWININETAPPINFOA hIC = NULL; TRACE("len(%ld)\n", *lpdwCurrentDirectory); if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) { WORKREQUEST workRequest; workRequest.asyncall = FTPGETCURRENTDIRECTORYA; workRequest.HFTPSESSION = (DWORD)hFtpSession; workRequest.LPSZDIRECTORY = (DWORD)lpszCurrentDirectory; workRequest.LPDWDIRECTORY = (DWORD)lpdwCurrentDirectory; return INTERNET_AsyncCall(&workRequest); } else { return FTP_FtpGetCurrentDirectoryA(hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory); } } /*********************************************************************** * FTP_FtpGetCurrentDirectoryA (Internal) * * Retrieves the current directory * * RETURNS * TRUE on success * FALSE on failure * */ BOOL WINAPI FTP_FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory, LPDWORD lpdwCurrentDirectory) { INT nResCode; LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; LPWININETAPPINFOA hIC = NULL; DWORD bSuccess = FALSE; TRACE("len(%ld)\n", *lpdwCurrentDirectory); if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } /* Clear any error information */ INTERNET_SetLastError(0); ZeroMemory(lpszCurrentDirectory, *lpdwCurrentDirectory); hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL, hIC->lpfnStatusCB, hFtpSession, lpwfs->hdr.dwContext)) goto lend; nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, hIC->lpfnStatusCB, hFtpSession, lpwfs->hdr.dwContext); if (nResCode) { if (nResCode == 257) /* Extract directory name */ { INT firstpos, lastpos, len; LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer(); for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++) { if ('"' == lpszResponseBuffer[lastpos]) { if (!firstpos) firstpos = lastpos; else break; } } len = lastpos - firstpos - 1; strncpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos+1], len < *lpdwCurrentDirectory ? len : *lpdwCurrentDirectory); *lpdwCurrentDirectory = len; bSuccess = TRUE; } else FTP_SetResponseError(nResCode); } lend: if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) { INTERNET_ASYNC_RESULT iar; iar.dwResult = (DWORD)bSuccess; iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR; hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar, sizeof(INTERNET_ASYNC_RESULT)); } return (DWORD) bSuccess; } /*********************************************************************** * FtpOpenFileA (WININET.@) * * Open a remote file for writing or reading * * RETURNS * HINTERNET handle on success * NULL on failure * */ HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession, LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags, DWORD dwContext) { LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; LPWININETAPPINFOA hIC = NULL; if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) { WORKREQUEST workRequest; workRequest.asyncall = FTPOPENFILEA; workRequest.HFTPSESSION = (DWORD)hFtpSession; workRequest.LPSZFILENAME = (DWORD)FTP_strdup(lpszFileName); workRequest.FDWACCESS = fdwAccess; workRequest.DWFLAGS = dwFlags; workRequest.DWCONTEXT = dwContext; INTERNET_AsyncCall(&workRequest); return NULL; } else { return FTP_FtpOpenFileA(hFtpSession, lpszFileName, fdwAccess, dwFlags, dwContext); } } /*********************************************************************** * FTP_FtpOpenFileA (Internal) * * Open a remote file for writing or reading * * RETURNS * HINTERNET handle on success * NULL on failure * */ HINTERNET FTP_FtpOpenFileA(HINTERNET hFtpSession, LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags, DWORD dwContext) { INT nDataSocket; BOOL bSuccess = FALSE; LPWININETFILE hFile = NULL; LPWININETAPPINFOA hIC = NULL; LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; TRACE("\n"); if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } /* Clear any error information */ INTERNET_SetLastError(0); if (GENERIC_READ == fdwAccess) { /* Set up socket to retrieve data */ bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags); } else if (GENERIC_WRITE == fdwAccess) { /* Set up socket to send data */ bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags); } /* Get data socket to server */ if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket)) { hFile = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFILE)); hFile->hdr.htype = WH_HFILE; hFile->hdr.dwFlags = dwFlags; hFile->hdr.dwContext = dwContext; hFile->hdr.lpwhparent = hFtpSession; hFile->nDataSocket = nDataSocket; } if (lpwfs->lstnSocket != -1) close(lpwfs->lstnSocket); hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) { INTERNET_ASYNC_RESULT iar; if (hFile) { iar.dwResult = (DWORD)hFile; iar.dwError = ERROR_SUCCESS; hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED, &iar, sizeof(INTERNET_ASYNC_RESULT)); } iar.dwResult = (DWORD)bSuccess; iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar, sizeof(INTERNET_ASYNC_RESULT)); } return (HINTERNET)hFile; } /*********************************************************************** * FtpGetFileA (WININET.@) * * Retrieve file from the FTP server * * RETURNS * TRUE on success * FALSE on failure * */ BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile, BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags, DWORD dwContext) { LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hInternet; LPWININETAPPINFOA hIC = NULL; if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) { WORKREQUEST workRequest; workRequest.asyncall = FTPGETFILEA; workRequest.HFTPSESSION = (DWORD)hInternet; workRequest.LPSZREMOTEFILE = (DWORD)FTP_strdup(lpszRemoteFile); workRequest.LPSZNEWFILE = (DWORD)FTP_strdup(lpszNewFile); workRequest.DWLOCALFLAGSATTRIBUTE = dwLocalFlagsAttribute; workRequest.FFAILIFEXISTS = (DWORD)fFailIfExists; workRequest.DWFLAGS = dwInternetFlags; workRequest.DWCONTEXT = dwContext; return INTERNET_AsyncCall(&workRequest); } else { return FTP_FtpGetFileA(hInternet, lpszRemoteFile, lpszNewFile, fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext); } } /*********************************************************************** * FTP_FtpGetFileA (Internal) * * Retrieve file from the FTP server * * RETURNS * TRUE on success * FALSE on failure * */ BOOL WINAPI FTP_FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile, BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags, DWORD dwContext) { DWORD nBytes; BOOL bSuccess = FALSE; HANDLE hFile; LPWININETAPPINFOA hIC = NULL; LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hInternet; TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", lpszRemoteFile, lpszNewFile); if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } /* Clear any error information */ INTERNET_SetLastError(0); /* Ensure we can write to lpszNewfile by opening it */ hFile = CreateFileA(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ? CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0); if (INVALID_HANDLE_VALUE == hFile) goto lend; /* Set up socket to retrieve data */ nBytes = FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags); if (nBytes > 0) { INT nDataSocket; /* Get data socket to server */ if (FTP_GetDataSocket(lpwfs, &nDataSocket)) { INT nResCode; /* Receive data */ FTP_RetrieveFileData(lpwfs, nDataSocket, nBytes, hFile); nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0); if (nResCode) { if (nResCode == 226) bSuccess = TRUE; else FTP_SetResponseError(nResCode); } close(nDataSocket); } } lend: if (lpwfs->lstnSocket != -1) close(lpwfs->lstnSocket); if (hFile) CloseHandle(hFile); hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) { INTERNET_ASYNC_RESULT iar; iar.dwResult = (DWORD)bSuccess; iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); hIC->lpfnStatusCB(hInternet, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar, sizeof(INTERNET_ASYNC_RESULT)); } return bSuccess; } /*********************************************************************** * FtpDeleteFileA (WININET.@) * * Delete a file on the ftp server * * RETURNS * TRUE on success * FALSE on failure * */ BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName) { LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; LPWININETAPPINFOA hIC = NULL; if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) { WORKREQUEST workRequest; workRequest.asyncall = FTPRENAMEFILEA; workRequest.HFTPSESSION = (DWORD)hFtpSession; workRequest.LPSZFILENAME = (DWORD)FTP_strdup(lpszFileName); return INTERNET_AsyncCall(&workRequest); } else { return FTP_FtpDeleteFileA(hFtpSession, lpszFileName); } } /*********************************************************************** * FTP_FtpDeleteFileA (Internal) * * Delete a file on the ftp server * * RETURNS * TRUE on success * FALSE on failure * */ BOOL FTP_FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName) { INT nResCode; BOOL bSuccess = FALSE; LPWININETAPPINFOA hIC = NULL; LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; TRACE("0x%08lx\n", (ULONG) hFtpSession); if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } /* Clear any error information */ INTERNET_SetLastError(0); if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0)) goto lend; nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0); if (nResCode) { if (nResCode == 250) bSuccess = TRUE; else FTP_SetResponseError(nResCode); } lend: hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) { INTERNET_ASYNC_RESULT iar; iar.dwResult = (DWORD)bSuccess; iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar, sizeof(INTERNET_ASYNC_RESULT)); } return bSuccess; } /*********************************************************************** * FtpRemoveDirectoryA (WININET.@) * * Remove a directory on the ftp server * * RETURNS * TRUE on success * FALSE on failure * */ BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory) { LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; LPWININETAPPINFOA hIC = NULL; if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) { WORKREQUEST workRequest; workRequest.asyncall = FTPREMOVEDIRECTORYA; workRequest.HFTPSESSION = (DWORD)hFtpSession; workRequest.LPSZDIRECTORY = (DWORD)FTP_strdup(lpszDirectory); return INTERNET_AsyncCall(&workRequest); } else { return FTP_FtpRemoveDirectoryA(hFtpSession, lpszDirectory); } } /*********************************************************************** * FTP_FtpRemoveDirectoryA (Internal) * * Remove a directory on the ftp server * * RETURNS * TRUE on success * FALSE on failure * */ BOOL FTP_FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory) { INT nResCode; BOOL bSuccess = FALSE; LPWININETAPPINFOA hIC = NULL; LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; TRACE("\n"); if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } /* Clear any error information */ INTERNET_SetLastError(0); if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0)) goto lend; nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0); if (nResCode) { if (nResCode == 250) bSuccess = TRUE; else FTP_SetResponseError(nResCode); } lend: hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) { INTERNET_ASYNC_RESULT iar; iar.dwResult = (DWORD)bSuccess; iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar, sizeof(INTERNET_ASYNC_RESULT)); } return bSuccess; } /*********************************************************************** * FtpRenameFileA (WININET.@) * * Rename a file on the ftp server * * RETURNS * TRUE on success * FALSE on failure * */ BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest) { LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; LPWININETAPPINFOA hIC = NULL; if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) { WORKREQUEST workRequest; workRequest.asyncall = FTPRENAMEFILEA; workRequest.HFTPSESSION = (DWORD)hFtpSession; workRequest.LPSZSRCFILE = (DWORD)FTP_strdup(lpszSrc); workRequest.LPSZDESTFILE = (DWORD)FTP_strdup(lpszDest); return INTERNET_AsyncCall(&workRequest); } else { return FTP_FtpRenameFileA(hFtpSession, lpszSrc, lpszDest); } } /*********************************************************************** * FTP_FtpRenameFileA (Internal) * * Rename a file on the ftp server * * RETURNS * TRUE on success * FALSE on failure * */ BOOL FTP_FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest) { INT nResCode; BOOL bSuccess = FALSE; LPWININETAPPINFOA hIC = NULL; LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession; TRACE("\n"); if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); return FALSE; } /* Clear any error information */ INTERNET_SetLastError(0); if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0)) goto lend; nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0); if (nResCode == 350) { if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0)) goto lend; nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0); } if (nResCode == 250) bSuccess = TRUE; else FTP_SetResponseError(nResCode); lend: hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent; if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) { INTERNET_ASYNC_RESULT iar; iar.dwResult = (DWORD)bSuccess; iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar, sizeof(INTERNET_ASYNC_RESULT)); } return bSuccess; } /*********************************************************************** * FTP_Connect (internal) * * Connect to a ftp server * * RETURNS * HINTERNET a session handle on success * NULL on failure * */ HINTERNET FTP_Connect(HINTERNET hInternet, LPCSTR lpszServerName, INTERNET_PORT nServerPort, LPCSTR lpszUserName, LPCSTR lpszPassword, DWORD dwFlags, DWORD dwContext) { struct sockaddr_in socketAddr; struct hostent *phe = NULL; INT nsocket = -1, sock_namelen; LPWININETAPPINFOA hIC = NULL; BOOL bSuccess = FALSE; LPWININETFTPSESSIONA lpwfs = NULL; TRACE("0x%08lx Server(%s) Port(%d) User(%s) Paswd(%s)\n", (ULONG) hInternet, lpszServerName, nServerPort, lpszUserName, lpszPassword); if (((LPWININETHANDLEHEADER)hInternet)->htype != WH_HINIT) goto lerror; hIC = (LPWININETAPPINFOA) hInternet; if (NULL == lpszUserName && NULL != lpszPassword) { INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_USER_NAME); goto lerror; } if (nServerPort == INTERNET_INVALID_PORT_NUMBER) nServerPort = INTERNET_DEFAULT_FTP_PORT; if (hIC->lpfnStatusCB) hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_RESOLVING_NAME, (LPSTR) lpszServerName, strlen(lpszServerName)); if (!GetAddress(lpszServerName, nServerPort, &phe, &socketAddr)) { INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED); goto lerror; } if (hIC->lpfnStatusCB) hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_NAME_RESOLVED, (LPSTR) lpszServerName, strlen(lpszServerName)); nsocket = socket(AF_INET,SOCK_STREAM,0); if (nsocket == -1) { INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT); goto lerror; } if (hIC->lpfnStatusCB) hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER, &socketAddr, sizeof(struct sockaddr_in)); if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0) { ERR("Unable to connect (%s)\n", strerror(errno)); INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT); } else { TRACE("Connected to server\n"); if (hIC->lpfnStatusCB) hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER, &socketAddr, sizeof(struct sockaddr_in)); lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONA)); if (NULL == lpwfs) { INTERNET_SetLastError(ERROR_OUTOFMEMORY); goto lerror; } lpwfs->hdr.htype = WH_HFTPSESSION; lpwfs->hdr.dwFlags = dwFlags; lpwfs->hdr.dwContext = dwContext; lpwfs->hdr.lpwhparent = (LPWININETHANDLEHEADER)hInternet; lpwfs->sndSocket = nsocket; sock_namelen = sizeof(lpwfs->socketAddress); getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen); lpwfs->phostent = phe; if (NULL == lpszUserName) { lpwfs->lpszUserName = FTP_strdup("anonymous"); lpwfs->lpszPassword = FTP_strdup("user@server"); } else { lpwfs->lpszUserName = FTP_strdup(lpszUserName); lpwfs->lpszPassword = FTP_strdup(lpszPassword); } if (FTP_ConnectToHost(lpwfs)) { if (hIC->lpfnStatusCB) { INTERNET_ASYNC_RESULT iar; iar.dwResult = (DWORD)lpwfs; iar.dwError = ERROR_SUCCESS; hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_HANDLE_CREATED, &iar, sizeof(INTERNET_ASYNC_RESULT)); } TRACE("Successfully logged into server\n"); bSuccess = TRUE; } } lerror: if (!bSuccess && nsocket == -1) close(nsocket); if (!bSuccess && lpwfs) { HeapFree(GetProcessHeap(), 0, lpwfs); lpwfs = NULL; } if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB) { INTERNET_ASYNC_RESULT iar; iar.dwResult = (DWORD)lpwfs; iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar, sizeof(INTERNET_ASYNC_RESULT)); } return (HINTERNET) lpwfs; } /*********************************************************************** * FTP_ConnectToHost (internal) * * Connect to a ftp server * * RETURNS * TRUE on success * NULL on failure * */ BOOL FTP_ConnectToHost(LPWININETFTPSESSIONA lpwfs) { INT nResCode; BOOL bSuccess = FALSE; TRACE("\n"); FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0); if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0)) goto lend; nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0); if (nResCode) { /* Login successful... */ if (nResCode == 230) bSuccess = TRUE; /* User name okay, need password... */ else if (nResCode == 331) bSuccess = FTP_SendPassword(lpwfs); /* Need account for login... */ else if (nResCode == 332) bSuccess = FTP_SendAccount(lpwfs); else FTP_SetResponseError(nResCode); } TRACE("Returning %d\n", bSuccess); lend: return bSuccess; } /*********************************************************************** * FTP_SendCommand (internal) * * Send command to server * * RETURNS * TRUE on success * NULL on failure * */ BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam, INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext) { DWORD len; CHAR *buf; DWORD nBytesSent = 0; DWORD nRC = 0; BOOL bParamHasLen; TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket); if (lpfnStatusCB) lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0); bParamHasLen = lpszParam && strlen(lpszParam) > 0; len = (bParamHasLen ? strlen(lpszParam) : -1) + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF)+ 1; if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1))) { INTERNET_SetLastError(ERROR_OUTOFMEMORY); return FALSE; } sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], bParamHasLen ? " " : "", bParamHasLen ? lpszParam : "", szCRLF); TRACE("Sending (%s) len(%ld)\n", buf, len); while((nBytesSent < len) && (nRC != -1)) { nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0); nBytesSent += nRC; } HeapFree(GetProcessHeap(), 0, (LPVOID)buf); if (lpfnStatusCB) lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_REQUEST_SENT, &nBytesSent, sizeof(DWORD)); TRACE("Sent %ld bytes\n", nBytesSent); return (nRC != -1); } /*********************************************************************** * FTP_ReceiveResponse (internal) * * Receive response from server * * RETURNS * Reply code on success * 0 on failure * */ INT FTP_ReceiveResponse(INT nSocket, LPSTR lpszResponse, DWORD dwResponse, INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext) { DWORD nRecv; INT rc = 0; char firstprefix[5]; BOOL multiline = FALSE; TRACE("socket(%d) \n", nSocket); if (lpfnStatusCB) lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0); while(1) { nRecv = dwResponse; if (!INTERNET_GetNextLine(nSocket, lpszResponse, &nRecv)) goto lerror; if (nRecv >= 3) { if(!multiline) { if(lpszResponse[3] != '-') break; else { /* Start of multiline repsonse. Loop until we get "nnn " */ multiline = TRUE; memcpy(firstprefix, lpszResponse, 3); firstprefix[3] = ' '; firstprefix[4] = '\0'; } } else { if(!memcmp(firstprefix, lpszResponse, 4)) break; } } } if (nRecv >= 3) { lpszResponse[nRecv] = '\0'; rc = atoi(lpszResponse); if (lpfnStatusCB) lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED, &nRecv, sizeof(DWORD)); } lerror: TRACE("return %d\n", rc); return rc; } /*********************************************************************** * FTP_SendPassword (internal) * * Send password to ftp server * * RETURNS * TRUE on success * NULL on failure * */ BOOL FTP_SendPassword(LPWININETFTPSESSIONA lpwfs) { INT nResCode; BOOL bSuccess = FALSE; TRACE("\n"); if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0)) goto lend; nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0); if (nResCode) { TRACE("Received reply code %d\n", nResCode); /* Login successful... */ if (nResCode == 230) bSuccess = TRUE; /* Command not implemented, superfluous at the server site... */ /* Need account for login... */ else if (nResCode == 332) bSuccess = FTP_SendAccount(lpwfs); else FTP_SetResponseError(nResCode); } lend: TRACE("Returning %d\n", bSuccess); return bSuccess; } /*********************************************************************** * FTP_SendAccount (internal) * * * * RETURNS * TRUE on success * FALSE on failure * */ BOOL FTP_SendAccount(LPWININETFTPSESSIONA lpwfs) { INT nResCode; BOOL bSuccess = FALSE; TRACE("\n"); if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, NOACCOUNT, 0, 0, 0)) goto lend; nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0); if (nResCode) bSuccess = TRUE; else FTP_SetResponseError(nResCode); lend: return bSuccess; } /*********************************************************************** * FTP_SendStore (internal) * * Send request to upload file to ftp server * * RETURNS * TRUE on success * FALSE on failure * */ BOOL FTP_SendStore(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType) { INT nResCode; BOOL bSuccess = FALSE; TRACE("\n"); if (!FTP_InitListenSocket(lpwfs)) goto lend; if (!FTP_SendType(lpwfs, dwType)) goto lend; if (!FTP_SendPortOrPasv(lpwfs)) goto lend; if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0)) goto lend; nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0); if (nResCode) { if (nResCode == 150) bSuccess = TRUE; else FTP_SetResponseError(nResCode); } lend: if (!bSuccess && lpwfs->lstnSocket != -1) { close(lpwfs->lstnSocket); lpwfs->lstnSocket = -1; } return bSuccess; } /*********************************************************************** * FTP_InitListenSocket (internal) * * Create a socket to listen for server response * * RETURNS * TRUE on success * FALSE on failure * */ BOOL FTP_InitListenSocket(LPWININETFTPSESSIONA lpwfs) { BOOL bSuccess = FALSE; size_t namelen = sizeof(struct sockaddr_in); TRACE("\n"); lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0); if (lpwfs->lstnSocket == -1) { TRACE("Unable to create listening socket\n"); goto lend; } /* We obtain our ip addr from the name of the command channel socket */ lpwfs->lstnSocketAddress = lpwfs->socketAddress; /* and get the system to assign us a port */ lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0); if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1) { TRACE("Unable to bind socket\n"); goto lend; } if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1) { TRACE("listen failed\n"); goto lend; } if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1) bSuccess = TRUE; lend: if (!bSuccess && lpwfs->lstnSocket == -1) { close(lpwfs->lstnSocket); lpwfs->lstnSocket = -1; } return bSuccess; } /*********************************************************************** * FTP_SendType (internal) * * Tell server type of data being transferred * * RETURNS * TRUE on success * FALSE on failure * * W98SE doesn't cache the type that's currently set * (i.e. it sends it always), * so we probably don't want to do that either. */ BOOL FTP_SendType(LPWININETFTPSESSIONA lpwfs, DWORD dwType) { INT nResCode; CHAR type[2] = { "I\0" }; BOOL bSuccess = FALSE; TRACE("\n"); if (dwType & INTERNET_FLAG_TRANSFER_ASCII) *type = 'A'; if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0)) goto lend; nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0)/100; if (nResCode) { if (nResCode == 2) bSuccess = TRUE; else FTP_SetResponseError(nResCode); } lend: return bSuccess; } /*********************************************************************** * FTP_SendPort (internal) * * Tell server which port to use * * RETURNS * TRUE on success * FALSE on failure * */ BOOL FTP_SendPort(LPWININETFTPSESSIONA lpwfs) { INT nResCode; CHAR szIPAddress[64]; BOOL bSuccess = FALSE; TRACE("\n"); sprintf(szIPAddress, "%d,%d,%d,%d,%d,%d", lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF, (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8, (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16, (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24, lpwfs->lstnSocketAddress.sin_port & 0xFF, (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8); if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0)) goto lend; nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN,0, 0, 0); if (nResCode) { if (nResCode == 200) bSuccess = TRUE; else FTP_SetResponseError(nResCode); } lend: return bSuccess; } /*********************************************************************** * FTP_DoPassive (internal) * * Tell server that we want to do passive transfers * and connect data socket * * RETURNS * TRUE on success * FALSE on failure * */ BOOL FTP_DoPassive(LPWININETFTPSESSIONA lpwfs) { INT nResCode; BOOL bSuccess = FALSE; TRACE("\n"); if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0)) goto lend; nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN,0, 0, 0); if (nResCode) { if (nResCode == 227) { LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer(); LPSTR p; int f[6]; int i; char *pAddr, *pPort; INT nsocket = -1; struct sockaddr_in dataSocketAddress; p = lpszResponseBuffer+4; /* skip status code */ /* do a very strict check; we can improve that later. */ if (strncmp(p, "Entering Passive Mode", 21)) { ERR("unknown response '%.*s', aborting\n", 21, p); goto lend; } p += 21; /* skip string */ if ((*p++ != ' ') || (*p++ != '(')) { ERR("unknown response format, aborting\n"); goto lend; } if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3], &f[4], &f[5]) != 6) { ERR("unknown response address format '%s', aborting\n", p); goto lend; } for (i=0; i < 6; i++) f[i] = f[i] & 0xff; dataSocketAddress = lpwfs->socketAddress; pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr); pPort = (char *)&(dataSocketAddress.sin_port); pAddr[0] = f[0]; pAddr[1] = f[1]; pAddr[2] = f[2]; pAddr[3] = f[3]; pPort[0] = f[4]; pPort[1] = f[5]; nsocket = socket(AF_INET,SOCK_STREAM,0); if (nsocket == -1) goto lend; if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress))) { ERR("can't connect passive FTP data port.\n"); goto lend; } lpwfs->pasvSocket = nsocket; bSuccess = TRUE; } else FTP_SetResponseError(nResCode); } lend: return bSuccess; } BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONA lpwfs) { if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE) { if (!FTP_DoPassive(lpwfs)) return FALSE; } else { if (!FTP_SendPort(lpwfs)) return FALSE; } return TRUE; } /*********************************************************************** * FTP_GetDataSocket (internal) * * Either accepts an incoming data socket connection from the server * or just returns the already opened socket after a PASV command * in case of passive FTP. * * * RETURNS * TRUE on success * FALSE on failure * */ BOOL FTP_GetDataSocket(LPWININETFTPSESSIONA lpwfs, LPINT nDataSocket) { struct sockaddr_in saddr; size_t addrlen = sizeof(struct sockaddr); TRACE("\n"); if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE) { *nDataSocket = lpwfs->pasvSocket; } else { *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen); close(lpwfs->lstnSocket); lpwfs->lstnSocket = -1; } return *nDataSocket != -1; } /*********************************************************************** * FTP_SendData (internal) * * Send data to the server * * RETURNS * TRUE on success * FALSE on failure * */ BOOL FTP_SendData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, HANDLE hFile) { BY_HANDLE_FILE_INFORMATION fi; DWORD nBytesRead = 0; DWORD nBytesSent = 0; DWORD nTotalSent = 0; DWORD nBytesToSend, nLen, nRC = 1; time_t s_long_time, e_long_time; LONG nSeconds; CHAR *lpszBuffer; TRACE("\n"); lpszBuffer = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR)*DATA_PACKET_SIZE); memset(lpszBuffer, 0, sizeof(CHAR)*DATA_PACKET_SIZE); /* Get the size of the file. */ GetFileInformationByHandle(hFile, &fi); time(&s_long_time); do { nBytesToSend = nBytesRead - nBytesSent; if (nBytesToSend <= 0) { /* Read data from file. */ nBytesSent = 0; if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0)) ERR("Failed reading from file\n"); if (nBytesRead > 0) nBytesToSend = nBytesRead; else break; } nLen = DATA_PACKET_SIZE < nBytesToSend ? DATA_PACKET_SIZE : nBytesToSend; nRC = send(nDataSocket, lpszBuffer, nLen, 0); if (nRC != -1) { nBytesSent += nRC; nTotalSent += nRC; } /* Do some computation to display the status. */ time(&e_long_time); nSeconds = e_long_time - s_long_time; if( nSeconds / 60 > 0 ) { TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld min %ld sec estimated remainig time %ld sec\n", nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60, nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent ); } else { TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld sec estimated remainig time %ld sec\n", nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent); } } while (nRC != -1); TRACE("file transfer complete!\n"); if(lpszBuffer != NULL) HeapFree(GetProcessHeap(), 0, lpszBuffer); return nTotalSent; } /*********************************************************************** * FTP_SendRetrieve (internal) * * Send request to retrieve a file * * RETURNS * Number of bytes to be received on success * 0 on failure * */ DWORD FTP_SendRetrieve(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType) { INT nResCode; DWORD nResult = 0; TRACE("\n"); if (!FTP_InitListenSocket(lpwfs)) goto lend; if (!FTP_SendType(lpwfs, dwType)) goto lend; if (!FTP_SendPortOrPasv(lpwfs)) goto lend; if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)) goto lend; nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0); if (nResCode) { if (nResCode == 125 || nResCode == 150) { /* Parse size of data to be retrieved */ INT i, sizepos = -1; LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer(); for (i = strlen(lpszResponseBuffer) - 1; i >= 0; i--) { if ('(' == lpszResponseBuffer[i]) { sizepos = i; break; } } if (sizepos >= 0) { nResult = atol(&lpszResponseBuffer[sizepos+1]); TRACE("Waiting to receive %ld bytes\n", nResult); } } } lend: if (0 == nResult && lpwfs->lstnSocket != -1) { close(lpwfs->lstnSocket); lpwfs->lstnSocket = -1; } return nResult; } /*********************************************************************** * FTP_RetrieveData (internal) * * Retrieve data from server * * RETURNS * TRUE on success * FALSE on failure * */ BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile) { DWORD nBytesWritten; DWORD nBytesReceived = 0; INT nRC = 0; CHAR *lpszBuffer; TRACE("\n"); if (INVALID_HANDLE_VALUE == hFile) return FALSE; lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE); if (NULL == lpszBuffer) { INTERNET_SetLastError(ERROR_OUTOFMEMORY); return FALSE; } while (nBytesReceived < nBytes && nRC != -1) { nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0); if (nRC != -1) { /* other side closed socket. */ if (nRC == 0) goto recv_end; WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL); nBytesReceived += nRC; } TRACE("%ld bytes of %ld (%ld%%)\r", nBytesReceived, nBytes, nBytesReceived * 100 / nBytes); } TRACE("Data transfer complete\n"); if (NULL != lpszBuffer) HeapFree(GetProcessHeap(), 0, lpszBuffer); recv_end: return (nRC != -1); } /*********************************************************************** * FTP_CloseSessionHandle (internal) * * Deallocate session handle * * RETURNS * TRUE on success * FALSE on failure * */ BOOL FTP_CloseSessionHandle(LPWININETFTPSESSIONA lpwfs) { if (lpwfs->sndSocket != -1) close(lpwfs->sndSocket); if (lpwfs->lstnSocket != -1) close(lpwfs->lstnSocket); if (lpwfs->lpszPassword) HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword); if (lpwfs->lpszUserName) HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName); HeapFree(GetProcessHeap(), 0, lpwfs); return TRUE; } /*********************************************************************** * FTP_CloseSessionHandle (internal) * * Deallocate session handle * * RETURNS * TRUE on success * FALSE on failure * */ BOOL FTP_CloseFindNextHandle(LPWININETFINDNEXTA lpwfn) { INT i; TRACE("\n"); for (i = 0; i < lpwfn->size; i++) { if (NULL != lpwfn->lpafp[i].lpszName) HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName); } HeapFree(GetProcessHeap(), 0, lpwfn->lpafp); HeapFree(GetProcessHeap(), 0, lpwfn); return TRUE; } /*********************************************************************** * FTP_ReceiveFileList (internal) * * Read file list from server * * RETURNS * Handle to file list on success * NULL on failure * */ HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONA lpwfs, INT nSocket, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwContext) { DWORD dwSize = 0; LPFILEPROPERTIESA lpafp = NULL; LPWININETFINDNEXTA lpwfn = NULL; TRACE("\n"); if (FTP_ParseDirectory(lpwfs, nSocket, &lpafp, &dwSize)) { FTP_ConvertFileProp(lpafp, lpFindFileData); lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFINDNEXTA)); if (NULL != lpwfn) { lpwfn->hdr.htype = WH_HFINDNEXT; lpwfn->hdr.lpwhparent = (LPWININETHANDLEHEADER)lpwfs; lpwfn->hdr.dwContext = dwContext; lpwfn->index = 1; /* Next index is 1 since we return index 0 */ lpwfn->size = dwSize; lpwfn->lpafp = lpafp; } } TRACE("Matched %ld files\n", dwSize); return (HINTERNET)lpwfn; } /*********************************************************************** * FTP_ConvertFileProp (internal) * * Converts FILEPROPERTIESA struct to WIN32_FIND_DATAA * * RETURNS * TRUE on success * FALSE on failure * */ BOOL FTP_ConvertFileProp(LPFILEPROPERTIESA lpafp, LPWIN32_FIND_DATAA lpFindFileData) { BOOL bSuccess = FALSE; ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA)); if (lpafp) { DWORD access = mktime(&lpafp->tmLastModified); /* Not all fields are filled in */ lpFindFileData->ftLastAccessTime.dwHighDateTime = HIWORD(access); lpFindFileData->ftLastAccessTime.dwLowDateTime = LOWORD(access); lpFindFileData->nFileSizeHigh = HIWORD(lpafp->nSize); lpFindFileData->nFileSizeLow = LOWORD(lpafp->nSize); if (lpafp->bIsDirectory) lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY; if (lpafp->lpszName) strncpy(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH); bSuccess = TRUE; } return bSuccess; } /*********************************************************************** * FTP_ParseDirectory (internal) * * Parse string of directory information * * RETURNS * TRUE on success * FALSE on failure * * FIXME: - This function needs serious clea-up * - We should consider both UNIX and NT list formats */ #define MAX_MONTH_LEN 10 #define MIN_LEN_DIR_ENTRY 15 BOOL FTP_ParseDirectory(LPWININETFTPSESSIONA lpwfs, INT nSocket, LPFILEPROPERTIESA *lpafp, LPDWORD dwfp) { /* *