#include "stdafx.h"
#include "ftp_client.h"
static boost::regex expression("^([0-9]+)(\\-| |$)(.*)$");
ftp_client::ftp_client() :cmd_socket_(io_service_),
work_(io_service_),data_socket_(io_service_),is_logined(false), is_connected(false)
{
thread_ptr ptr(new boost::thread(boost::bind(&boost::asio::io_service::run, boost::ref(io_service_))));
}
ftp_client::~ftp_client()
{
if (data_socket_.is_open()){
data_socket_.shutdown(boost::asio::socket_base::shutdown_both);
data_socket_.close();
}
if (cmd_socket_.is_open()){
cmd_socket_.shutdown(boost::asio::socket_base::shutdown_both);
cmd_socket_.close();
}
}
bool ftp_client::connect(std::string host, std::string port, std::string user, std::string passwd)
{
if (is_connected){
return true;
}
if (host.empty() || port.empty() || user.empty() || passwd.empty()){
return false;
}
user_ = user; passwd_ = passwd; host_ = host; port_ = port;
ip::tcp::resolver resolver(io_service_);
ip::tcp::resolver::query query(host_, port_);
ip::tcp::resolver::iterator it = resolver.resolve(query);
cmd_socket_.async_connect( *it,
boost::bind(&ftp_client::handle_connect,
this, boost::asio::placeholders::error));
return true;
}
void ftp_client::upload_file(std::string filename)
{//async upload file
try{
boost::mutex::scoped_lock lock(iomutex);
bool write_in_progress = !file_list.empty();
file_list.push_back(std::make_pair(true, filename));
if (!write_in_progress && is_logined){
_write_message("PASV \r\n");
}
}catch(...){
save_info((boost::format("%1%<%2%>: Remote is leave, session is close") %__FILE__ %__LINE__ ).str());
}
}
string ftp_client::download_file(std::string filename)
{//sync download file
try{
boost::mutex::scoped_lock lock(cond_mutex);
{
boost::mutex::scoped_lock lock(iomutex);
bool write_in_progress = !file_list.empty();
file_list.push_back(std::make_pair(false, filename));
if (!write_in_progress && is_logined){
_write_message("PASV \r\n");
}
}
condition.wait(lock);
}catch(...){
save_info(boost::format ("Remote is leave, session is close").str());
}
boost::filesystem::path path = boost::filesystem::current_path();
path += "\\download";
boost::filesystem::create_directory(path);
path += "\\";
path += boost::filesystem::path(filename).filename();
return path.string();
}
void ftp_client::start()
{
boost::asio::async_read_until(cmd_socket_,
receive_msg, '\n',
boost::bind(&ftp_client::read_complete, this,
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void ftp_client::handle_connect(const boost::system::error_code& error)
{
if (!error){
is_connected = true;
start();
}else{
is_logined = is_connected = false;
save_info((boost::format("%1%<%2%>: %3%") %__FILE__ %__LINE__ %error.message()).str());
}
}
void ftp_client::service(boost::asio::io_service &io_service)
{// not using
while (1){// while io.run isn't have work, it will exit, so rerun it.
boost::this_thread::interruption_point();
boost::this_thread::sleep(boost::posix_time::milliseconds(100));
io_service.run();
io_service.reset();
}
}
void ftp_client::paser_response(size_t resp_code, std::string resp_arg)
{
boost::mutex::scoped_lock lock(iomutex);
std::string cmd;
std::string filename;
switch (resp_code){
case 220: //welcome
cmd= "User " + user_ +"\r\n";
_write_message(cmd);
break;
case 331:// enter password
cmd = "Pass " + passwd_ +"\r\n";
_write_message(cmd);
break;
case 230://loging success
is_logined = true;
if (!file_list.empty()){
_write_message("PASV \r\n");
}
break;
case 150://ftp server is reday for receive/trans data, client can send/receive data
{
if (!file_list.empty()){
if(true == file_list.front().first){//uploadfile
if ((trans_data(file_list.front().second))){
std::cout << "file "<< file_list.front().second << "is upload to server\n";
file_list.pop_front();
}
}else{// download file;
if ((receive_data(file_list.front().second))){
std::cout << "file "<< file_list.front().second << "is download from server\n";
}
file_list.pop_front();
condition.notify_all();
}
}
break;
}
case 226: // Transfer complete.
if (!file_list.empty()){//start trans file
_write_message("PASV\n");
}
break;
case 227://Entering passive Mode
parser_pasv(resp_arg);
if (file_list.empty()){
break;
}
filename = file_list.front().second;
if (file_list.front().first){
filename = filename.substr(filename.find_last_of('\\')+1);
cmd = "STOR " +filename + "\r\n";
}else{
cmd = "RETR " + filename + "\r\n";
}
_write_message(cmd);
break;
case 530://Login or password incorrect!
{
_write_message("QUIT\r\n");
}
case 421:// connect timeout
{
is_logined = false;
is_connected = false;
if (cmd_socket_.is_open()){
cmd_socket_.shutdown(boost::asio::socket_base::shutdown_both);
cmd_socket_.close();
}
condition.notify_all();
connect(host_, port_, user_, passwd_);
break;
}
case 550://File not found
{
if (!file_list.empty()) {
file_list.pop_front();
condition.notify_all();
}
break;
}
case 500:// oops
default:
{
save_info((boost::format("%1%<%2%>: Can not process Resp code:%3%, Resp args: %4%")
%__FILE__ %__LINE__ %resp_code %resp_arg).str());
if (data_socket_.is_open()){
data_socket_.shutdown(boost::asio::socket_base::shutdown_both);
data_socket_.close();
}
is_logined = false;
is_connected = false;
if (cmd_socket_.is_open()){
cmd_socket_.shutdown(boost::asio::socket_base::shutdown_both);
cmd_socket_.close();
}
connect(host_, port_, user_, passwd_);
break;
}
}
}
void ftp_client::parser_pasv(std::string pasv)
{
if ((pasv.find('(') == std::string::npos) && (pasv.find(')') == std::string::npos)){
return;
}
pairporthost = parsehostport(pasv);
ip::tcp::resolver resolver(io_service_);
ip::tcp::resolver::query query(porthost.first, porthost.second);
ip::tcp::resolver::iterator it = resolver.resolve(query);
ip::tcp::endpoint endpoint = *it;
if (data_socket_.is_open()){
data_socket_.shutdown(boost::asio::socket_base::shutdown_both);
data_socket_.close();
}
data_socket_.connect(endpoint);
}
pair ftp_client::parsehostport(string s)
{
size_t paramspos = s.find('(');
string params = s.substr(paramspos + 1);
size_t ip1pos = params.find(',');
string ip1 = params.substr(0, ip1pos);
size_t ip2pos = params.find(',', ip1pos + 1);
string ip2 = params.substr(ip1pos + 1, ip2pos - ip1pos - 1);
size_t ip3pos = params.find(',', ip2pos + 1);
string ip3 = params.substr(ip2pos + 1, ip3pos - ip2pos - 1);
size_t ip4pos = params.find(',', ip3pos + 1);
string ip4 = params.substr(ip3pos + 1, ip4pos - ip3pos - 1);
size_t port1pos = params.find(',', ip4pos + 1);
string port1 = params.substr(ip4pos + 1, port1pos - ip4pos - 1);
size_t port2pos = params.find(')', port1pos + 1);
string port2 = params.substr(port1pos + 1, port2pos - port1pos - 1);
pair hostport;
hostport.first = ip1 + "." + ip2 + "." + ip3 + "." + ip4;
int portval = atoi(port1.c_str()) * 256 + atoi(port2.c_str());
hostport.second = boost::lexical_cast(portval);
return hostport;
}
bool ftp_client::trans_data(std::string filename)
{
std::ifstream ifs(filename.c_str(), std::ifstream::binary);
char buffer[1448];
while (1){
ifs.read(buffer, 1448);
data_socket_.send(boost::asio::buffer(buffer,ifs.gcount()));
if (ifs.gcount() < 1448){
break;
}
}
ifs.close();
data_socket_.shutdown(boost::asio::socket_base::shutdown_both);
data_socket_.close();
return true;
}
bool ftp_client::receive_data(std::string filename)
{
boost::filesystem::path path = boost::filesystem::current_path();
path += "\\download";
boost::filesystem::create_directory(path);
path += "\\";
path += boost::filesystem::path(filename).filename();
cout <:%3%") %__FILE__ %__LINE__ %e.what()).str());
}catch (...){
save_info((boost::format("%1%<%2%>:Ftp Receive data failed") %__FILE__ %__LINE__).str());
}
ofs.close();
data_socket_.shutdown(boost::asio::socket_base::shutdown_both);
data_socket_.close();
return true;
}
void ftp_client::read_complete(const boost::system::error_code& error, size_t bytes_transferred)
{
if (!error) {
boost::asio::streambuf::const_buffers_type bufs= receive_msg.data();
std::string line(boost::asio::buffers_begin(bufs), boost::asio::buffers_begin(bufs)+ bytes_transferred);
receive_msg.consume(bytes_transferred);
boost::cmatch what;
if(boost::regex_match(line.c_str(), what, expression)){
paser_response(::atoi(what[1].first), std::string(what[3].first, what[3].second));
}
start();
}else{
is_connected = false;
save_info((boost::format("%1%<%2%>:%3%") %__FILE__ %__LINE__ % error.message() ).str());
}
}
#include
#include
#include
#define SINGLETON
class ftp_client:public boost::noncopyable
{
public:
ftp_client();
~ftp_client();
bool connected(){return is_connected;}
#ifdef SINGLETON
static ftp_client & getInstence()
{
static ftp_client ftp;
return ftp;
}
#endif
bool connect(std::string host, std::string port, std::string user, std::string passwd);
void upload_file(std::string filename);
string download_file(std::string filename);
string ftp_client::get_last_error(){
boost::mutex::scoped_lock lock(iomutex);
return error_msg;
}
private:
void start();
void handle_connect(const boost::system::error_code& error);
void read_complete(const boost::system::error_code& error, size_t bytes_transferred);
void paser_response(size_t resp_code, std::string resp_arg);
size_t write_message(std::string msg)
{
boost::mutex::scoped_lock lock(iomutex);
return _write_message(msg);
}
size_t _write_message(std::string msg)
{
boost::system::error_code ec;
if (!cmd_socket_.is_open()){
save_info((boost::format("%1%<%2%>: Server is not connect") %__FILE__ %__LINE__).str());
return 0;
}
return boost::asio::write(cmd_socket_, boost::asio::buffer(msg),ec);
}
void parser_pasv(std::string pasv);
pair parsehostport(string s);
bool trans_data(std::string filename);
bool receive_data(std::string filename);
//not using it.
void service(boost::asio::io_service &io_service);
void save_info(const string info){
#ifdef _DEBUG
cout << info < > file_list;
boost::mutex iomutex;
enum{
msg_head_len = 1024,
};
std::string user_;
std::string passwd_;
std::string host_;
std::string port_;
string error_msg;
};