/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include "filesystem/ftp_lib.h"
		#include "nstring/nstring.h"
		#include "platform_config.h"
		#include "alloc/ialloc.h"
		#include "log/log.h"

		using namespace GS;

		#define	ConnectionBufferSize	1024

#if		__PLATFORM_WINDOWS__

#define	SETSOCKOPT_OPTVAL_TYPE	(const char *)
#define	net_read(x,y,z)			recv(x,(char*)y,z,0)
#define	net_write(x,y,z)		send(x,(char*)y,z,0)
#define	net_close				closesocket
typedef	int	socklen_t;

#elif	(__PLATFORM_LINUX__ || __PLATFORM_OSX__)

		#include <unistd.h>
		#include <sys/socket.h>
		#include <netinet/in.h>
		#include <netdb.h>
		#include <arpa/inet.h>

#define	SETSOCKOPT_OPTVAL_TYPE	(void *)
#define	net_read	read
#define	net_write	write
#define	net_close	close
#define	SOCKET		int

#endif


//------------------------------------------------------------------------------
#if		(__PLATFORM_WINDOWS__ || __PLATFORM_LINUX__ || __PLATFORM_OSX__)


#define	FTP_PROPAGATE_ERROR__(__C__)	{	State s = __C__; if (s != FtpOk) return s;	}


		#include <stdio.h>
		#include <stdlib.h>
		#include <string.h>
		#include <errno.h>
		#include <ctype.h>
		#include <sys/types.h>


//----------------------------
FtpConnection::FtpConnection()
//----------------------------
{
	handle = -1;
	ready = false;

	buffer = 0;
	buffer_usage = 0;
}

//---------------------------------------------
void				FtpConnection::FreeBuffer()
//---------------------------------------------
{	_safe_delete_array(buffer);	}

//-------------------------------------------------
bool				FtpConnection::AllocateBuffer()
//-------------------------------------------------
{
	FreeBuffer();
	return (buffer = new char[ConnectionBufferSize]) != NULL ? true : false;
}

//-------------------------------------------------------------------
FtpLibrary::State	FtpLibrary::WaitSocket(FtpConnection *connection)
//-------------------------------------------------------------------
{
	fd_set fd, *rfd = NULL, *wfd = NULL;

	FD_ZERO(&fd);
	if	(connection->direction == FtpConnection::Upload)
		wfd = &fd;
	else
		rfd = &fd;

	forever
	{
		FD_SET(connection->handle, &fd);

		// 5 seconds timeout.
		timeval tv;
		tv.tv_sec = 4;
		tv.tv_usec = 100000;

		SOCKET rv = select((int)(connection->handle + 1), rfd, wfd, NULL, &tv);

				if	(rv == -1)
				return FtpError;
		else	if	(!rv)
				return FtpSocketTimeout;
		else
				break;
/*
		if	(!IdleCallback())
			FtpSocketTimeout;
*/
	}
	return FtpOk;
}

//---------------------------------------------------
bool				FtpLibrary::CheckResponse(char c)
//---------------------------------------------------
{
	char match[5];
	int length;
	if	(ReadASCII(response, 256, &master_connection, length) != FtpOk)
		return false;

	if	(response[3] == '-')
	{
		strncpy(match, response, 3);
		match[3] = ' ';
		match[4] = '\0';

		do
		{
			if	(ReadASCII(response, 256, &master_connection, length) != FtpOk)
				return false;
		}	while (strncmp(response, match, 4));
	}
	return (response[0] == c) ? true : false;
}

//-------------------------------------------------------
bool				FtpLibrary::Connect(const char *host)
//-------------------------------------------------------
{
	sockaddr_in sin;
	memset(&sin,0,sizeof(sin));
	sin.sin_family = AF_INET;

	char *lhost = strdup(host), *pnum = strchr(lhost,':');

	servent *pse;
	if	(!pnum)
	{
		if	((pse = getservbyname("ftp", "tcp")) == NULL)
			__ERR__(__LOG__ << "[!] (FTP) Failed to get service port.\n", false)
		sin.sin_port = pse->s_port;
	}
	else
	{
		*pnum++ = 0;
		if	(isdigit(*pnum))
			sin.sin_port = htons((u_short)atoi(pnum));
		else
		{
			pse = getservbyname(pnum, "tcp");
			sin.sin_port = pse->s_port;
		}
	}

#if		__PLATFORM_WINDOWS__
	if	((sin.sin_addr.s_addr = inet_addr(lhost)) == -1)
#else
	if	(!inet_aton(lhost, &sin.sin_addr))
#endif
	{
		hostent *phe;
		if	((phe = gethostbyname(lhost)) == NULL)
			__ERR__(__LOG__ << "[!] (FTP) Failed to resolve host.\n", false)
		memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length);
	}
	free(lhost);

	//-------------------------------------------------------------------------------------------------------
	#define	__ConnectError__(_S_)\
	{	__LOG__ << _S_; net_close(master_connection.handle); master_connection.handle = 0; return false;	}
	//-------------------------------------------------------------------------------------------------------

	master_connection.handle = (int)socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	if	(master_connection.handle == -1)
		__ConnectError__("[!] (FTP) Failed to create socket.\n")

	int on = 1;
	if	(setsockopt(master_connection.handle, SOL_SOCKET, SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1)
		__ConnectError__("[!] (FTP) Set socket option failed.\n")
	if	(connect(master_connection.handle, (struct sockaddr *)&sin, sizeof(sin)) == -1)
		__ConnectError__("[!] (FTP) Socket failed to connect.\n")
	if	(!CheckResponse('2'))
		__ConnectError__("")

	return true;
}

//-----------------------------------------------------------------
bool				FtpLibrary::CheckPASVResponse(unsigned char *v)
//-----------------------------------------------------------------
{
	sockaddr sa;
	socklen_t l = sizeof(sa);

	if	(getpeername(master_connection.handle, &sa, &l) == -1)
	{
		net_close(master_connection.handle);
		return false;
	}
	for	(int i = 2; i < 6; ++i)
		v[i] = sa.sa_data[i];

	return true;
}

//---------------------------------------------------------------------------
bool				FtpLibrary::FtpSendCmd(const char *command, char expresp)
//---------------------------------------------------------------------------
{
	if	(!master_connection.handle)
		return 0;

	char ftp_command[256];
	_snprintf(ftp_command, 255, "%s\r\n", command);
	if	(net_write(master_connection.handle, ftp_command, (int)strlen(ftp_command)) <= 0)
		return false;

	SendCommandCallback(ftp_command);
	return CheckResponse(expresp);
}

//---------------------------------------------------------------------------
bool				FtpLibrary::Login(const char *user, const char *password)
//---------------------------------------------------------------------------
{
	char ftp_command[64];

	// Send user.
	_snprintf(ftp_command, 63, "USER %s", user);
	if	(!FtpSendCmd(ftp_command, '3'))
	{
		if	(*GetLastResponse() == '2')
			return true;
		return false;
	}

	// Send password.
	_snprintf(ftp_command, 63, "PASS %s", password);
	return FtpSendCmd(ftp_command, '2');
}

//------------------------------------------------------------------------------------------------------------------------------
FtpConnection		*FtpLibrary::CreatePORTConnection(TransferMode mode, FtpConnection::Direction dir, char *connection_command)
//------------------------------------------------------------------------------------------------------------------------------
{
	union
	{
		sockaddr	sa;
		sockaddr_in	in;
	} sin;

	// Create the new connection.
	FtpConnection *connection = new FtpConnection;
	if	(!connection)
		__ERR__(__LOG__ << "[!] (FTP) Failed to allocate new connection object.\n", NULL)

	// Get socket from the master connection.
	socklen_t l = sizeof(sin);
	if	(getsockname(master_connection.handle, &sin.sa, &l) < 0)
		return connection;

	// Create socket.
	connection->handle = (int)socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	if	(connection->handle == -1)
		return connection;

	// Set socket options.
	int on = 1;
	if	(setsockopt(connection->handle, SOL_SOCKET, SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1)
		return connection;

	linger lng = { 0, 0 };
	if	(setsockopt(connection->handle, SOL_SOCKET, SO_LINGER, SETSOCKOPT_OPTVAL_TYPE &lng, sizeof(lng)) == -1)
		return connection;

	// Bind socket.
	sin.in.sin_port = 0;
	if	(
			(bind(connection->handle, &sin.sa, sizeof(sin)) == -1) ||
			(listen(connection->handle, 1) < 0) ||
			(getsockname(connection->handle, &sin.sa, &l) < 0)
		)
		return connection;

	// Open PORT connection.
	char ftp_command[256];
	_snprintf(ftp_command, 255, "PORT %hhu,%hhu,%hhu,%hhu,%hhu,%hhu",
								(unsigned char)sin.sa.sa_data[2],
								(unsigned char)sin.sa.sa_data[3],
								(unsigned char)sin.sa.sa_data[4],
								(unsigned char)sin.sa.sa_data[5],
								(unsigned char)sin.sa.sa_data[0],
								(unsigned char)sin.sa.sa_data[1]	);

	if	(!FtpSendCmd(ftp_command, '2'))
		return connection;

	// Handle resuming.
	if	(offset)
	{
		_snprintf(ftp_command, 255, "REST %lld", offset);
		if	(!FtpSendCmd(ftp_command, '3'))
			return connection;		// TODO allow for a full restart here?
	}

	// Allocate buffer for binary transaction.
	if	((mode == TransferASCII) && !connection->AllocateBuffer())
		return connection;

	// Finally send the connection command.
	if	(!FtpSendCmd(connection_command, '1'))
		return connection;

	// Connection is ready.
	connection->direction = dir;
	connection->ready = true;
	return connection;
}

//------------------------------------------------------------------------------------------------------------------------------
FtpConnection		*FtpLibrary::CreatePASVConnection(TransferMode mode, FtpConnection::Direction dir, char *connection_command)
//------------------------------------------------------------------------------------------------------------------------------
{
	// Set PASV mode.
	if	(!FtpSendCmd("PASV", '2'))
		return NULL;

	// Check server answer.
	char *cp = strchr(response,'(');
	if	(!cp)
		return NULL;

	unsigned char v[6];
	sscanf(++cp, "%hhu,%hhu,%hhu,%hhu,%hhu,%hhu", &v[2], &v[3], &v[4], &v[5], &v[0], &v[1]);
	if	(correctpasv && !CheckPASVResponse(v))
		__ERR__(__LOG__ << "[!] (FTP) Incorrect PASV response.\n", NULL)

	struct sockaddr sa;
	sa.sa_family = AF_INET;
	sa.sa_data[2] = v[2];
	sa.sa_data[3] = v[3];
	sa.sa_data[4] = v[4];
	sa.sa_data[5] = v[5];
	sa.sa_data[0] = v[0];
	sa.sa_data[1] = v[1];

	// Handle resume.
	char ftp_command[256];

	if	(offset)
	{
		_snprintf(ftp_command, 255, "REST %lld", offset);
		if	(!FtpSendCmd(ftp_command, '3'))
			return NULL;
	}

	// Create the new connection.
	FtpConnection	*connection = new FtpConnection;
	if	(!connection)
		__ERR__(__LOG__ << "[!] (FTP) Failed to allocate new connection object.\n", NULL)

	// Create socket.
	int on = 1;
	linger lng = { 0, 0 };
	connection->handle = (int)socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
	if	(
			(connection->handle == -1) ||
			(setsockopt(connection->handle, SOL_SOCKET, SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1) ||
			(setsockopt(connection->handle, SOL_SOCKET, SO_LINGER, SETSOCKOPT_OPTVAL_TYPE &lng, sizeof(lng)) == -1)
		)
		return connection;

	// Setup connection.
	_snprintf(ftp_command, 255, "%s\r\n", connection_command);
	if	(net_write(master_connection.handle, ftp_command, (int)strlen(ftp_command)) <= 0)
		return connection;

	// Connect socket.
	if	((connect(connection->handle, &sa, sizeof(sa)) == -1) || !CheckResponse('1'))
		return connection;

	// Allocate buffer for binary transaction.
	if	((mode == TransferASCII) && !connection->AllocateBuffer())
		return connection;

	// Connection is ready.
	connection->direction = dir;
	connection->ready = true;
	return connection;
}

//----------------------------------------------------------------------------
bool				FtpLibrary::FtpAcceptConnection(FtpConnection *connection)
//----------------------------------------------------------------------------
{
	// Reset all connections.
	fd_set mask;

	FD_ZERO(&mask);
	FD_SET(master_connection.handle, &mask);
	FD_SET(connection->handle, &mask);

	// Setup timeout.
	timeval tv;
	tv.tv_usec = 0;
	tv.tv_sec = 30;

	// Select handle.
	int i = (int)master_connection.handle;
	if	(i < connection->handle)
		i = connection->handle;
	i = select((int)(i + 1), &mask, NULL, NULL, &tv);

	switch (i)
	{
		case	-1:	// Error.
			strncpy(response, strerror(errno), sizeof(response));
			break;

		case	0:	// Time out.
			strcpy(response, "Time out waiting for connection.");
			break;

		default:
			if	(FD_ISSET(connection->handle, &mask))
			{
				sockaddr addr;
				socklen_t l = sizeof(addr);
				int handle = (int)accept(connection->handle, &addr, &l);
				i = errno;
				net_close(connection->handle);

				if	(handle > 0)
				{
					connection->handle = handle;
					return true;
				}

				strncpy(response, strerror((int)i), sizeof(response));
				connection->handle = 0;
				return false;
			}
			else
				if	(FD_ISSET(connection->handle, &mask))
					CheckResponse('2');

			break;
	}

	net_close(connection->handle);
	connection->handle = 0;
	return false;
}

//------------------------------------------------------------------------------
FtpConnection		*FtpLibrary::CreateConnection(const char *path, AccessType type, TransferMode mode)
{
	if	(!path && ((type == AccessFileWrite) || (type == AccessFileRead) || (type == AccessFileReadAppend) || (type == AccessFileWriteAppend)))
		__ERR__(__LOG__ << "[!] (FTP) Missing path argument.\n", NULL)

	// Setup transfer mode.
	char ftp_command[256];
	_snprintf(ftp_command, 255, "TYPE %c", mode);
	if	(!FtpSendCmd(ftp_command, '2'))
		__ERR__(__LOG__ << "[!] (FTP) Failed to set tranfer mode.\n", NULL)

	// Select sub command.
	const char *sub_command;
	FtpConnection::Direction direction = FtpConnection::Download;

	switch (type)
	{
		case AccessDir:
			sub_command = "NLST";
			break;

		case AccessDirVerbose:
			sub_command = "LIST -aL";
			break;

		case AccessFileReadAppend:
		case AccessFileRead:
			sub_command = "RETR";
			break;

		case AccessFileWriteAppend:
		case AccessFileWrite:
			sub_command = "STOR";
			direction = FtpConnection::Upload;
			break;

		default:
			__ERR__(__LOG__ << "[!] (FTP) Invalid access type.\n", NULL)
	}

	// Append path.
	if	(path)
		_snprintf(ftp_command, 255, "%s %s", sub_command, path);

	// Open connection.
	FtpConnection *connection = NULL;

	switch (connection_mode)
	{
		case	ConnectionPASV:
			connection = CreatePASVConnection(mode, direction, ftp_command);
			break;

		case	ConnectionPORT:
			connection = CreatePORTConnection(mode, direction, ftp_command);
			if	(!connection || !connection->ready)
				break;

			if	(!FtpAcceptConnection(connection))
			{
				CloseConnection(connection);
				return NULL;
			}
			break;
	}
	return connection;
}
bool				FtpLibrary::CloseConnection(FtpConnection *connection)
{
	// Sanity check.
	if	(connection == &master_connection)
		return false;

	// Purge writing cache.
	int length;
	if	(connection->direction == FtpConnection::Upload)
		if	(connection->buffer)
			WriteASCII(NULL, 0, connection, length);

	shutdown(connection->handle, 2);
	net_close(connection->handle);
	_safe_delete(connection);

	return CheckResponse('2');
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
FtpLibrary::State	FtpLibrary::ReadASCII(char *buf, int max, FtpConnection *connection, int &total_read_count)
{
/// TODO
	// Early exit case.
	if	(max == 0)
		return FtpOk;
	if	((connection != &master_connection) && (connection->direction != FtpConnection::Download))
		return FtpError;

	// Read.
	total_read_count = 0;
	char *p_buffer = buf;

	forever
	{
		// Check available data on cache.
		if	(connection->buffer_usage > 0)
		{
			// Determine read size.
			char *eol = (char *)memchr(connection->buffer, '\n', connection->buffer_usage);
			int read_size = (int)(eol ? eol - connection->buffer + 1 : connection->buffer_usage);

			if	(read_size > (max - 1))
				read_size = max - 1;

			// Perform reading.
			memcpy(p_buffer, connection->buffer, read_size);

			max -= read_size;
			total_read_count += read_size;
			p_buffer += read_size;

			// Update cache.
			if	(read_size < connection->buffer_usage)
				memcpy(connection->buffer, &connection->buffer[read_size], connection->buffer_usage - read_size);
			connection->buffer_usage -= read_size;

			// Catch end of buffer.
			if	((max == 1) || eol)
			{
				p_buffer[0] = 0;
				break;
			}
		}

		// Wait socket.
		FTP_PROPAGATE_ERROR__(WaitSocket(connection))

		// Fill cache.
		int buffer_left = (ConnectionBufferSize - connection->buffer_usage) - 1,
			read_count = net_read(connection->handle, &connection->buffer[connection->buffer_usage], buffer_left);

		connection->buffer_usage += read_count;

				if	(read_count == -1)
					__ERR__(__LOG__ << "[!] (FTP) Read error.\n", FtpError)
		else	if	(!read_count)
					break;		// EOF
	}
	return FtpOk;
}
FtpLibrary::State	FtpLibrary::WriteASCII(char *output, int length, FtpConnection *connection, int &x)
{
/// TODO
	if	(connection->direction != FtpConnection::Upload)
		return FtpError;

	FTP_PROPAGATE_ERROR__(WaitSocket(connection))
	if	((x = net_write(connection->handle, output, length)) != length)
		return FtpError;

	return FtpOk;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
FtpLibrary::State	FtpLibrary::ReadBinary(void *buf, int max, FtpConnection *connection, int &length)
{
	if (connection->direction != FtpConnection::Download)
		return FtpError;

	FTP_PROPAGATE_ERROR__(WaitSocket(connection))
	length = net_read(connection->handle, buf, max);
	if	(length == -1)
		return FtpError;

	DataReadCallback(connection);
	return FtpOk;
}
FtpLibrary::State	FtpLibrary::WriteBinary(void *buf, int len, FtpConnection *connection)
{
	if	(connection->direction != FtpConnection::Upload)
		return FtpError;

	FTP_PROPAGATE_ERROR__(WaitSocket(connection))
	if	(net_write(connection->handle, buf, len) != len)
		return FtpError;

	DataWriteCallback(connection);
	return FtpOk;
}
//------------------------------------------------------------------------------

//-----------------------------------------------------------------------------------------------------------------------
FtpLibrary::State	FtpLibrary::DataTransfer(const char *localfile, const char *path, AccessType type, TransferMode mode)
//-----------------------------------------------------------------------------------------------------------------------
{
	// Open local file or I/O stream.
	FILE *file;

	if	(localfile)
	{
		const char *access;

		switch (type)
		{
			default:
			case AccessDir:
			case AccessDirVerbose:
			case AccessFileRead:
				access = (mode == TransferBinary) ? "wb" : "w";
				break;

			case AccessFileReadAppend:
				access = (mode == TransferBinary) ? "ab" : "a";
				break;

			case AccessFileWriteAppend:
			case AccessFileWrite:
				access = (mode == TransferBinary) ? "rb" : "r";
				break;
		}

		file = fopen(localfile, access);
		if	(!file)
			__ERR__(__LOG_E__ << "Failed to open FTP output file.\n", FtpError)

		if	(type == AccessFileWriteAppend)
			if	(fseek(file, offset, SEEK_SET))
			{
				fclose(file);
				__ERR__(__LOG_E__ << "Failed to seek in FTP file for transfer.\n", FtpError)
			}
	}
	else
		file = (type == AccessFileWrite || type == AccessFileWriteAppend) ? stdin : stdout;

	// Create a new connection.
	FtpConnection *connection = CreateConnection(path, type, mode);
	if	(!connection)
	{
		if	(localfile)
			fclose(file);
		return FtpError;
	}

	// Perform transfer.
	State retv = FtpOk;
	GS::Array <char> temp_buffer(ConnectionBufferSize);
	int length;

	if	((type == AccessFileWrite) || (type == AccessFileWriteAppend))
	{
		while ((length = (int)fread(temp_buffer, 1, ConnectionBufferSize, file)) > 0)
			if	(WriteBinary(temp_buffer, length, connection) != FtpOk)
			{
				retv = FtpError;		// FTP write failed.
				break;
			}
	}
	else
	{
		while ((retv = ReadBinary(temp_buffer, ConnectionBufferSize, connection, length)) == FtpOk)
			if	(!length || (fwrite(temp_buffer, 1, length, file) <= 0))
				break;	// Done.
	}

	// Flush file.
	fflush(file);
	if	(localfile)
		fclose(file);

	CloseConnection(connection);
	return retv;
}

//------------------------------------------------------------------------------
FtpLibrary::State	FtpLibrary::Download(const char *file, const char *path, TransferMode mode, int _offset)
{
	offset = _offset;
	if	(!offset)
			return DataTransfer(file, path, AccessFileRead, mode);
	else	return DataTransfer(file, path, AccessFileReadAppend, mode);
}
FtpLibrary::State	FtpLibrary::Upload(const char *file, const char *path, TransferMode mode, int _offset)
{
	offset = _offset;
	if	(!offset)
			return DataTransfer(file, path, AccessFileWrite, mode);
	else	return DataTransfer(file, path, AccessFileWriteAppend, mode);
}
//------------------------------------------------------------------------------

//------------------------------------
bool				FtpLibrary::Quit()
//------------------------------------
{
	if	(!master_connection.handle)
		return true;

	bool r = FtpSendCmd("QUIT", '2');

	net_close(master_connection.handle);
	master_connection.handle = 0;
	return r;
}

//------------------------------------------------------------------------------
bool				FtpLibrary::DeleteFile(const char *path)
{	return FtpSendCmd(String::Format("DELE %s", path).c_str(), '2');	}
bool				FtpLibrary::ChangeDirectory(const char *path)
{	return FtpSendCmd(String::Format("CWD %s", path).c_str(), '2');	}
bool				FtpLibrary::UpDirectory()
{	return FtpSendCmd("CDUP", '2');		}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
int					FtpLibrary::GetFileSize(const char *path, TransferMode mode)
//------------------------------------------------------------------------------
{
	int rs, sz = -1;
	if	(	FtpSendCmd(String::Format("TYPE %c", mode).c_str(), '2') &&
			FtpSendCmd(String::Format("SIZE %s", path).c_str(), '2') &&
			(sscanf(response, "%d %d", &rs, &sz) == 2)	)
		return sz;
	return -1;
}

//----------------------------------------------------------------------------
bool				FtpLibrary::Nlst(const char *outputfile, const char *path)
//----------------------------------------------------------------------------
{
	offset = 0;
	return DataTransfer(outputfile, path, FtpLibrary::AccessDir, FtpLibrary::TransferASCII) == FtpOk ? true : false;
}

//------------------------------------------------------------------------------
FtpLibrary::FtpLibrary()
{
#if		__PLATFORM_WINDOWS__
	WSADATA		wsa;
	if	(WSAStartup(MAKEWORD(1, 1), &wsa))
		__LOG__ << "[!] (FTP) WINSOCK startup error.\n";
#endif

	connection_mode = ConnectionPORT;
	master_connection.AllocateBuffer();

	//
	offset = 0;
	correctpasv = false;
}
FtpLibrary::~FtpLibrary()
{	Quit();	}
//------------------------------------------------------------------------------


#endif



























/*
int FtpLibrary::Site(const char *cmd)
{
	char buf[256];

	if ((strlen(cmd) + 7) > sizeof(buf)) return 0;
	sprintf(buf,"SITE %s",cmd);
	if (!FtpSendCmd(buf,'2',mp_ftphandle)) return 0;
	return 1;
}

int FtpLibrary::Raw(const char *cmd)
{
	char buf[256];
	strncpy(buf, cmd, 256);
	if (!FtpSendCmd(buf,'2',mp_ftphandle)) return 0;
	return 1;
}

int FtpLibrary::SysType(char *buf, int max)
{
	int l = max;
	char *b = buf;
	char *s;
	if (!FtpSendCmd("SYST",'2',mp_ftphandle)) return 0;
	s = &mp_ftphandle->response[4];
	while ((--l) && (*s != ' ')) *b++ = *s++;
	*b++ = '\0';
	return 1;
}

int FtpLibrary::Mkdir(const char *path)
{
	char buf[256];

	if ((strlen(path) + 6) > sizeof(buf)) return 0;
	sprintf(buf,"MKD %s",path);
	if (!FtpSendCmd(buf,'2', mp_ftphandle)) return 0;
	return 1;
}


int FtpLibrary::Rmdir(const char *path)
{
	char buf[256];

	if ((strlen(path) + 6) > sizeof(buf)) return 0;
	sprintf(buf,"RMD %s",path);
	if (!FtpSendCmd(buf,'2',mp_ftphandle)) return 0;
	return 1;
}

int FtpLibrary::Pwd(char *path, int max)
{
	int l = max;
	char *b = path;
	char *s;

	if (!FtpSendCmd("PWD",'2',mp_ftphandle)) return 0;
	s = strchr(mp_ftphandle->response, '"');
	if (s == NULL) return 0;
	s++;
	while ((--l) && (*s) && (*s != '"')) *b++ = *s++;
	*b++ = '\0';
	return 1;
}

int FtpLibrary::Dir(const char *outputfile, const char *path)
{
	mp_ftphandle->offset = 0;
	return FtpXfer(outputfile, path, mp_ftphandle, FtpLibrary::dirverbose, FtpLibrary::ascii);
}

int FtpLibrary::ModDate(const char *path, char *dt, int max)
{
	char buf[256];
	int rv = 1;

	if ((strlen(path) + 7) > sizeof(buf)) return 0;
	sprintf(buf,"MDTM %s",path);
	if (!FtpSendCmd(buf,'2',mp_ftphandle)) rv = 0;
	else strncpy(dt, &mp_ftphandle->response[4], max);
	return rv;
}

int FtpLibrary::Rename(const char *src, const char *dst)
{
	char cmd[256];

	if (((strlen(src) + 7) > sizeof(cmd)) || ((strlen(dst) + 7) > sizeof(cmd))) return 0;
	sprintf(cmd,"RNFR %s",src);
	if (!FtpSendCmd(cmd,'3',mp_ftphandle)) return 0;
	sprintf(cmd,"RNTO %s",dst);
	if (!FtpSendCmd(cmd,'2',mp_ftphandle)) return 0;

	return 1;
}




void FtpLibrary::SetConnmode(connmode mode)
{
	mp_ftphandle->cmode = mode;
}

ftphandle* FtpLibrary::RawOpen(const char *path, accesstype type, transfermode mode)
{
	int ret;
	ftphandle* datahandle;
	ret = CreateConnection(path, type, mode, mp_ftphandle, &datahandle);
	if (ret) return datahandle;
	else return NULL;
}

int FtpLibrary::RawClose(ftphandle* handle)
{
	return FtpClose(handle);
}

int FtpLibrary::RawWrite(void* buf, int len, ftphandle* handle)
{
	return FtpWrite(buf, len, handle);
}

int FtpLibrary::RawRead(void* buf, int max, ftphandle* handle)
{
	return FtpRead(buf, max, handle);
}
*/
