340 lines
7.6 KiB
C++
340 lines
7.6 KiB
C++
/* client.cpp
|
|
|
|
libftp++ C++ classes for ftplib C ftp library
|
|
|
|
compile:
|
|
clang++ -std=c++11 -o client client.cpp -lftp++
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <sys/stat.h> // stat
|
|
#include <ftplib.h> // libftp
|
|
#include <ftp++.hpp> // libftp++
|
|
|
|
|
|
/* C++ classes:
|
|
|
|
Connection main ftp connection (user class)
|
|
|
|
ConnectionBase base class for Connection (internal)
|
|
|
|
DataConnection connection type, (internal)
|
|
read bytes from server,
|
|
write bytes to server
|
|
|
|
ConnectionException custom exception (thrown by Connection)
|
|
|
|
*/
|
|
|
|
|
|
/** ftp::Connection class API
|
|
|
|
data members:
|
|
A Connection instance contains only a pointer as a data member
|
|
|
|
{
|
|
protected:
|
|
netbuf * conn; // pointer to ftplib netbuf struct
|
|
}
|
|
|
|
member functions:
|
|
|
|
connect:
|
|
|
|
Connection(const char * host); // constructor
|
|
|
|
void setConnectionMode(Mode mode); // passive or port
|
|
|
|
void login(const char * user, const char * password); // Log in
|
|
|
|
info:
|
|
|
|
const char * getLastResponse(); // last server response
|
|
|
|
std::string getSystemType(); // server type
|
|
|
|
std::string getDirectory(); // remote pwd
|
|
|
|
void getList(const char * filename, const char * path); // short dir list
|
|
|
|
unsigned size(const char * path, TransferMode mode);
|
|
|
|
file transfer:
|
|
|
|
void get(const char * local, const char * remote, TransferMode mode);
|
|
|
|
void put(const char * local, const char * remote, TransferMode mode);
|
|
|
|
**/
|
|
|
|
// libc functions
|
|
int stat(const char *pathname, struct stat *buf); // local file info
|
|
char *strerror(int errnum); // explanation of error
|
|
char *basename(char *path); // filename at end of path
|
|
|
|
|
|
// STL classes and functions used
|
|
namespace stl
|
|
{
|
|
using std::cout; // stdout
|
|
using std::cerr; // stderr
|
|
using std::string; // string
|
|
using std::ifstream; // files on disk
|
|
using std::remove; // delete file on disk
|
|
};
|
|
|
|
using namespace stl;
|
|
|
|
|
|
// connection modes
|
|
using Mode = ftp::Connection::Mode;
|
|
Mode PASV = Mode::PASSIVE;
|
|
Mode PORT = Mode::PORT;
|
|
|
|
//file transfer modes
|
|
using TransferMode = ftp::Connection::TransferMode;
|
|
TransferMode BINARY = TransferMode::BINARY;
|
|
TransferMode TEXT = TransferMode::TEXT;
|
|
|
|
|
|
/* ********************** */
|
|
// ftp session parameters
|
|
struct session
|
|
{
|
|
const string server; // server name
|
|
const string port; // usually 21
|
|
const string user; // username
|
|
const string pass; // password
|
|
Mode mode; // PASV, PORT
|
|
TransferMode txmode; // BINARY, TEXT
|
|
string dir; // current or default dir
|
|
};
|
|
|
|
/* ********************** */
|
|
// local helper functions
|
|
|
|
ftp::Connection connect_ftp( const session& sess);
|
|
size_t get_ftp( ftp::Connection& conn, string const& path);
|
|
string readFile( const string& filename);
|
|
string login_ftp(ftp::Connection& conn, const session& sess);
|
|
string dir_listing( ftp::Connection& conn, const string& path);
|
|
|
|
|
|
/* ******************************** */
|
|
// Read a file into one long string
|
|
|
|
string readFile( const string& filename)
|
|
{
|
|
struct stat stat_buf;
|
|
string contents;
|
|
|
|
errno = 0;
|
|
if (stat(filename.c_str() , &stat_buf) != -1) // file stats
|
|
{
|
|
size_t len = stat_buf.st_size; // size of file
|
|
|
|
string bytes(len+1, '\0'); // string big enough
|
|
|
|
ifstream ifs(filename); // open for input
|
|
|
|
ifs.read(&bytes[0], len); // read all bytes as chars into string
|
|
|
|
if (! ifs.fail() ) contents.swap(bytes); // swap into contents
|
|
|
|
ifs.close();
|
|
}
|
|
else
|
|
{
|
|
cerr << "stat error: " << strerror(errno);
|
|
}
|
|
|
|
return contents;
|
|
}
|
|
|
|
/* *************** */
|
|
// start a session
|
|
|
|
ftp::Connection connect_ftp( const session& sess)
|
|
try
|
|
{
|
|
string constr = sess.server + ":" + sess.port;
|
|
|
|
cerr << "connecting to " << constr << " ...\n";
|
|
|
|
ftp::Connection conn{ constr.c_str() };
|
|
|
|
cerr << "connected to " << constr << "\n";
|
|
conn.setConnectionMode(sess.mode);
|
|
|
|
return conn;
|
|
}
|
|
catch (ftp::ConnectException e)
|
|
{
|
|
cerr << "FTP error: could not connect to server" << "\n";
|
|
}
|
|
|
|
/* ***** */
|
|
// login
|
|
|
|
string login_ftp(ftp::Connection& conn, const session& sess)
|
|
{
|
|
conn.login(sess.user.c_str() , sess.pass.c_str() );
|
|
|
|
return conn.getLastResponse();
|
|
}
|
|
|
|
/* ***************************** */
|
|
// get remote directory listing
|
|
|
|
// ftplib writes to file for dir contents
|
|
// convert file contents to string
|
|
|
|
string dir_listing( ftp::Connection& conn, const string& path)
|
|
try
|
|
{
|
|
// file on disk to write to
|
|
const char* dirdata = "/dev/shm/dirdata";
|
|
|
|
conn.getList(dirdata, path.c_str() );
|
|
// conn.getFullList(dirdata, path.c_str() );
|
|
// conn.getFullList(NULL, path.c_str() ); // to stdout
|
|
|
|
string dir_string = readFile(dirdata);
|
|
|
|
cerr << conn.getLastResponse() << "\n";
|
|
|
|
errno = 0;
|
|
if ( remove(dirdata) != 0 ) // delete file on disk
|
|
{
|
|
cerr << "error: " << strerror(errno) << "\n";
|
|
}
|
|
|
|
return dir_string;
|
|
}
|
|
catch (...) {
|
|
cerr << "error: getting dir contents: \n"
|
|
<< strerror(errno) << "\n";
|
|
}
|
|
|
|
/* ************************* */
|
|
// retrieve file from server
|
|
|
|
size_t get_ftp( ftp::Connection& conn, const string& r_path)
|
|
{
|
|
size_t received = 0;
|
|
|
|
const char* path = r_path.c_str();
|
|
|
|
unsigned remotefile_size = conn.size(path , BINARY);
|
|
|
|
const char* localfile = basename(path);
|
|
|
|
conn.get(localfile, path, BINARY); // get file
|
|
|
|
cerr << conn.getLastResponse() << "\n";
|
|
|
|
// get local file size
|
|
struct stat stat_buf;
|
|
|
|
errno = 0;
|
|
if (stat(localfile, &stat_buf) != -1)
|
|
received = stat_buf.st_size;
|
|
else
|
|
cerr << strerror(errno);
|
|
|
|
return received;
|
|
}
|
|
|
|
/* ******************** */
|
|
// default test session
|
|
const session sonic
|
|
{
|
|
"mirrors.sonic.net",
|
|
"21" ,
|
|
"anonymous",
|
|
"xxxx@nohost.org",
|
|
PASV,
|
|
BINARY,
|
|
"/pub/OpenBSD"
|
|
};
|
|
|
|
/* **** */
|
|
// main
|
|
|
|
int main(int argc, char* argv[], char * env[] )
|
|
{
|
|
const session remote = sonic; // copy session info
|
|
|
|
try
|
|
{
|
|
|
|
// open an ftp connection
|
|
ftp::Connection conn = connect_ftp(remote);
|
|
|
|
// login with username and passwd
|
|
cerr << login_ftp(conn, remote);
|
|
|
|
// what type system
|
|
cout << "System type: " << conn.getSystemType() << "\n";
|
|
cerr << conn.getLastResponse() << "\n";
|
|
|
|
// cd to default session dir
|
|
conn.cd(remote.dir.c_str()); // change to dir on server
|
|
cerr << conn.getLastResponse() << "\n";
|
|
|
|
// get current remote directory
|
|
string pwdstr = conn.getDirectory();
|
|
cout << "PWD: " << pwdstr << "\n";
|
|
cerr << conn.getLastResponse() << "\n";
|
|
|
|
|
|
// get file listing
|
|
string dirlist = dir_listing(conn, pwdstr.c_str() );
|
|
cout << dirlist << "\n";
|
|
|
|
string filename = "ftplist"; // small text file
|
|
|
|
auto pos = dirlist.find(filename); // find filename in dir list
|
|
|
|
auto notfound = string::npos;
|
|
|
|
if (pos != notfound) // found filename
|
|
{
|
|
// get file
|
|
size_t received = get_ftp(conn, filename.c_str() );
|
|
|
|
if (received == 0)
|
|
cerr << "got 0 bytes\n";
|
|
else
|
|
cerr << "got " << filename
|
|
<< " (" << received << " bytes)\n";
|
|
}
|
|
else
|
|
{
|
|
cerr << "file " << filename
|
|
<< "not found on server. \n";
|
|
}
|
|
|
|
}
|
|
catch (ftp::ConnectException e)
|
|
{
|
|
cerr << "FTP error: could not connect to server" << "\n";
|
|
}
|
|
catch (ftp::Exception e)
|
|
{
|
|
cerr << "FTP error: " << e << "\n";
|
|
}
|
|
catch (...)
|
|
{
|
|
cerr << "error: " << strerror(errno) << "\n";
|
|
}
|
|
|
|
// logout, connection closes automatically when conn destructs
|
|
|
|
return 0;
|
|
}
|
|
/* END */
|