diff --git a/ChangeLog b/ChangeLog index bbe2d745..bfa45b58 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2009-05-10 Tatsuhiro Tsujikawa + + Added BASIC authentication for XML-RPC. Added --xml-rpc-user and + --xml-rpc-passwd option. + * src/HttpServer.cc + * src/HttpServer.h + * src/HttpServerCommand.cc + * src/OptionHandlerFactory.cc + * src/prefs.cc + * src/prefs.h + * src/usage_text.h + 2009-05-10 Tatsuhiro Tsujikawa Fixed the bug that SocketCore::isReadable() prevents aria2 from diff --git a/src/HttpServer.cc b/src/HttpServer.cc index 70b5e00b..3ab9ad9c 100644 --- a/src/HttpServer.cc +++ b/src/HttpServer.cc @@ -44,6 +44,7 @@ #include "Util.h" #include "LogFactory.h" #include "Logger.h" +#include "Base64.h" namespace aria2 { @@ -133,13 +134,28 @@ bool HttpServer::supportsPersistentConnection() const void HttpServer::feedResponse(const std::string& text, const std::string& contentType) { - std::string header = "HTTP/1.1 200 OK\r\n" + feedResponse("200 OK", "", text, contentType); +} + +void HttpServer::feedResponse(const std::string& status, + const std::string& headers, + const std::string& text, + const std::string& contentType) +{ + std::string header = "HTTP/1.1 "+status+"\r\n" "Content-Type: "+contentType+"\r\n" "Content-Length: "+Util::uitos(text.size())+"\r\n"; if(!supportsPersistentConnection()) { header += "Connection: close\r\n"; } + if(!headers.empty()) { + header += headers; + if(!Util::endsWith(headers, "\r\n")) { + header += "\r\n"; + } + } + header += "\r\n"; _logger->debug("HTTP Server sends response:\n%s", header.c_str()); @@ -158,4 +174,23 @@ bool HttpServer::sendBufferIsEmpty() const return _socketBuffer.sendBufferIsEmpty(); } +bool HttpServer::authenticate() +{ + if(_username.empty()) { + return true; + } + + std::string authHeader = _lastRequestHeader->getFirst("Authorization"); + if(authHeader.empty()) { + return false; + } + std::pair p = Util::split(authHeader, " "); + if(p.first != "Basic") { + return false; + } + std::string userpass = Base64::decode(p.second); + std::pair userpassPair = Util::split(userpass, ":"); + return _username == userpassPair.first && _password == userpassPair.second; +} + } // namespace aria2 diff --git a/src/HttpServer.h b/src/HttpServer.h index 8c8d184a..3142ad6d 100644 --- a/src/HttpServer.h +++ b/src/HttpServer.h @@ -62,6 +62,8 @@ private: uint64_t _lastContentLength; std::stringstream _lastBody; bool _keepAlive; + std::string _username; + std::string _password; public: HttpServer(const SharedHandle& socket, DownloadEngine* e); @@ -77,6 +79,20 @@ public: void feedResponse(const std::string& text, const std::string& contentType); + void feedResponse(const std::string& status, + const std::string& headers, + const std::string& text, + const std::string& contentType); + + bool authenticate(); + + void setUsernamePassword + (const std::string& username, const std::string& password) + { + _username = username; + _password = password; + } + ssize_t sendResponse(); bool sendBufferIsEmpty() const; @@ -86,6 +102,8 @@ public: void enableKeepAlive() { _keepAlive = true; } void disableKeepAlive() { _keepAlive = false; } + + uint64_t getContentLength() const { return _lastContentLength; } }; } // namespace aria2 diff --git a/src/HttpServerCommand.cc b/src/HttpServerCommand.cc index 7d889945..725fe582 100644 --- a/src/HttpServerCommand.cc +++ b/src/HttpServerCommand.cc @@ -41,7 +41,10 @@ #include "RequestGroup.h" #include "RequestGroupMan.h" #include "HttpServerBodyCommand.h" +#include "HttpServerResponseCommand.h" #include "RecoverableException.h" +#include "prefs.h" +#include "Option.h" namespace aria2 { @@ -53,6 +56,8 @@ HttpServerCommand::HttpServerCommand(int32_t cuid, DownloadEngine* e, _httpServer(new HttpServer(socket, e)) { _e->addSocketForReadCheck(_socket, this); + _httpServer->setUsernamePassword(_e->option->get(PREF_XML_RPC_USER), + _e->option->get(PREF_XML_RPC_PASSWD)); } HttpServerCommand::HttpServerCommand(int32_t cuid, @@ -87,6 +92,18 @@ bool HttpServerCommand::execute() e, cuid); return true; } + if(!_httpServer->authenticate()) { + _httpServer->disableKeepAlive(); + _httpServer->feedResponse("401 Unauthorized", + "WWW-Authenticate: Basic realm=\"aria2\"", + "","text/html"); + Command* command = + new HttpServerResponseCommand(cuid, _httpServer, _e, _socket); + command->setStatus(Command::STATUS_ONESHOT_REALTIME); + _e->commands.push_back(command); + _e->setNoWait(true); + return true; + } if(header.isNull()) { _e->commands.push_back(this); return false; diff --git a/src/OptionHandlerFactory.cc b/src/OptionHandlerFactory.cc index 91cd928e..dacaee96 100644 --- a/src/OptionHandlerFactory.cc +++ b/src/OptionHandlerFactory.cc @@ -364,6 +364,20 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers() op->addTag(TAG_ADVANCED); handlers.push_back(op); } + { + SharedHandle op(new DefaultOptionHandler + (PREF_XML_RPC_USER, + TEXT_XML_RPC_USER)); + op->addTag(TAG_ADVANCED); + handlers.push_back(op); + } + { + SharedHandle op(new DefaultOptionHandler + (PREF_XML_RPC_PASSWD, + TEXT_XML_RPC_PASSWD)); + op->addTag(TAG_ADVANCED); + handlers.push_back(op); + } #endif // ENABLE_XML_RPC // HTTP/FTP options { diff --git a/src/prefs.cc b/src/prefs.cc index 4837b853..2500e16a 100644 --- a/src/prefs.cc +++ b/src/prefs.cc @@ -163,6 +163,10 @@ const std::string PREF_RESET_URI("reset-uri"); const std::string PREF_DRY_RUN("dry-run"); // value: true | false const std::string PREF_REUSE_URI("reuse-uri"); +// value: string +const std::string PREF_XML_RPC_USER("xml-rpc-user"); +// value: string +const std::string PREF_XML_RPC_PASSWD("xml-rpc-passwd"); /** * FTP related preferences diff --git a/src/prefs.h b/src/prefs.h index cd69cc9d..b9888ab7 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -167,6 +167,10 @@ extern const std::string PREF_RESET_URI; extern const std::string PREF_DRY_RUN; // value: true | false extern const std::string PREF_REUSE_URI; +// value: string +extern const std::string PREF_XML_RPC_USER; +// value: string +extern const std::string PREF_XML_RPC_PASSWD; /** * FTP related preferences diff --git a/src/usage_text.h b/src/usage_text.h index 6108fa89..f78597ff 100644 --- a/src/usage_text.h +++ b/src/usage_text.h @@ -481,10 +481,15 @@ _(" --event-poll=POLL Specify the method for polling events.") #define TEXT_XML_RPC_LISTEN_PORT \ _(" --xml-rpc-listen-port=PORT Specify a port number for XML-RPC server to listen\n"\ " to.") -// Excluded from translation candidiates because it is subject to change. #define TEXT_ENABLE_XML_RPC \ _(" --enable-xml-rpc[=true|false] Enable XML-RPC server.\n"\ - " See also --xml-rpc-listen-port option.") + " It is strongly recommended to set username and\n"\ + " password using --xml-rpc-user and --xml-rpc-passwd\n"\ + " option. See also --xml-rpc-listen-port option.") +#define TEXT_XML_RPC_USER \ +_(" --xml-rpc-user=USER Set XML-RPC user.") +#define TEXT_XML_RPC_PASSWD \ +_(" --xml-rpc-passwd=PASSWD Set XML-RPC password.") #define TEXT_BT_EXTERNAL_IP \ _(" --bt-external-ip=IPADDRESS Specify the external IP address to report to a\n"\ " BitTorrent tracker. Although this function is\n"\