diff options
Diffstat (limited to 'ACE/protocols/ace/INet/FTP_ClientRequestHandler.cpp')
-rw-r--r-- | ACE/protocols/ace/INet/FTP_ClientRequestHandler.cpp | 713 |
1 files changed, 713 insertions, 0 deletions
diff --git a/ACE/protocols/ace/INet/FTP_ClientRequestHandler.cpp b/ACE/protocols/ace/INet/FTP_ClientRequestHandler.cpp new file mode 100644 index 00000000000..1f9a8775f55 --- /dev/null +++ b/ACE/protocols/ace/INet/FTP_ClientRequestHandler.cpp @@ -0,0 +1,713 @@ +// $Id$ + +#include "ace/INet/FTP_ClientRequestHandler.h" + +#if !defined (__ACE_INLINE__) +#include "ace/INet/FTP_ClientRequestHandler.inl" +#endif + +#include "ace/INet/INet_Log.h" +#include "ace/INet/String_IOStream.h" +#include "ace/Auto_Ptr.h" +#include "ace/OS_NS_ctype.h" +#include "ace/Connector.h" +#include "ace/Acceptor.h" +#include "ace/SOCK_Acceptor.h" + + + +ACE_BEGIN_VERSIONED_NAMESPACE_DECL + +namespace ACE +{ + namespace FTP + { + + ClientRequestHandler::SessionHolder::SessionHolder () + : session_ () + { + } + + ClientRequestHandler::SessionHolder::~SessionHolder() + { + } + + ClientRequestHandler::SessionFactory::SessionFactory () + { + } + + ClientRequestHandler::SessionFactory::~SessionFactory () + { + } + + ACE::INet::ConnectionHolder* + ClientRequestHandler::SessionFactory::create_connection ( + const ACE::INet::ConnectionKey& key) const + { + INET_TRACE ("FTP::ClientRequestHandler::SessionFactory::create_connection"); + + const INetConnectionKey& ikey = dynamic_cast<const INetConnectionKey&> (key); + + SessionHolder* session_holder = 0; + ACE_NEW_RETURN (session_holder, + SessionHolder (), + 0); + ACE_Auto_Ptr<SessionHolder> session_safe_ref (session_holder); + + (*session_holder)->set_host (ikey.host (), ikey.port ()); + + if ((*session_holder)->connect (true)) + { + return session_safe_ref.release (); + } + + return 0; + } + + ClientRequestHandler::Authentication::Authentication ( + const ACE_CString& realm, + ACE_CString& user, + ACE_CString& pw) + : AuthenticationBase (), + realm_ (realm), + user_ (user), + password_ (pw) + { + } + + ClientRequestHandler::Authentication::~Authentication () {} + + const ACE_CString& ClientRequestHandler::Authentication::scheme () const + { + return URL::protocol (); + } + + const ACE_CString& ClientRequestHandler::Authentication::realm () const + { + return this->realm_; + } + + const ACE_CString& ClientRequestHandler::Authentication::user () const + { + return this->user_; + } + + void ClientRequestHandler::Authentication::user (const ACE_CString& usr) + { + this->user_ = usr; + } + + const ACE_CString& ClientRequestHandler::Authentication::password () const + { + return this->password_; + } + + void ClientRequestHandler::Authentication::password (const ACE_CString& pw) + { + this->password_ = pw; + } + + const ACE_CString ClientRequestHandler::anonymous_user_ = "anonymous"; + const ACE_CString ClientRequestHandler::empty_; + + ClientRequestHandler::ClientRequestHandler (bool passive_mode) + : session_ (0), + use_passive_mode_ (passive_mode), + active_port_ (0), + out_data_stream_ (0), + in_data_stream_ (0), + transfer_active_ (false) + { + this->out_data_stream_.set_interceptor (*this); + this->in_data_stream_.set_interceptor (*this); + } + + ClientRequestHandler::~ClientRequestHandler () + { + this->release_connection (); + } + + bool ClientRequestHandler::is_response_ok () const + { + return this->response_.is_completed_ok () || + (this->response_.is_preliminary_ok () && + !this->in_data_stream_.bad ()); + } + + std::istream& ClientRequestHandler::handle_open_request ( + const ACE::INet::URL_Base& url) + { + const URL& ftp_url = dynamic_cast<const URL&> (url); + return this->handle_get_request (ftp_url); + } + + std::istream& ClientRequestHandler::handle_get_request ( + const URL& ftp_url, + bool binary) + { + if (this->initialize_connection (ftp_url.get_host (), + ftp_url.get_port ())) + { + bool rc = true; + if (this->session ()->is_new_connection () || + (!ftp_url.get_user_info ().empty () && + ftp_url.get_user_info () != this->current_user_)) + { + if (!this->session ()->is_new_connection ()) + rc = this->logout (); + + if (rc) + { + ACE_CString user = ftp_url.get_user_info ().empty () ? + anonymous_user_ : ftp_url.get_user_info (); + ACE_CString password = user; + rc = this->handle_credentials (ftp_url, user, password); + if (rc) rc = this->login (user, password); + } + } + + if (rc) rc = this->finish_transfer (); + + if (rc) + { + stream_type* data_stream = + this->start_download (ftp_url.get_path (), binary); + if (data_stream) + { + // no need to check prev stream since we just called finish_transfer + // to clean everything up + this->in_data_stream_.set_stream (data_stream); + } + else + { + rc = false; + } + } + + if (!rc) + { + this->close_connection (); + + this->handle_request_error(ftp_url); + } + } + else + { + this->handle_connection_error (ftp_url); + } + + return this->response_stream (); + } + + void ClientRequestHandler::on_eof () + { + this->finish_transfer (); + } + + bool ClientRequestHandler::initialize_connection (const ACE_CString& host, + u_short port) + { + static const SessionFactory session_factory; + + ACE::INet::ConnectionHolder* pch = 0; + if (this->connection_cache ().claim_connection (INetConnectionKey (host, port), + pch, + session_factory)) + { + this->session (dynamic_cast<SessionHolder*> (pch)); + return true; + } + else + return false; + } + + bool ClientRequestHandler::handle_credentials (const URL& url, + ACE_CString& user, + ACE_CString& password) + { + Authentication authentication (url.get_host(), + user, + password); + return URL::authenticate (authentication); + } + + void ClientRequestHandler::handle_request_error (const URL& /*url*/) + { + } + + void ClientRequestHandler::handle_connection_error (const URL& /*url*/) + { + } + + void ClientRequestHandler::release_connection () + { + if (this->session_) + { + this->connection_cache ().release_connection ( + INetConnectionKey (this->session ()->get_host (), + this->session ()->get_port ()), + this->session_); + this->session_ = 0; + } + } + + void ClientRequestHandler::close_connection () + { + if (this->session_) + { + this->connection_cache ().close_connection ( + INetConnectionKey (this->session ()->get_host (), + this->session ()->get_port ()), + this->session_); + this->session_ = 0; + } + } + + void ClientRequestHandler::quit_connection () + { + this->logout (); + this->close_connection (); + } + + bool ClientRequestHandler::logout () + { + bool rc = true; + if (this->session ()->is_connected ()) + { + try + { + this->finish_transfer (); + } + catch (...) + { + } + this->process_command (Request::FTP_QUIT); + rc = this->response_.is_completed_ok (); + this->current_user_ = anonymous_user_; + this->session ()->close (); + } + return rc; + } + + bool ClientRequestHandler::login (const ACE_CString& user, + const ACE_CString& password) + { + // read startup message + this->session ()->receive_response (this->response_); + if (this->response_.is_completed_ok ()) + { + // send user name + this->process_command (Request::FTP_USER, user); + if (this->response_.is_intermediate_ok ()) + { + this->process_command (Request::FTP_PASS, password); + } + } + return this->response_.is_completed_ok (); + } + + bool ClientRequestHandler::is_valid_path (const ACE_CString& path) + { + return this->process_command (Request::FTP_STAT, path) == Response::COMPLETED_OK; + } + + bool ClientRequestHandler::is_file (const ACE_CString& path) + { + return (this->is_valid_path (path) && + this->process_command (Request::FTP_CWD, path) != Response::COMPLETED_OK); + } + + bool ClientRequestHandler::is_dir (const ACE_CString& path) + { + return (this->is_valid_path (path) && + this->process_command (Request::FTP_CWD, path) == Response::COMPLETED_OK); + } + + bool ClientRequestHandler::set_filetype (bool binary) + { + return this->process_command (Request::FTP_TYPE, binary ? "I" : "A") == Response::COMPLETED_OK; + } + + ClientRequestHandler::stream_type* + ClientRequestHandler::start_download (const ACE_CString& path, bool binary) + { + if (path.empty () || this->is_dir (path)) + { + if (this->set_filetype (false)) + { + return this->open_data_connection (Request::FTP_LIST, path); + } + } + else + { + if (this->set_filetype (binary)) + { + return this->open_data_connection (Request::FTP_RETR, path); + } + } + return 0; + } + + ClientRequestHandler::stream_type* + ClientRequestHandler::start_upload (const ACE_CString& path, bool binary) + { + if (this->set_filetype (binary)) + { + return this->open_data_connection (Request::FTP_STOR, path); + } + return 0; + } + + bool ClientRequestHandler::finish_transfer () + { + if (transfer_active_) + { + stream_type* old_stream = dynamic_cast<stream_type*> (this->in_data_stream_.set_stream (0)); + if (old_stream) + { + // decrease ref count to 1 + const_cast<connection_type&> (old_stream->stream ()).remove_reference (); + // also removes last ref count on stream handler + delete old_stream; + } + old_stream = dynamic_cast<stream_type*> (this->out_data_stream_.set_stream (0)); + { + // decrease ref count to 1 + const_cast<connection_type&> (old_stream->stream ()).remove_reference (); + // also removes last ref count on stream handler + delete old_stream; + } + + this->transfer_active_ = false; + + this->session ()->receive_response (this->response_); + return this->response_.is_completed_ok (); + } + return true; + } + + bool ClientRequestHandler::abort_transfer () + { + if (transfer_active_) + { + this->session ()->send_interrupt (); + this->process_command (Request::FTP_ABOR); + if (this->response_.status () == 426) + this->session ()->receive_response (this->response_); + + stream_type* old_stream = dynamic_cast<stream_type*> (this->in_data_stream_.set_stream (0)); + { + // decrease ref count to 1 + const_cast<connection_type&> (old_stream->stream ()).remove_reference (); + // also removes last ref count on stream handler + delete old_stream; + } + old_stream = dynamic_cast<stream_type*> (this->out_data_stream_.set_stream (0)); + { + // decrease ref count to 1 + const_cast<connection_type&> (old_stream->stream ()).remove_reference (); + // also removes last ref count on stream handler + delete old_stream; + } + + this->transfer_active_ = false; + + return this->response_.is_completed_ok (); + } + return true; + } + + Response::StatusType + ClientRequestHandler::process_command (const ACE_CString& cmd, + const ACE_CString& arg) + { + if (this->session ()->send_request (this->request_(cmd) << arg)) + { + this->session ()->receive_response (this->response_); + } + else + { + this->response_(0); + } + return this->response_.status_type (); + } + + ClientRequestHandler::stream_type* + ClientRequestHandler::open_data_connection (const ACE_CString& cmd, + const ACE_CString& arg) + { + if (this->use_passive_mode_) + { + // get address for passive data connection + ACE_INET_Addr data_addr; + if (this->get_passive_address (data_addr)) + { + // establish data connection + + // copy sync settings from session + unsigned long f_reactor = + this->session ()->is_reactive() ? ACE_Synch_Options::USE_REACTOR : 0; + ACE_Synch_Options sync_opt (ACE_Synch_Options::USE_TIMEOUT | f_reactor, + this->session ()->timeout ()); + + typedef ACE_Connector<SessionHolder::session_type::connection_type, + ACE_SOCK_CONNECTOR> connector_type; + connector_type connector; + + // create connection object (stream handler) + SessionHolder::session_type::connection_type* data_connection = 0; + ACE_NEW_NORETURN (data_connection, + SessionHolder::session_type::connection_type(sync_opt)); + if (data_connection == 0) + { + return 0; + } + + // connect to data connection address + if (connector.connect (data_connection, + data_addr, + ACE_Synch_Options (0, + this->session ()->timeout ())) == -1) + { + INET_ERROR (1, (LM_ERROR, DLINFO + ACE_TEXT ("(%d) ACE_FTP_ClientRequestHandler::open_data_connection - ") + ACE_TEXT ("failed to connect to %C:%d\n"), + ACE_OS::last_error (), + data_addr.get_host_name (), + data_addr.get_port_number ())); + // as the connection was dynamically allocated + // the connector causes it to be destroyed after + // the connection failure + return 0; + } + + // enable ref counting so we can control when to destroy + data_connection->reference_counting_policy ().value ( + ACE_Event_Handler::Reference_Counting_Policy::ENABLED); + + // create io stream for connection + stream_type* data_stream = 0; + ACE_NEW_NORETURN (data_stream, + stream_type (data_connection)); + if (data_stream) + { + if (this->process_command (cmd, arg) == Response::PRELIM_OK) + return data_stream; + + delete data_stream; // decreases ref count on connection + } + // remove last ref count -> delete + data_connection->remove_reference (); + } + } + else + { + // address for active data connection + ACE_INET_Addr data_addr; + this->session ()->get_local_addr (data_addr); + data_addr.set_port_number (this->active_port_); + + // copy sync settings from session + unsigned long f_reactor = + this->session ()->is_reactive() ? ACE_Synch_Options::USE_REACTOR : 0; + ACE_Synch_Options sync_opt (ACE_Synch_Options::USE_TIMEOUT | f_reactor, + this->session ()->timeout ()); + + typedef ACE_Oneshot_Acceptor<SessionHolder::session_type::connection_type, + ACE_SOCK_ACCEPTOR> acceptor_type; + acceptor_type acceptor; + + // start data connection acceptor listening and retrieve actual listening address + if (acceptor.open (data_addr) == 0 && + acceptor.acceptor ().get_local_addr (data_addr) == 0) + { + // send listen address to peer followed by data command to execute + if (this->send_active_address (data_addr) && + this->process_command (cmd, arg) == Response::PRELIM_OK) + { + // create connection object (stream handler) + SessionHolder::session_type::connection_type* data_connection = 0; + ACE_NEW_NORETURN (data_connection, + SessionHolder::session_type::connection_type(sync_opt)); + if (data_connection == 0) + { + return 0; + } + + // accept data connection from peer + if (acceptor.accept (data_connection, + 0, + ACE_Synch_Options (ACE_Synch_Options::USE_TIMEOUT, + this->session ()->timeout ())) == -1) + { + INET_ERROR (1, (LM_ERROR, DLINFO + ACE_TEXT ("(%d) ACE_FTP_ClientRequestHandler::open_data_connection - ") + ACE_TEXT ("failed to accept connection to %C:%d\n"), + ACE_OS::last_error (), + data_addr.get_host_name (), + data_addr.get_port_number ())); + + // as the connection was dynamically allocated + // the acceptor causes it to be destroyed after + // the connection failure + return 0; + } + + // enable ref counting so we can control when to destroy + data_connection->reference_counting_policy ().value ( + ACE_Event_Handler::Reference_Counting_Policy::ENABLED); + + // create io stream for connection + stream_type* data_stream = 0; + ACE_NEW_NORETURN (data_stream, + stream_type (data_connection)); + if (data_stream) + { + return data_stream; + } + // remove last ref count -> delete + data_connection->remove_reference (); + } + } + } + return 0; + } + + + bool + ClientRequestHandler::get_passive_address (ACE_INET_Addr& addr) + { + // can we use the RFC 2428 extensions? + if (this->session ()->supports_ftp_extensions ()) + { + if (this->process_command (Request::FTP_EPSV) == Response::COMPLETED_OK) + { + return this->parse_ext_address (this->response_.response ()[0], + addr); + } + // flag RFC 2428 extensions as unsupported + this->session ()->set_ftp_extension_support (false); + } + // use 'old' command + if (this->process_command (Request::FTP_PASV) == Response::COMPLETED_OK) + { + return this->parse_address (this->response_.response ()[0], + addr); + } + return false; + } + + bool + ClientRequestHandler::parse_address (const ACE_CString& str, ACE_INET_Addr& addr) + { + static const int eof_ = std::char_traits<char>::eof (); + + ACE::IOS::CString_OStream sos_host; + u_short port_hi = 0, port_lo = 0; + + ACE::IOS::CString_IStream sis (str); + sis.ignore (str.length (), '('); + int ch = sis.get (); + if (ACE_OS::ace_isdigit (ch)) + { + for (int i=0; i<4 ;++i) + { + if (ch == ',') + { + sos_host.put ('.'); + ch = sis.get (); + } + while (ch != eof_ && ACE_OS::ace_isdigit (ch)) + { + sos_host.put (ch); + ch = sis.get (); + } + } + if (ch == ',') + { + sis >> port_hi; + ch = sis.get (); + if (ch == ',') + { + sis >> port_lo; + + u_short port = port_hi*256 + port_lo; + addr.set (port, sos_host.str ().c_str ()); + return true; + } + } + } + return false; + } + + bool + ClientRequestHandler::parse_ext_address (const ACE_CString& str, ACE_INET_Addr& addr) + { + static const int eof_ = std::char_traits<char>::eof (); + + ACE::IOS::CString_IStream sis (str); + sis.ignore (str.length (), '('); + int ch = sis.get (); + if (ch != eof_) + { + int delim = ch; + sis.ignore (str.length (), delim); + sis.ignore (str.length (), delim); + if (sis.peek () != eof_) + { + u_short port = 0; + sis >> port; + addr.set (port, this->session ()->get_host ().c_str ()); + return true; + } + } + return false; + } + + bool + ClientRequestHandler::send_active_address (const ACE_INET_Addr& addr) + { + ACE::IOS::CString_OStream arg; + char ip_buf[128]; + if (addr.get_host_addr (ip_buf, sizeof(ip_buf)) == 0) + { + return false; + } + u_short port = addr.get_port_number (); + // can we use the RFC 2428 extensions? + if (this->session ()->supports_ftp_extensions ()) + { + arg << '|' + << (addr.get_type () == AF_INET ? '1' : '2') + << '|' + << (&ip_buf[0]) + << '|' + << port + << '|'; + if (this->process_command (Request::FTP_EPRT, arg.str ()) == Response::COMPLETED_OK) + { + return true; + } + // flag RFC 2428 extensions as unsupported + this->session ()->set_ftp_extension_support (false); + + arg.clear (); + } + // use 'old' command + ACE_CString ip_addr = addr.get_host_addr (ip_buf, sizeof(ip_buf)); + ACE_CString::size_type pos; + while ((pos = ip_addr.find ('.')) != ACE_CString::npos) + { + ip_addr[pos] = ','; + } + arg << ip_addr << ',' + << (port / 256) // port hi + << ',' + << (port % 256); // port lo + return (this->process_command (Request::FTP_PORT, arg.str ()) == Response::COMPLETED_OK); + } + + } +} + +ACE_END_VERSIONED_NAMESPACE_DECL |