diff --git a/configure.ac b/configure.ac index 1f7d0982..bcd63582 100644 --- a/configure.ac +++ b/configure.ac @@ -28,6 +28,7 @@ PKG_CHECK_MODULES(STUFF, sigc++-2.0 libcurl >= 7.12.0 libtorrent >= 0.11.3, AC_LANG_PUSH(C++) TORRENT_WITH_FASTCGI +TORRENT_WITH_XMLRPC_C AC_LANG_POP(C++) AC_DEFINE(HAVE_CONFIG_H, 1, true if config.h was included) diff --git a/rak/functional_fun.h b/rak/functional_fun.h index 37297917..b47b16a5 100644 --- a/rak/functional_fun.h +++ b/rak/functional_fun.h @@ -80,6 +80,14 @@ public: virtual Result operator () (Arg1 arg1, Arg2 arg2) = 0; }; +template +class function_base3 { +public: + virtual ~function_base3() {} + + virtual Result operator () (Arg1 arg1, Arg2 arg2, Arg3 arg3) = 0; +}; + template class function0 { public: @@ -131,6 +139,23 @@ private: std::auto_ptr m_base; }; +template +class function3 { +public: + typedef Result result_type; + typedef function_base3 base_type; + + bool is_valid() const { return m_base.get() != NULL; } + + void set(base_type* base) { m_base = std::auto_ptr(base); } + base_type* release() { return m_base.release(); } + + Result operator () (Arg1 arg1, Arg2 arg2, Arg3 arg3) { return (*m_base)(arg1, arg2, arg3); } + +private: + std::auto_ptr m_base; +}; + template class ptr_fn0_t : public function_base0 { public: @@ -203,6 +228,36 @@ private: Func m_func; }; +template +class mem_fn3_t : public function_base3 { +public: + typedef Result (Object::*Func)(Arg1, Arg2, Arg3); + + mem_fn3_t(Object* object, Func func) : m_object(object), m_func(func) {} + virtual ~mem_fn3_t() {} + + virtual Result operator () (Arg1 arg1, Arg2 arg2, Arg3 arg3) { return (m_object->*m_func)(arg1, arg2, arg3); } + +private: + Object* m_object; + Func m_func; +}; + +template +class mem_fn2_t : public function_base2 { +public: + typedef Result (Object::*Func)(Arg1, Arg2); + + mem_fn2_t(Object* object, Func func) : m_object(object), m_func(func) {} + virtual ~mem_fn2_t() {} + + virtual Result operator () (Arg1 arg1, Arg2 arg2) { return (m_object->*m_func)(arg1, arg2); } + +private: + Object* m_object; + Func m_func; +}; + template class const_mem_fn0_t : public function_base0 { public: @@ -416,6 +471,18 @@ mem_fn(Object* object, Result (Object::*func)(Arg1)) { return new mem_fn1_t(object, func); } +template +inline function_base2* +mem_fn(Object* object, Result (Object::*func)(Arg1, Arg2)) { + return new mem_fn2_t(object, func); +} + +template +inline function_base3* +mem_fn(Object* object, Result (Object::*func)(Arg1, Arg2, Arg3)) { + return new mem_fn3_t(object, func); +} + template inline function_base0* mem_fn(const Object* object, Result (Object::*func)() const) { diff --git a/scripts/checks.m4 b/scripts/checks.m4 index 77194aa8..ea7272b7 100644 --- a/scripts/checks.m4 +++ b/scripts/checks.m4 @@ -291,11 +291,11 @@ AC_DEFUN([TORRENT_WITH_ADDRESS_SPACE], [ AC_DEFUN([TORRENT_WITH_FASTCGI], [ + AC_MSG_CHECKING(for FastCGI) + AC_ARG_WITH(fastcgi, [ --with-fastcgi=PATH Enable FastCGI RPC support.], [ - AC_MSG_CHECKING(for FastCGI) - if test "$withval" = "no"; then AC_MSG_RESULT(no) @@ -334,8 +334,44 @@ AC_DEFUN([TORRENT_WITH_FASTCGI], [ AC_DEFINE(HAVE_FASTCGI, 1, Support for FastCGI.) fi ],[ - AC_MSG_CHECKING(for FastCGI) - AC_MSG_RESULT(no) + AC_MSG_RESULT(ignored) ]) ]) + +AC_DEFUN([TORRENT_WITH_XMLRPC_C], [ + AC_MSG_CHECKING(for XMLRPC-C) + + AC_ARG_WITH(xmlrpc-c, + [ --with-xmlrpc-c=PATH Enable XMLRPC-C support.], + [ + if test "$withval" = "no"; then + AC_MSG_RESULT(no) + + else + if eval xmlrpc-c-config --version 2>/dev/null >/dev/null; then + CXXFLAGS="$CXXFLAGS `xmlrpc-c-config --cflags server-util`" + LIBS="$LIBS `xmlrpc-c-config --libs server-util` -lxmlrpc_server" + + AC_TRY_LINK( + [ #include + ],[ xmlrpc_registry_new(NULL); ], + [ + AC_MSG_RESULT(ok) + ], [ + AC_MSG_RESULT(failed) + AC_MSG_ERROR(Could not compile XMLRPC-C test.) + ]) + + AC_DEFINE(HAVE_XMLRPC_C, 1, Support for XMLRPC-C.) + + else + AC_MSG_RESULT(failed) + AC_MSG_ERROR(Could not compile XMLRPC-C test.) + fi + fi + + ],[ + AC_MSG_RESULT(ignored) + ]) +]) diff --git a/src/control.cc b/src/control.cc index 35ceef4a..94b94539 100644 --- a/src/control.cc +++ b/src/control.cc @@ -51,6 +51,7 @@ #include "input/manager.h" #include "input/input_event.h" #include "rpc/fast_cgi.h" +#include "rpc/xmlrpc.h" #include "ui/root.h" #include "utils/variable_map.h" @@ -72,6 +73,7 @@ Control::Control() : m_downloadVariables(new utils::VariableMap()), m_fastCgi(NULL), + m_xmlrpc(NULL), m_tick(0) { @@ -127,6 +129,7 @@ Control::initialize() { void Control::cleanup() { delete m_fastCgi; m_fastCgi = NULL; + delete m_xmlrpc; m_xmlrpc = NULL; priority_queue_erase(&taskScheduler, &m_taskShutdown); diff --git a/src/control.h b/src/control.h index 965e1728..df1bc8a9 100644 --- a/src/control.h +++ b/src/control.h @@ -64,6 +64,7 @@ namespace input { namespace rpc { class FastCgi; + class XmlRpc; } namespace utils { @@ -106,6 +107,9 @@ public: rpc::FastCgi* fast_cgi() { return m_fastCgi; } void set_fast_cgi(rpc::FastCgi* f) { m_fastCgi = f; } + rpc::XmlRpc* xmlrpc() { return m_xmlrpc; } + void set_xmlrpc(rpc::XmlRpc* f) { m_xmlrpc = f; } + uint64_t tick() const { return m_tick; } void inc_tick() { m_tick++; } @@ -136,6 +140,7 @@ private: utils::VariableMap* m_downloadVariables; rpc::FastCgi* m_fastCgi; + rpc::XmlRpc* m_xmlrpc; uint64_t m_tick; diff --git a/src/option_handler_rules.cc b/src/option_handler_rules.cc index e605ddc5..7e447e40 100644 --- a/src/option_handler_rules.cc +++ b/src/option_handler_rules.cc @@ -65,6 +65,7 @@ #include "core/scheduler.h" #include "core/view_manager.h" #include "rpc/fast_cgi.h" +#include "rpc/xmlrpc.h" #include "ui/root.h" #include "utils/directory.h" #include "utils/parse.h" @@ -309,7 +310,12 @@ apply_fast_cgi(const std::string& arg) { if (control->fast_cgi() != NULL) throw torrent::input_error("FastCGI already enabled."); + if (control->xmlrpc() == NULL) { + control->set_xmlrpc(new rpc::XmlRpc); + } + control->set_fast_cgi(new rpc::FastCgi(arg)); + control->fast_cgi()->set_slot_process(rak::mem_fn(control->xmlrpc(), &rpc::XmlRpc::process)); } void diff --git a/src/rpc/Makefile.am b/src/rpc/Makefile.am index 78d299a1..efb50dad 100644 --- a/src/rpc/Makefile.am +++ b/src/rpc/Makefile.am @@ -2,6 +2,8 @@ noinst_LIBRARIES = libsub_rpc.a libsub_rpc_a_SOURCES = \ fast_cgi.cc \ - fast_cgi.h + fast_cgi.h \ + xmlrpc.h \ + xmlrpc.cc INCLUDES = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) diff --git a/src/rpc/fast_cgi.cc b/src/rpc/fast_cgi.cc index f22651b6..86cd96c0 100644 --- a/src/rpc/fast_cgi.cc +++ b/src/rpc/fast_cgi.cc @@ -43,6 +43,7 @@ #include #include #include +#include #include #include "globals.h" @@ -116,6 +117,7 @@ FastCgi::event_read() { int length; char* endPtr; char* buffer = NULL; + slot_write slotWrite; const char* contentLength = FCGX_GetParam("CONTENT_LENGTH", m_request->envp); @@ -131,14 +133,15 @@ FastCgi::event_read() { goto event_read_exit; } - if (FCGX_PutS("This worked!!!", m_request->out) == -1) { + slotWrite.set(rak::mem_fn(this, &FastCgi::receive_write)); + + if (!m_slotProcess(buffer, length, slotWrite) || + FCGX_FFlush(m_request->out) == -1) { control->core()->push_log("FastCGI could not write data."); goto event_read_exit; } - control->core()->push_log("Got a message: " + std::string(buffer, buffer + 30)); - - FCGX_Finish_r(m_request); + control->core()->push_log("Processed an XMLRPC call."); event_read_exit: FCGX_Finish_r(m_request); @@ -154,6 +157,15 @@ FastCgi::event_error() { throw torrent::internal_error("FastCgi::event_error()."); } +bool +FastCgi::receive_write(const char* buffer, uint32_t length) { + return + FCGX_FPrintF(m_request->out, + "Content-type: text/plain\r\n" + "Content-length: %i\r\n\r\n", length) != -1 && + FCGX_PutStr(buffer, length, m_request->out) != -1; +} + #else FastCgi::FastCgi(const std::string& path) { throw torrent::resource_error("FastCGI not supported."); } @@ -162,6 +174,8 @@ void FastCgi::event_read() {} void FastCgi::event_write() {} void FastCgi::event_error() {} +void FastCgi::receive_write(const char* buffer, uint32_t length); + #endif } diff --git a/src/rpc/fast_cgi.h b/src/rpc/fast_cgi.h index a781b600..8c39a835 100644 --- a/src/rpc/fast_cgi.h +++ b/src/rpc/fast_cgi.h @@ -42,24 +42,38 @@ struct FCGX_Request; +namespace rak { + template class function2; + template class function3; +} + namespace rpc { class FastCgi : public torrent::Event { public: + typedef rak::function2 slot_write; + typedef rak::function3 slot_process; + FastCgi(const std::string& path); virtual ~FastCgi(); const std::string path() const { return m_path; } + void set_slot_process(slot_process::base_type* s) { m_slotProcess.set(s); } + virtual void event_read(); virtual void event_write(); virtual void event_error(); + bool receive_write(const char* buffer, uint32_t length); + private: static bool m_initialized; FCGX_Request* m_request; std::string m_path; + + slot_process m_slotProcess; }; } diff --git a/src/rpc/xmlrpc.cc b/src/rpc/xmlrpc.cc new file mode 100644 index 00000000..c0761f31 --- /dev/null +++ b/src/rpc/xmlrpc.cc @@ -0,0 +1,93 @@ +// rTorrent - BitTorrent client +// Copyright (C) 2005-2006, Jari Sundell +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// In addition, as a special exception, the copyright holders give +// permission to link the code of portions of this program with the +// OpenSSL library under certain conditions as described in each +// individual source file, and distribute linked combinations +// including the two. +// +// You must obey the GNU General Public License in all respects for +// all of the code used other than OpenSSL. If you modify file(s) +// with this exception, you may extend this exception to your version +// of the file(s), but you are not obligated to do so. If you do not +// wish to do so, delete this exception statement from your version. +// If you delete this exception statement from all source files in the +// program, then also delete it here. +// +// Contact: Jari Sundell +// +// Skomakerveien 33 +// 3185 Skoppum, NORWAY + +#include "config.h" + +#include +#include + +#include "xmlrpc.h" + +namespace rpc { + +xmlrpc_value* +xmlrpc_test_add(xmlrpc_env* env, xmlrpc_value* value, void* serverInfo) { + // Grabbed from the XMLRPC-C examples. + xmlrpc_int32 x, y, z; + + /* Parse our argument array. */ + xmlrpc_parse_value(env, value, "(ii)", &x, &y); + if (env->fault_occurred) + return NULL; + + /* Add our two numbers. */ + z = x + y; + + /* Return our result. */ + return xmlrpc_build_value(env, "i", z); +} + +XmlRpc::XmlRpc() : m_env(new xmlrpc_env) { + xmlrpc_env_init(m_env); + + m_registry = xmlrpc_registry_new(m_env); + + // Move this out. + xmlrpc_registry_add_method_w_doc(m_env, m_registry, NULL, "my.test", &xmlrpc_test_add, new int, "i:ii", "Not much."); +} + +XmlRpc::~XmlRpc() { + xmlrpc_registry_free(m_registry); + xmlrpc_env_clean(m_env); + delete m_env; +} + +bool +XmlRpc::process(const char* inBuffer, uint32_t length, slot_write slotWrite) { + xmlrpc_env localEnv; + xmlrpc_env_init(&localEnv); + + xmlrpc_mem_block* memblock = xmlrpc_registry_process_call(&localEnv, m_registry, NULL, inBuffer, length); + + bool result = slotWrite((const char*)xmlrpc_mem_block_contents(memblock), + xmlrpc_mem_block_size(memblock)); + + xmlrpc_mem_block_free(memblock); + xmlrpc_env_clean(&localEnv); + return result; +} + +} diff --git a/src/rpc/xmlrpc.h b/src/rpc/xmlrpc.h new file mode 100644 index 00000000..aab521ec --- /dev/null +++ b/src/rpc/xmlrpc.h @@ -0,0 +1,63 @@ +// rTorrent - BitTorrent client +// Copyright (C) 2005-2006, Jari Sundell +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// In addition, as a special exception, the copyright holders give +// permission to link the code of portions of this program with the +// OpenSSL library under certain conditions as described in each +// individual source file, and distribute linked combinations +// including the two. +// +// You must obey the GNU General Public License in all respects for +// all of the code used other than OpenSSL. If you modify file(s) +// with this exception, you may extend this exception to your version +// of the file(s), but you are not obligated to do so. If you do not +// wish to do so, delete this exception statement from your version. +// If you delete this exception statement from all source files in the +// program, then also delete it here. +// +// Contact: Jari Sundell +// +// Skomakerveien 33 +// 3185 Skoppum, NORWAY + +#ifndef RTORRENT_RPC_XMLRPC_H +#define RTORRENT_RPC_XMLRPC_H + +#include + +typedef struct _xmlrpc_env xmlrpc_env; +typedef struct _xmlrpc_registry xmlrpc_registry; + +namespace rpc { + +class XmlRpc { +public: + typedef rak::function2 slot_write; + + XmlRpc(); + ~XmlRpc(); + + bool process(const char* inBuffer, uint32_t length, slot_write slotWrite); + +private: + xmlrpc_env* m_env; + xmlrpc_registry* m_registry; +}; + +} + +#endif