985 lines
25 KiB
C++
985 lines
25 KiB
C++
/* -----------------------------------------------------------------------------
|
|
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);
|
|
}
|
|
*/
|