From a8bd4050407d22021cb2f0ebdab13bbd736201b3 Mon Sep 17 00:00:00 2001 From: rakshasa Date: Thu, 19 Jul 2007 20:50:48 +0000 Subject: [PATCH] * Added support for xmlrpc commands on torrent::File objects. The xmlrpc command takes the download info-hash and the file index as the first two parameters. * Added several file commands. git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@937 e378c898-3ddf-0310-93e7-cc216c733640 --- src/Makefile.am | 1 + src/command_download.cc | 6 +- src/command_file.cc | 133 ++++++++++++++++++++++++++ src/command_helpers.cc | 13 ++- src/command_helpers.h | 4 + src/command_network.cc | 17 +++- src/command_ui.cc | 2 +- src/rpc/Makefile.am | 2 + src/rpc/command_file_slot.cc | 123 ++++++++++++++++++++++++ src/rpc/command_file_slot.h | 176 +++++++++++++++++++++++++++++++++++ src/rpc/command_map.cc | 52 +++++++++-- src/rpc/command_map.h | 35 +++---- src/rpc/exec_file.cc | 2 + src/rpc/parse_commands.h | 2 + src/rpc/xmlrpc.cc | 169 ++++++++++++++++++++++++++------- src/rpc/xmlrpc.h | 16 +++- 16 files changed, 686 insertions(+), 67 deletions(-) create mode 100644 src/command_file.cc create mode 100644 src/rpc/command_file_slot.cc create mode 100644 src/rpc/command_file_slot.h diff --git a/src/Makefile.am b/src/Makefile.am index 5b2e02d2..510250e5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,6 +19,7 @@ rtorrent_LDADD = \ rtorrent_SOURCES = \ command_download.cc \ command_events.cc \ + command_file.cc \ command_helpers.cc \ command_helpers.h \ command_local.cc \ diff --git a/src/command_download.cc b/src/command_download.cc index f8c72f86..03f95589 100644 --- a/src/command_download.cc +++ b/src/command_download.cc @@ -278,9 +278,9 @@ initialize_command_download() { ADD_CD_F_VOID("erase", rak::make_mem_fun(control->core()->download_list(), &core::DownloadList::erase_ptr)); ADD_CD_F_VOID("check_hash", rak::make_mem_fun(control->core()->download_list(), &core::DownloadList::check_hash)); - ADD_CD_VALUE_UNI("is_open", rak::on(std::mem_fun(&core::Download::download), std::mem_fun(&torrent::Download::is_open))); - ADD_CD_VALUE_UNI("is_active", rak::on(std::mem_fun(&core::Download::download), std::mem_fun(&torrent::Download::is_active))); - ADD_CD_VALUE_UNI("is_hash_checked", rak::on(std::mem_fun(&core::Download::download), std::mem_fun(&torrent::Download::is_hash_checked))); + ADD_CD_VALUE_UNI("is_open", rak::on(std::mem_fun(&core::Download::download), std::mem_fun(&torrent::Download::is_open))); + ADD_CD_VALUE_UNI("is_active", rak::on(std::mem_fun(&core::Download::download), std::mem_fun(&torrent::Download::is_active))); + ADD_CD_VALUE_UNI("is_hash_checked", rak::on(std::mem_fun(&core::Download::download), std::mem_fun(&torrent::Download::is_hash_checked))); ADD_CD_VALUE_UNI("is_hash_checking", rak::on(std::mem_fun(&core::Download::download), std::mem_fun(&torrent::Download::is_hash_checking))); // 0 - stopped diff --git a/src/command_file.cc b/src/command_file.cc new file mode 100644 index 00000000..740d8ec2 --- /dev/null +++ b/src/command_file.cc @@ -0,0 +1,133 @@ +// 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 +#include +//#include +#include +#include + +//#include "core/download.h" +#include "core/manager.h" +#include "rpc/command_slot.h" +#include "rpc/command_file_slot.h" + +#include "globals.h" +#include "control.h" +#include "command_helpers.h" + + +#define ADD_CF_SLOT(key, function, slot, parm, doc) \ + commandFileSlotsItr->set_slot(slot); \ + rpc::commands.insert_file(key, commandFileSlotsItr++, &rpc::CommandFileSlot::function, rpc::CommandMap::flag_dont_delete, parm, doc); + +#define ADD_CF_SLOT_PUBLIC(key, function, slot, parm, doc) \ + commandFileSlotsItr->set_slot(slot); \ + rpc::commands.insert_file(key, commandFileSlotsItr++, &rpc::CommandFileSlot::function, rpc::CommandMap::flag_dont_delete | rpc::CommandMap::flag_public_xmlrpc, parm, doc); + +/* +#define ADD_CF_VOID(key, slot) \ + ADD_CF_SLOT_PUBLIC("get_f_" key, call_unknown, rpc::object_f_fn(slot), "i:", "") + +#define ADD_CF_V_VOID(key, slot) \ + ADD_CF_SLOT_PUBLIC("d_" key, call_unknown, rpc::object_f_fn(slot), "i:", "") + +#define ADD_CF_F_VOID(key, slot) \ + ADD_CF_SLOT_PUBLIC("d_" key, call_unknown, rpc::object_void_f_fn(slot), "i:", "") + +#define ADD_CF_LIST(key, slot) \ + ADD_CF_SLOT_PUBLIC(key, call_list, slot, "i:", "") + +#define ADD_CF_VARIABLE_VALUE(key, firstKey, secondKey) \ + ADD_CF_SLOT_PUBLIC("get_f_" key, call_unknown, rpc::get_variable_f_fn(firstKey, secondKey), "i:", ""); \ + ADD_CF_SLOT("set_f_" key, call_value, rpc::set_variable_f_fn(firstKey, secondKey), "i:i", ""); + +#define ADD_CF_VARIABLE_VALUE_PUBLIC(key, firstKey, secondKey) \ + ADD_CF_SLOT_PUBLIC("get_f_" key, call_unknown, rpc::get_variable_f_fn(firstKey, secondKey), "i:", ""); \ + ADD_CF_SLOT_PUBLIC("set_f_" key, call_value, rpc::set_variable_f_fn(firstKey, secondKey), "i:i", ""); + +#define ADD_CF_VARIABLE_STRING(key, firstKey, secondKey) \ + ADD_CF_SLOT_PUBLIC("get_f_" key, call_unknown, rpc::get_variable_f_fn(firstKey, secondKey), "i:", ""); \ + ADD_CF_SLOT("set_f_" key, call_string, rpc::set_variable_f_fn(firstKey, secondKey), "i:s", ""); + +*/ +#define ADD_CF_VALUE_UNI(key, get) \ + ADD_CF_SLOT_PUBLIC("get_f_" key, call_unknown, rpc::object_void_f_fn(get), "i:", "") + +/* +#define ADD_CF_VALUE_BI(key, set, get) \ + ADD_CF_SLOT_PUBLIC("set_f_" key, call_value, rpc::object_value_f_fn(set), "i:i", "") \ + ADD_CF_SLOT_PUBLIC("get_f_" key, call_unknown, rpc::object_void_f_fn(get), "i:", "") + +#define ADD_CF_VALUE_MEM_BI(key, target, set, get) \ + ADD_CF_VALUE_BI(key, rak::on2(std::mem_fun(target), std::mem_fun(set)), rak::on(std::mem_fun(target), std::mem_fun(get))); + +*/ +#define ADD_CF_STRING_UNI(key, get) \ + ADD_CF_SLOT_PUBLIC("get_f_" key, call_unknown, rpc::object_void_f_fn(get), "s:", "") + +/* +#define ADD_CF_STRING_BI(key, set, get) \ + ADD_CF_SLOT_PUBLIC("set_f_" key, call_string, rpc::object_string_f_fn(set), "i:s", "") \ + ADD_CF_SLOT_PUBLIC("get_f_" key, call_unknown, rpc::object_void_f_fn(get), "s:", "") +*/ + +void +initialize_command_file() { + ADD_CF_VALUE_UNI("is_created", std::mem_fun(&torrent::File::is_created)); + ADD_CF_VALUE_UNI("is_open", std::mem_fun(&torrent::File::is_open)); + + ADD_CF_VALUE_UNI("size_bytes", std::mem_fun(&torrent::File::size_bytes)); + ADD_CF_VALUE_UNI("size_chunks", std::mem_fun(&torrent::File::size_chunks)); + ADD_CF_VALUE_UNI("completed_chunks", std::mem_fun(&torrent::File::completed_chunks)); + + ADD_CF_VALUE_UNI("offset", std::mem_fun(&torrent::File::offset)); + ADD_CF_VALUE_UNI("range_first", std::mem_fun(&torrent::File::range_first)); + ADD_CF_VALUE_UNI("range_second", std::mem_fun(&torrent::File::range_second)); + + // Priority needs to be protected... + ADD_CF_VALUE_UNI("priority", std::mem_fun(&torrent::File::priority)); + + ADD_CF_STRING_UNI("frozen_path", std::mem_fun(&torrent::File::frozen_path)); + ADD_CF_VALUE_UNI("match_depth_prev", std::mem_fun(&torrent::File::match_depth_prev)); + ADD_CF_VALUE_UNI("match_depth_next", std::mem_fun(&torrent::File::match_depth_next)); + + ADD_CF_VALUE_UNI("last_touched", std::mem_fun(&torrent::File::last_touched)); +} diff --git a/src/command_helpers.cc b/src/command_helpers.cc index 372b6386..2355c571 100644 --- a/src/command_helpers.cc +++ b/src/command_helpers.cc @@ -41,6 +41,7 @@ #include "rpc/command_slot.h" #include "rpc/command_variable.h" #include "rpc/command_download_slot.h" +#include "rpc/command_file_slot.h" #include "globals.h" #include "control.h" @@ -52,9 +53,12 @@ rpc::CommandVariable commandVariables[COMMAND_VARIABLES_SIZE]; rpc::CommandVariable* commandVariablesItr = commandVariables; rpc::CommandDownloadSlot commandDownloadSlots[COMMAND_DOWNLOAD_SLOTS_SIZE]; rpc::CommandDownloadSlot* commandDownloadSlotsItr = commandDownloadSlots; +rpc::CommandFileSlot commandFileSlots[COMMAND_FILE_SLOTS_SIZE]; +rpc::CommandFileSlot* commandFileSlotsItr = commandFileSlots; void initialize_command_download(); void initialize_command_events(); +void initialize_command_file(); void initialize_command_local(); void initialize_command_network(); void initialize_command_ui(); @@ -66,13 +70,18 @@ initialize_commands() { initialize_command_local(); initialize_command_ui(); initialize_command_download(); + initialize_command_file(); #ifdef ADDING_COMMANDS if (commandSlotsItr > commandSlots + COMMAND_SLOTS_SIZE || - commandVariablesItr > commandVariables + COMMAND_VARIABLES_SIZE) + commandVariablesItr > commandVariables + COMMAND_VARIABLES_SIZE || + commandDownloadSlotsItr > commandDownloadSlots + COMMAND_DOWNLOAD_SLOTS_SIZE || + commandFileSlotsItr > commandFileSlots + COMMAND_FILE_SLOTS_SIZE) #else if (commandSlotsItr != commandSlots + COMMAND_SLOTS_SIZE || - commandVariablesItr != commandVariables + COMMAND_VARIABLES_SIZE) + commandVariablesItr != commandVariables + COMMAND_VARIABLES_SIZE || + commandDownloadSlotsItr != commandDownloadSlots + COMMAND_DOWNLOAD_SLOTS_SIZE || + commandFileSlotsItr != commandFileSlots + COMMAND_FILE_SLOTS_SIZE) #endif throw torrent::internal_error("initialize_commands() static command array size mismatch."); } diff --git a/src/command_helpers.h b/src/command_helpers.h index afbe1188..df16c010 100644 --- a/src/command_helpers.h +++ b/src/command_helpers.h @@ -43,6 +43,7 @@ namespace rpc { class CommandSlot; class CommandVariable; class CommandDownloadSlot; + class CommandFileSlot; } // By using a static array we avoid allocating the variables on the @@ -50,6 +51,7 @@ namespace rpc { #define COMMAND_SLOTS_SIZE 100 #define COMMAND_VARIABLES_SIZE 100 #define COMMAND_DOWNLOAD_SLOTS_SIZE 100 +#define COMMAND_FILE_SLOTS_SIZE 25 #define ADDING_COMMANDS @@ -59,6 +61,8 @@ extern rpc::CommandVariable commandVariables[COMMAND_VARIABLES_SIZE]; extern rpc::CommandVariable* commandVariablesItr; extern rpc::CommandDownloadSlot commandDownloadSlots[COMMAND_DOWNLOAD_SLOTS_SIZE]; extern rpc::CommandDownloadSlot* commandDownloadSlotsItr; +extern rpc::CommandFileSlot commandFileSlots[COMMAND_FILE_SLOTS_SIZE]; +extern rpc::CommandFileSlot* commandFileSlotsItr; void initialize_commands(); diff --git a/src/command_network.cc b/src/command_network.cc index fdaae4ea..2b1c98de 100644 --- a/src/command_network.cc +++ b/src/command_network.cc @@ -135,10 +135,21 @@ apply_enable_trackers(int64_t arg) { } } +torrent::File* +xmlrpc_find_file(core::Download* download, uint32_t index) { + torrent::FileList* fileList = download->download()->file_list(); + + if (index >= fileList->size_files()) + return NULL; + + return (*fileList)[index]; +} + void initialize_xmlrpc() { rpc::xmlrpc.initialize(); rpc::xmlrpc.set_slot_find_download(rak::mem_fn(control->core()->download_list(), &core::DownloadList::find_hex_ptr)); + rpc::xmlrpc.set_slot_find_file(rak::ptr_fn(&xmlrpc_find_file)); unsigned int count = 0; @@ -147,9 +158,11 @@ initialize_xmlrpc() { continue; if (itr->second.m_genericSlot != NULL) - rpc::xmlrpc.insert_command(itr->first, itr->second.m_parm, itr->second.m_doc, false); + rpc::xmlrpc.insert_command(itr->first, itr->second.m_parm, itr->second.m_doc, rpc::XmlRpc::call_generic); else if (itr->second.m_downloadSlot != NULL) - rpc::xmlrpc.insert_command(itr->first, itr->second.m_parm, itr->second.m_doc, true); + rpc::xmlrpc.insert_command(itr->first, itr->second.m_parm, itr->second.m_doc, rpc::XmlRpc::call_download); + else if (itr->second.m_fileSlot != NULL) + rpc::xmlrpc.insert_command(itr->first, itr->second.m_parm, itr->second.m_doc, rpc::XmlRpc::call_file); else throw torrent::internal_error("XMLRPC: Bad entry."); diff --git a/src/command_ui.cc b/src/command_ui.cc index 060c3c8f..b72cdadd 100644 --- a/src/command_ui.cc +++ b/src/command_ui.cc @@ -114,7 +114,7 @@ apply_print(const torrent::Object& rawArgs) { } case torrent::Object::TYPE_VALUE: default: - current += snprintf(buffer, buffer + 1024 - current, "%lli", itr->as_value()); + current += snprintf(current, buffer + 1024 - current, "%lli", itr->as_value()); break; } } diff --git a/src/rpc/Makefile.am b/src/rpc/Makefile.am index 30702696..62d63146 100644 --- a/src/rpc/Makefile.am +++ b/src/rpc/Makefile.am @@ -4,6 +4,8 @@ libsub_rpc_a_SOURCES = \ command.h \ command_download_slot.cc \ command_download_slot.h \ + command_file_slot.cc \ + command_file_slot.h \ command_map.cc \ command_map.h \ command_scheduler.cc \ diff --git a/src/rpc/command_file_slot.cc b/src/rpc/command_file_slot.cc new file mode 100644 index 00000000..0ab37539 --- /dev/null +++ b/src/rpc/command_file_slot.cc @@ -0,0 +1,123 @@ +// rTorrent - BitTorrent client +// Copyright (C) 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 "parse.h" +#include "command_file_slot.h" + +namespace rpc { + +const torrent::Object +CommandFileSlot::call_unknown(Command* rawCommand, torrent::File* file, const torrent::Object& rawArgs) { + CommandFileSlot* command = static_cast(rawCommand); + + return command->m_slot(file, rawArgs); +} + +const torrent::Object +CommandFileSlot::call_list(Command* rawCommand, torrent::File* file, const torrent::Object& rawArgs) { + CommandFileSlot* command = static_cast(rawCommand); + + switch (rawArgs.type()) { + case torrent::Object::TYPE_LIST: + return command->m_slot(file, rawArgs); + + case torrent::Object::TYPE_VALUE: + case torrent::Object::TYPE_STRING: + { + torrent::Object tmpList(torrent::Object::TYPE_LIST); + tmpList.as_list().push_back(rawArgs); + + return command->m_slot(file, tmpList); + } + default: + throw torrent::input_error("Not a list."); + } +} + +const torrent::Object +CommandFileSlot::call_value_base(Command* rawCommand, torrent::File* file, const torrent::Object& rawArgs, int base, int unit) { + CommandFileSlot* command = static_cast(rawCommand); + + const torrent::Object& arg = convert_to_single_argument(rawArgs); + + switch (arg.type()) { + case torrent::Object::TYPE_VALUE: + // Should shift this one too, so it gives the right unit. + return command->m_slot(file, arg); + + case torrent::Object::TYPE_STRING: + { + torrent::Object argValue(torrent::Object::TYPE_VALUE); + + if (!parse_whole_value_nothrow(arg.as_string().c_str(), &argValue.as_value(), base, unit)) + throw torrent::input_error("Not a value."); + + return command->m_slot(file, argValue); + } + default: + throw torrent::input_error("Not a value."); + } +} + +const torrent::Object +CommandFileSlot::call_string(Command* rawCommand, torrent::File* file, const torrent::Object& rawArgs) { + CommandFileSlot* command = static_cast(rawCommand); + + const torrent::Object& arg = convert_to_single_argument(rawArgs); + + switch (arg.type()) { + case torrent::Object::TYPE_STRING: + return command->m_slot(file, arg); + +// case torrent::Object::TYPE_NONE: +// throw torrent::input_error("CDS: void."); + +// case torrent::Object::TYPE_VALUE: +// throw torrent::input_error("CDS: value."); + +// case torrent::Object::TYPE_LIST: +// throw torrent::input_error("CDS: list."); + + default: + throw torrent::input_error("Not a string."); + } +} + +} diff --git a/src/rpc/command_file_slot.h b/src/rpc/command_file_slot.h new file mode 100644 index 00000000..710df6f3 --- /dev/null +++ b/src/rpc/command_file_slot.h @@ -0,0 +1,176 @@ +// rTorrent - BitTorrent client +// Copyright (C) 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_COMMAND_FILE_SLOT_H +#define RTORRENT_RPC_COMMAND_FILE_SLOT_H + +#include +#include +#include +#include + +#include "command.h" + +namespace torrent { + class File; +} + +namespace rpc { + +class CommandFileSlot : public Command { +public: + typedef rak::function2 slot_type; + + CommandFileSlot() {} + + CommandFileSlot(slot_type::base_type* s) { + m_slot.set(s); + } + + void set_slot(slot_type::base_type* s) { m_slot.set(s); } + + static const torrent::Object call_unknown(Command* rawCommand, torrent::File* file, const torrent::Object& args); + + static const torrent::Object call_list(Command* rawCommand, torrent::File* file, const torrent::Object& args); + static const torrent::Object call_string(Command* rawCommand, torrent::File* file, const torrent::Object& args); + + static const torrent::Object call_value_base(Command* rawCommand, torrent::File* file, const torrent::Object& args, int base, int unit); + + static const torrent::Object call_value(Command* rawCommand, torrent::File* file, const torrent::Object& args) { return call_value_base(rawCommand, file, args, 0, 1); } + static const torrent::Object call_value_kb(Command* rawCommand, torrent::File* file, const torrent::Object& args) { return call_value_base(rawCommand, file, args, 0, 1 << 10); } + + template + static const torrent::Object call_value(Command* rawCommand, torrent::File* file, const torrent::Object& args) { return call_value_base(rawCommand, file, args, base, unit); } + +// static const torrent::Object& get_list(Command* rawCommand, const torrent::Object& args); + +private: + slot_type m_slot; +}; + +// Some slots that convert torrent::Object arguments to proper +// function calls. + +template +class object_void_f_fn_t : public rak::function_base2 { +public: + object_void_f_fn_t(Func func) : m_func(func) {} + + virtual torrent::Object operator () (torrent::File* file, const torrent::Object& arg1) { return torrent::Object(m_func(file)); } + +private: + Func m_func; +}; + +template +class object_void_f_fn_t : public rak::function_base2 { +public: + object_void_f_fn_t(Func func) : m_func(func) {} + + virtual torrent::Object operator () (torrent::File* file, const torrent::Object& arg1) { + m_func(file); + return torrent::Object(); + } + +private: + Func m_func; +}; + +template +class object_value_f_fn1_t : public rak::function_base2 { +public: + object_value_f_fn1_t(Func func) : m_func(func) {} + + virtual torrent::Object operator () (torrent::File* file, const torrent::Object& arg1) { + return torrent::Object((int64_t)m_func(file, arg1.as_value())); + } + +private: + Func m_func; +}; + +template +class object_value_f_fn1_t : public rak::function_base2 { +public: + object_value_f_fn1_t(Func func) : m_func(func) {} + + virtual torrent::Object operator () (torrent::File* file, const torrent::Object& arg1) { + m_func(file, arg1.as_value()); + return torrent::Object(); + } + +private: + Func m_func; +}; + +template +class object_string_f_fn1_t : public rak::function_base2 { +public: + object_string_f_fn1_t(Func func) : m_func(func) {} + + virtual torrent::Object operator () (torrent::File* file, const torrent::Object& arg1) { return torrent::Object(m_func(file, arg1.as_string())); } + +private: + Func m_func; +}; + +template +class object_string_f_fn1_t : public rak::function_base2 { +public: + object_string_f_fn1_t(Func func) : m_func(func) {} + + virtual torrent::Object operator () (torrent::File* file, const torrent::Object& arg1) { + m_func(file, arg1.as_string()); + + return torrent::Object(); + } + +private: + Func m_func; +}; + +template object_void_f_fn_t* +object_f_fn(Return (*func)(torrent::File*)) { + return new object_void_f_fn_t(func); +} + +template object_void_f_fn_t* object_void_f_fn(Func func) { return new object_void_f_fn_t(func); } +template object_value_f_fn1_t* object_value_f_fn(Func func) { return new object_value_f_fn1_t(func); } +template object_string_f_fn1_t* object_string_f_fn(Func func) { return new object_string_f_fn1_t(func); } + +} + +#endif diff --git a/src/rpc/command_map.cc b/src/rpc/command_map.cc index 96bfadbd..a2328960 100644 --- a/src/rpc/command_map.cc +++ b/src/rpc/command_map.cc @@ -60,15 +60,30 @@ CommandMap::~CommandMap() { delete itr->second.m_variable; } -void -CommandMap::insert(key_type key, Command* variable, generic_slot genericSlot, download_slot downloadSlot, int flags, - const char* parm, const char* doc) { +CommandMap::iterator +CommandMap::insert(key_type key, Command* variable, int flags, const char* parm, const char* doc) { iterator itr = base_type::find(key); if (itr != base_type::end()) throw torrent::internal_error("CommandMap::insert(...) tried to insert an already existing key."); - base_type::insert(itr, value_type(key, command_map_data_type(variable, genericSlot, downloadSlot, flags, parm, doc))); + return base_type::insert(itr, value_type(key, command_map_data_type(variable, flags, parm, doc))); +} + +void +CommandMap::insert(key_type key, Command* variable, generic_slot genericSlot, download_slot downloadSlot, int flags, + const char* parm, const char* doc) { + iterator itr = insert(key, variable, flags, parm, doc); + + itr->second.m_genericSlot = genericSlot; + itr->second.m_downloadSlot = downloadSlot; +} + +void +CommandMap::insert_file(key_type key, Command* variable, file_slot fileSlot, int flags, const char* parm, const char* doc) { + iterator itr = insert(key, variable, flags, parm, doc); + + itr->second.m_fileSlot = fileSlot; } void @@ -78,10 +93,18 @@ CommandMap::insert(key_type key, const command_map_data_type src) { if (itr != base_type::end()) throw torrent::internal_error("CommandMap::insert(...) tried to insert an already existing key."); - base_type::insert(itr, value_type(key, command_map_data_type(src.m_variable, src.m_genericSlot, src.m_downloadSlot, - src.m_flags | flag_dont_delete, src.m_parm, src.m_doc))); + itr = base_type::insert(itr, value_type(key, command_map_data_type(src.m_variable, src.m_flags | flag_dont_delete, src.m_parm, src.m_doc))); + + itr->second.m_genericSlot = src.m_genericSlot; + itr->second.m_downloadSlot = src.m_downloadSlot; + itr->second.m_fileSlot = src.m_fileSlot; } +// These should really be handled by a single function. An type enum +// passed with the object would work, and the switch would probably be +// optimized away as the type difference is not reflected in the +// member function pointer call. + const CommandMap::mapped_type CommandMap::call_command(key_type key, const mapped_type& arg) { const_iterator itr = base_type::find(key); @@ -112,4 +135,21 @@ CommandMap::call_command_d(key_type key, core::Download* download, const mapped_ return itr->second.m_downloadSlot(itr->second.m_variable, download, arg); } +const CommandMap::mapped_type +CommandMap::call_command_f(key_type key, torrent::File* file, const mapped_type& arg) { + const_iterator itr = base_type::find(key); + + if (itr == base_type::end()) + throw torrent::input_error("Command \"" + std::string(key) + "\" does not exist."); + + if (itr->second.m_fileSlot == NULL || file == NULL) { + if (itr->second.m_genericSlot == NULL) + throw torrent::input_error("Command does not have a generic slot."); + + return itr->second.m_genericSlot(itr->second.m_variable, arg); + } + + return itr->second.m_fileSlot(itr->second.m_variable, file, arg); +} + } diff --git a/src/rpc/command_map.h b/src/rpc/command_map.h index 6bb68e68..1746de0b 100644 --- a/src/rpc/command_map.h +++ b/src/rpc/command_map.h @@ -46,6 +46,10 @@ namespace core { class Download; } +namespace torrent { + class File; +} + namespace rpc { class Command; @@ -60,14 +64,19 @@ struct command_map_data_type { // will register a member function pointer to be used instead. typedef const torrent::Object (*generic_slot)(Command*, const torrent::Object&); typedef const torrent::Object (*download_slot)(Command*, core::Download*, const torrent::Object&); + typedef const torrent::Object (*file_slot)(Command*, torrent::File*, const torrent::Object&); - command_map_data_type(Command* variable, generic_slot genericSlot, download_slot downloadSlot, int flags, - const char* parm, const char* doc) : - m_variable(variable), m_genericSlot(genericSlot), m_downloadSlot(downloadSlot), m_flags(flags), m_parm(parm), m_doc(doc) {} + command_map_data_type(Command* variable, int flags, const char* parm, const char* doc) : + m_variable(variable), m_genericSlot(NULL), m_downloadSlot(NULL), m_fileSlot(NULL), + m_flags(flags), m_parm(parm), m_doc(doc) {} Command* m_variable; + + // Should make this into a union and pass a type id when calling + // commands, making it all use the same generic interface. generic_slot m_genericSlot; download_slot m_downloadSlot; + file_slot m_fileSlot; int m_flags; @@ -81,6 +90,7 @@ public: typedef command_map_data_type::generic_slot generic_slot; typedef command_map_data_type::download_slot download_slot; + typedef command_map_data_type::file_slot file_slot; typedef torrent::Object mapped_type; typedef mapped_type::value_type mapped_value_type; @@ -102,27 +112,18 @@ public: bool has(const char* key) const { return base_type::find(key) != base_type::end(); } bool has(const std::string& key) const { return has(key.c_str()); } + iterator insert(key_type key, Command* variable, int flags, const char* parm, const char* doc); + void insert(key_type key, Command* variable, generic_slot genericSlot, download_slot downloadSlot, int flags, const char* parm, const char* doc); + void insert_file(key_type key, Command* variable, file_slot fileSlot, int flags, const char* parm, const char* doc); + void insert(key_type key, const command_map_data_type src); const mapped_type call_command(key_type key, const mapped_type& arg); -// const mapped_type call_command_void(key_type key) { return call_command(key, torrent::Object()); } -// const std::string call_command_string(key_type key) { return call_command(key, torrent::Object()).as_string(); } -// mapped_value_type call_command_value(key_type key) { return call_command(key, torrent::Object()).as_value(); } - -// void call_command_set_string(key_type key, const std::string& arg) { call_command(key, mapped_type(arg)); } -// void call_command_set_std_string(const std::string& key, const std::string& arg) { call_command(key.c_str(), mapped_type(arg)); } - const mapped_type call_command_d(key_type key, core::Download* download, const mapped_type& arg); -// const mapped_type call_command_d_void(key_type key, core::Download* download) { return call_command_d(key, download, torrent::Object()); } -// const std::string call_command_d_string(key_type key, core::Download* download) { return call_command_d(key, download, torrent::Object()).as_string(); } -// mapped_value_type call_command_d_value(key_type key, core::Download* download) { return call_command_d(key, download, torrent::Object()).as_value(); } - -// void call_command_d_set_value(key_type key, core::Download* download, mapped_value_type arg) { call_command_d(key, download, mapped_type(arg)); } -// void call_command_d_set_string(key_type key, core::Download* download, const std::string& arg) { call_command_d(key, download, mapped_type(arg)); } -// void call_command_d_set_std_string(const std::string& key, core::Download* download, const std::string& arg) { call_command_d(key.c_str(), download, mapped_type(arg)); } + const mapped_type call_command_f(key_type key, torrent::File* file, const mapped_type& arg); private: CommandMap(const CommandMap&); diff --git a/src/rpc/exec_file.cc b/src/rpc/exec_file.cc index 34897c17..6b4bd98a 100644 --- a/src/rpc/exec_file.cc +++ b/src/rpc/exec_file.cc @@ -37,6 +37,8 @@ #include "config.h" #include +#include +#include #include "exec_file.h" diff --git a/src/rpc/parse_commands.h b/src/rpc/parse_commands.h index 071d6911..f2feaf68 100644 --- a/src/rpc/parse_commands.h +++ b/src/rpc/parse_commands.h @@ -100,6 +100,8 @@ inline void call_command_d_set_value(const char* key, core::Download* inline void call_command_d_set_string(const char* key, core::Download* download, const std::string& arg) { commands.call_command_d(key, download, torrent::Object(arg)); } inline void call_command_d_set_std_string(const std::string& key, core::Download* download, const std::string& arg) { commands.call_command_d(key.c_str(), download, torrent::Object(arg)); } +inline torrent::Object call_command_f(const char* key, torrent::File* file, const torrent::Object& obj) { return commands.call_command_f(key, file, obj); } + inline torrent::Object call_command_d_range(const char* key, core::Download* download, torrent::Object::list_type::const_iterator first, torrent::Object::list_type::const_iterator last) { torrent::Object rawArgs(torrent::Object::TYPE_LIST); diff --git a/src/rpc/xmlrpc.cc b/src/rpc/xmlrpc.cc index 1bb8c2e0..810df645 100644 --- a/src/rpc/xmlrpc.cc +++ b/src/rpc/xmlrpc.cc @@ -51,11 +51,8 @@ namespace rpc { #ifdef HAVE_XMLRPC_C -xmlrpc_value* xmlrpc_call_command(xmlrpc_env* env, xmlrpc_value* args, void* voidServerInfo); -xmlrpc_value* xmlrpc_call_command_d(xmlrpc_env* env, xmlrpc_value* args, void* voidServerInfo); - -core::Download* xmlrpc_to_download(xmlrpc_env* env, xmlrpc_value* value); -torrent::Object xmlrpc_to_object_d(xmlrpc_env* env, xmlrpc_value* value, core::Download** download); +// xmlrpc_value* xmlrpc_call_command(xmlrpc_env* env, xmlrpc_value* args, void* voidServerInfo); +// xmlrpc_value* xmlrpc_call_command_d(xmlrpc_env* env, xmlrpc_value* args, void* voidServerInfo); torrent::Object xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value) { @@ -136,19 +133,21 @@ xmlrpc_to_download(xmlrpc_env* env, xmlrpc_value* value) { switch (xmlrpc_value_type(value)) { case XMLRPC_TYPE_STRING: - const char* valueString; - xmlrpc_read_string(env, value, &valueString); + { + const char* str; + xmlrpc_read_string(env, value, &str); if (env->fault_occurred) return NULL; - if (std::strlen(valueString) != 40 || - (download = xmlrpc.get_slot_find_download()(valueString)) == NULL) + if (std::strlen(str) != 40 || + (download = xmlrpc.get_slot_find_download()(str)) == NULL) xmlrpc_env_set_fault(env, XMLRPC_TYPE_ERROR, "Could not find info-hash."); // Urgh, seriously? - ::free((void*)valueString); + ::free((void*)str); return download; + } default: xmlrpc_env_set_fault(env, XMLRPC_TYPE_ERROR, "Unsupported type found."); @@ -156,51 +155,131 @@ xmlrpc_to_download(xmlrpc_env* env, xmlrpc_value* value) { } } +torrent::File* +xmlrpc_to_file(xmlrpc_env* env, xmlrpc_value* value, core::Download* download) { + int index; + + switch (xmlrpc_value_type(value)) { + case XMLRPC_TYPE_INT: + xmlrpc_read_int(env, value, &index); + break; + +#ifdef XMLRPC_HAVE_I8 + case XMLRPC_TYPE_I8: + long long v2; + xmlrpc_read_i8(env, value, &v2); + + index = v2; + break; +#endif + + case XMLRPC_TYPE_STRING: + { + const char* str; + xmlrpc_read_string(env, value, &str); + + if (env->fault_occurred) + return NULL; + + const char* end = str; + index = ::strtol(str, (char**)&end, 0); + + ::free((void*)str); + + if (*str == '\0' || *end != '\0') { + xmlrpc_env_set_fault(env, XMLRPC_TYPE_ERROR, "Invalid file index."); + return NULL; + } + + break; + } + + default: + xmlrpc_env_set_fault(env, XMLRPC_TYPE_ERROR, "Unsupported type found."); + return NULL; + } + + if (env->fault_occurred) + return NULL; + + torrent::File* file = xmlrpc.get_slot_find_file()(download, index); + + if (file == NULL) + xmlrpc_env_set_fault(env, XMLRPC_TYPE_ERROR, "Invalid file index."); + + return file; +} + // This should really be cleaned up and support for an array of // downloads should be added. torrent::Object -xmlrpc_to_object_d(xmlrpc_env* env, xmlrpc_value* value, core::Download** download) { +xmlrpc_to_object_target(xmlrpc_env* env, xmlrpc_value* value, int callType, void** target) { switch (xmlrpc_value_type(value)) { case XMLRPC_TYPE_STRING: - *download = xmlrpc_to_download(env, value); + if (callType != XmlRpc::call_download) { + xmlrpc_env_set_fault(env, XMLRPC_TYPE_ERROR, "Unsupported type found."); + break; + } + + *target = xmlrpc_to_download(env, value); break; case XMLRPC_TYPE_ARRAY: { + unsigned int current = 0; unsigned int last = xmlrpc_array_size(env, value); if (env->fault_occurred) break; if (last < 1) { - xmlrpc_env_set_fault(env, XMLRPC_TYPE_ERROR, "Unsupported type found."); + xmlrpc_env_set_fault(env, XMLRPC_TYPE_ERROR, "Too few arguments."); break; } - // Need to decref. - xmlrpc_value* tmpDownload; - xmlrpc_array_read_item(env, value, 0, &tmpDownload); + { + xmlrpc_value* tmp; + xmlrpc_array_read_item(env, value, current++, &tmp); - if (env->fault_occurred) - break; + if (env->fault_occurred) + break; - *download = xmlrpc_to_download(env, tmpDownload); - xmlrpc_DECREF(tmpDownload); + *target = xmlrpc_to_download(env, tmp); + xmlrpc_DECREF(tmp); - if (env->fault_occurred) - break; + if (env->fault_occurred) + break; + } + + if (callType == XmlRpc::call_file) { + if (current == last) { + xmlrpc_env_set_fault(env, XMLRPC_TYPE_ERROR, "Too few arguments."); + break; + } + + xmlrpc_value* tmp; + xmlrpc_array_read_item(env, value, current++, &tmp); + + if (env->fault_occurred) + break; + + *target = xmlrpc_to_file(env, tmp, (core::Download*)*target); + xmlrpc_DECREF(tmp); + + if (env->fault_occurred) + break; + } torrent::Object result; - if (last > 2) { + if (current + 1 > last) { result = torrent::Object(torrent::Object::TYPE_LIST); torrent::Object::list_type& listRef = result.as_list(); - // Move this into a helper function. - for (unsigned int i = 1; i != last; i++) { - // Need to decref. + // Move this into a helper function? + while (current != last) { xmlrpc_value* tmp; - xmlrpc_array_read_item(env, value, i, &tmp); + xmlrpc_array_read_item(env, value, current++, &tmp); if (env->fault_occurred) break; @@ -211,10 +290,10 @@ xmlrpc_to_object_d(xmlrpc_env* env, xmlrpc_value* value, core::Download** downlo return result; - } else if (last == 2) { + } else if (current + 1 == last) { // Need to decref. xmlrpc_value* tmp; - xmlrpc_array_read_item(env, value, 1, &tmp); + xmlrpc_array_read_item(env, value, current, &tmp); if (env->fault_occurred) break; @@ -292,7 +371,7 @@ xmlrpc_call_command(xmlrpc_env* env, xmlrpc_value* args, void* voidServerInfo) { xmlrpc_value* xmlrpc_call_command_d(xmlrpc_env* env, xmlrpc_value* args, void* voidServerInfo) { core::Download* download = NULL; - torrent::Object object = xmlrpc_to_object_d(env, args, &download); + torrent::Object object = xmlrpc_to_object_target(env, args, XmlRpc::call_download, (void**)&download); if (env->fault_occurred) return NULL; @@ -306,6 +385,23 @@ xmlrpc_call_command_d(xmlrpc_env* env, xmlrpc_value* args, void* voidServerInfo) } } +xmlrpc_value* +xmlrpc_call_command_f(xmlrpc_env* env, xmlrpc_value* args, void* voidServerInfo) { + torrent::File* file = NULL; + torrent::Object object = xmlrpc_to_object_target(env, args, XmlRpc::call_file, (void**)&file); + + if (env->fault_occurred) + return NULL; + + try { + return object_to_xmlrpc(env, rpc::call_command_f((const char*)voidServerInfo, file, object)); + + } catch (torrent::local_error& e) { + xmlrpc_env_set_fault(env, XMLRPC_PARSE_ERROR, e.what()); + return NULL; + } +} + void XmlRpc::initialize() { #ifndef XMLRPC_HAVE_I8 @@ -344,13 +440,20 @@ XmlRpc::process(const char* inBuffer, uint32_t length, slot_write slotWrite) { } void -XmlRpc::insert_command(const char* name, const char* parm, const char* doc, bool onDownload) { +XmlRpc::insert_command(const char* name, const char* parm, const char* doc, int call) { xmlrpc_env localEnv; xmlrpc_env_init(&localEnv); + xmlrpc_value* (*callSlot)(xmlrpc_env*, xmlrpc_value*, void*); + + switch (call) { + case call_download: callSlot = &xmlrpc_call_command_d; break; + case call_file: callSlot = &xmlrpc_call_command_f; break; + default: callSlot = &xmlrpc_call_command; break; + } + xmlrpc_registry_add_method_w_doc(&localEnv, (xmlrpc_registry*)m_registry, NULL, name, - onDownload ? &xmlrpc_call_command_d : &xmlrpc_call_command, - const_cast(name), parm, doc); + callSlot, const_cast(name), parm, doc); if (localEnv.fault_occurred) throw torrent::internal_error("Fault occured while inserting xmlrpc call."); diff --git a/src/rpc/xmlrpc.h b/src/rpc/xmlrpc.h index 7d2ba569..00adebb5 100644 --- a/src/rpc/xmlrpc.h +++ b/src/rpc/xmlrpc.h @@ -44,6 +44,7 @@ namespace core { } namespace torrent { + class File; class Object; } @@ -51,13 +52,18 @@ namespace rpc { class XmlRpc { public: - typedef rak::function1 slot_find_download; - typedef rak::function2 slot_write; + typedef rak::function1 slot_find_download; + typedef rak::function2 slot_find_file; + typedef rak::function2 slot_write; static const int dialect_generic = 0; static const int dialect_i8 = 1; static const int dialect_apache = 2; + static const int call_generic = 0; + static const int call_download = 1; + static const int call_file = 2; + XmlRpc() : m_env(NULL), m_registry(NULL), m_dialect(dialect_i8) {} bool is_valid() const { return m_env != NULL; } @@ -67,7 +73,7 @@ public: bool process(const char* inBuffer, uint32_t length, slot_write slotWrite); - void insert_command(const char* name, const char* parm, const char* doc, bool onDownload); + void insert_command(const char* name, const char* parm, const char* doc, int call); int dialect() { return m_dialect; } void set_dialect(int dialect); @@ -75,6 +81,9 @@ public: slot_find_download& get_slot_find_download() { return m_slotFindDownload; } void set_slot_find_download(slot_find_download::base_type* slot) { m_slotFindDownload.set(slot); } + slot_find_file& get_slot_find_file() { return m_slotFindFile; } + void set_slot_find_file(slot_find_file::base_type* slot) { m_slotFindFile.set(slot); } + private: void* m_env; void* m_registry; @@ -82,6 +91,7 @@ private: int m_dialect; slot_find_download m_slotFindDownload; + slot_find_file m_slotFindFile; }; }