From 7600a5103949c5a2d590db0ff859dbf6a4e191b5 Mon Sep 17 00:00:00 2001 From: rakshasa Date: Sun, 16 Nov 2008 07:54:45 +0000 Subject: [PATCH] * Fix potential buffer overflows in case snprintf buffer is too small. Patch by Josef Drexler. * Added 'view.persistance' command that makes downloads inserted into that view persist across sessions. Only call on user-created views. * Added 'ratio.*' commands that call the 'group.seeding.ratio.*' equivalents. git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@1080 e378c898-3ddf-0310-93e7-cc216c733640 --- src/Makefile.am | 1 + src/command_download.cc | 57 ++++++++++++++++++++++++ src/command_events.cc | 4 +- src/command_helpers.cc | 2 + src/command_helpers.h | 6 ++- src/command_object.cc | 50 +++++++++++++++++++++ src/command_ui.cc | 17 +++++++ src/core/download_factory.cc | 1 + src/core/view.h | 3 ++ src/display/text_element_string.cc | 2 +- src/display/text_element_value.cc | 22 ++++----- src/display/window_download_statusbar.cc | 4 +- src/main.cc | 17 +++---- 13 files changed, 161 insertions(+), 25 deletions(-) create mode 100644 src/command_object.cc diff --git a/src/Makefile.am b/src/Makefile.am index 02c543ae..1936c7d8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,6 +25,7 @@ rtorrent_SOURCES = \ command_helpers.h \ command_local.cc \ command_network.cc \ + command_object.cc \ command_peer.cc \ command_tracker.cc \ command_scheduler.cc \ diff --git a/src/command_download.cc b/src/command_download.cc index 7d7ccbdf..c55b7bac 100644 --- a/src/command_download.cc +++ b/src/command_download.cc @@ -384,6 +384,57 @@ p_multicall(core::Download* download, const torrent::Object& rawArgs) { return resultRaw; } +inline torrent::Object& +d_object_wrapper(const std::pair keyPair, core::Download* download) { + if (keyPair.first == NULL) + return download->bencode()->get_key(keyPair.second); + else + return download->bencode()->get_key(keyPair.first).get_key(keyPair.second); +} + +torrent::Object +d_object_get(const std::pair keyPair, core::Download* download, __UNUSED const torrent::Object& rawArgs) { + return d_object_wrapper(keyPair, download); +} + +torrent::Object +d_list_push_back(const std::pair keyPair, core::Download* download, const torrent::Object& rawArgs) { + d_object_wrapper(keyPair, download).as_list().push_back(rawArgs); + + return torrent::Object(); +} + +torrent::Object +d_list_push_back_unique(const std::pair keyPair, core::Download* download, const torrent::Object& rawArgs) { + const torrent::Object& args = (rawArgs.is_list() && !rawArgs.as_list().empty()) ? rawArgs.as_list().front() : rawArgs; + torrent::Object::list_type& list = d_object_wrapper(keyPair, download).as_list(); + + if (std::find_if(list.begin(), list.end(), + rak::bind1st(std::ptr_fun(&torrent::object_equal), args)) == list.end()) + list.push_back(rawArgs); + + return torrent::Object(); +} + +torrent::Object +d_list_has(const std::pair keyPair, core::Download* download, const torrent::Object& rawArgs) { + const torrent::Object& args = (rawArgs.is_list() && !rawArgs.as_list().empty()) ? rawArgs.as_list().front() : rawArgs; + torrent::Object::list_type& list = d_object_wrapper(keyPair, download).as_list(); + + return (int64_t)(std::find_if(list.begin(), list.end(), + rak::bind1st(std::ptr_fun(&torrent::object_equal), args)) != list.end()); +} + +torrent::Object +d_list_remove(const std::pair keyPair, core::Download* download, const torrent::Object& rawArgs) { + const torrent::Object& args = (rawArgs.is_list() && !rawArgs.as_list().empty()) ? rawArgs.as_list().front() : rawArgs; + torrent::Object::list_type& list = d_object_wrapper(keyPair, download).as_list(); + + list.erase(std::remove_if(list.begin(), list.end(), rak::bind1st(std::ptr_fun(&torrent::object_equal), args)), list.end()); + + return torrent::Object(); +} + #define ADD_CD_SLOT(key, function, slot, parm, doc) \ commandDownloadSlotsItr->set_slot(slot); \ rpc::commands.insert_type(key, commandDownloadSlotsItr++, &rpc::CommandSlot::function, rpc::CommandMap::flag_dont_delete, parm, doc); @@ -547,6 +598,12 @@ initialize_command_download() { ADD_CD_VALUE_BI("hashing_failed", std::mem_fun(&core::Download::set_hash_failed), std::mem_fun(&core::Download::is_hash_failed)); + CMD_D("d.views", rak::bind_ptr_fn(&d_object_get, std::make_pair("rtorrent", "views"))); + CMD_D("d.views.has", rak::bind_ptr_fn(&d_list_has, std::make_pair("rtorrent", "views"))); + CMD_D("d.views.remove", rak::bind_ptr_fn(&d_list_remove, std::make_pair("rtorrent", "views"))); + CMD_D("d.views.push_back", rak::bind_ptr_fn(&d_list_push_back, std::make_pair("rtorrent", "views"))); + CMD_D("d.views.push_back_unique", rak::bind_ptr_fn(&d_list_push_back_unique, std::make_pair("rtorrent", "views"))); + // This command really needs to be improved, so we have proper // logging support. ADD_CD_STRING_BI("message", std::mem_fun(&core::Download::set_message), std::mem_fun(&core::Download::message)); diff --git a/src/command_events.cc b/src/command_events.cc index 164be667..c3974fc6 100644 --- a/src/command_events.cc +++ b/src/command_events.cc @@ -122,7 +122,7 @@ apply_on_ratio(const torrent::Object& rawArgs) { if (!(*itr)->is_seeding() || rpc::call_command_value("d.get_ignore_commands", rpc::make_target(*itr)) != 0) continue; - rpc::parse_command_single(rpc::make_target(*itr), "print={Checked ratio of download.}"); + // rpc::parse_command_single(rpc::make_target(*itr), "print={Checked ratio of download.}"); int64_t totalDone = (*itr)->download()->bytes_done(); int64_t totalUpload = (*itr)->download()->up_rate()->total(); @@ -137,7 +137,7 @@ apply_on_ratio(const torrent::Object& rawArgs) { std::strcpy(bufferStart, "command"); for (std::vector::iterator itr = downloads.begin(), last = downloads.end(); itr != last; itr++) { - rpc::commands.call("print", rpc::make_target(*itr), "Calling ratio command."); + // rpc::commands.call("print", rpc::make_target(*itr), "Calling ratio command."); rpc::commands.call_catch(buffer, rpc::make_target(*itr), torrent::Object(), "Ratio reached, but command failed: "); } diff --git a/src/command_helpers.cc b/src/command_helpers.cc index 65610905..adbb93d1 100644 --- a/src/command_helpers.cc +++ b/src/command_helpers.cc @@ -64,6 +64,7 @@ rpc::CommandSlot* commandTrackerSlotsItr = commandTr rpc::CommandSlot commandAnySlots[COMMAND_ANY_SLOTS_SIZE]; rpc::CommandSlot* commandAnySlotsItr = commandAnySlots; +void initialize_command_object(); void initialize_command_dynamic(); void initialize_command_download(); void initialize_command_events(); @@ -77,6 +78,7 @@ void initialize_command_ui(); void initialize_commands() { + initialize_command_object(); initialize_command_dynamic(); initialize_command_events(); initialize_command_network(); diff --git a/src/command_helpers.h b/src/command_helpers.h index f1419eee..a807b5f7 100644 --- a/src/command_helpers.h +++ b/src/command_helpers.h @@ -213,7 +213,11 @@ add_variable(key, NULL, NULL, &rpc::CommandVariable::get_string, NULL, std::stri #define CMD_D_SLOT(key, function, slot, parm, doc) \ commandDownloadSlotsItr->set_slot(slot); \ - rpc::commands.insert_type(key, commandDownloadSlotsItr++, &rpc::CommandSlot::function, rpc::CommandMap::flag_dont_delete | rpc::CommandMap::flag_public_xmlrpc, parm, doc); + rpc::commands.insert_type(key, commandDownloadSlotsItr++, &rpc::CommandSlot::function, \ + rpc::CommandMap::flag_dont_delete | rpc::CommandMap::flag_public_xmlrpc, parm, doc); + +#define CMD_D(key, slot) \ + CMD_D_SLOT(key, call_unknown, slot, "i:", "") #define CMD_D_ANY(key, slot) \ CMD_D_SLOT(key, call_unknown, slot, "i:", "") diff --git a/src/command_object.cc b/src/command_object.cc new file mode 100644 index 00000000..06d8335e --- /dev/null +++ b/src/command_object.cc @@ -0,0 +1,50 @@ +// rTorrent - BitTorrent client +// Copyright (C) 2005-2007, 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 "globals.h" +#include "control.h" +#include "command_helpers.h" +#include "rpc/command_variable.h" + +void +initialize_command_object() { +// CMD_N ("system.method.insert", rak::ptr_fn(&system_method_insert)); +// CMD_N_STRING("system.method.erase", rak::ptr_fn(&system_method_erase)); +} diff --git a/src/command_ui.cc b/src/command_ui.cc index cf1f232f..fa4653ec 100644 --- a/src/command_ui.cc +++ b/src/command_ui.cc @@ -394,6 +394,21 @@ cmd_view_size_not_visible(__UNUSED rpc::target_type target, const torrent::Objec return (*control->view_manager()->find_throw(rawArgs.as_string()))->size_not_visible(); } +torrent::Object +cmd_view_persistent(__UNUSED rpc::target_type target, const torrent::Object& rawArgs) { + const std::string& args = rawArgs.as_string(); + core::View* view = *control->view_manager()->find_throw(args); + + if (!view->get_filter().empty() || !view->get_event_added().empty() || !view->get_event_removed().empty()) + throw torrent::input_error("Cannot set modified views as persitent."); + + view->set_filter("d.views.has=" + args); + view->set_event_added("d.views.push_back_unique=" + args); + view->set_event_removed("d.views.remove=" + args); + + return torrent::Object(); +} + torrent::Object cmd_ui_set_view(__UNUSED rpc::target_type target, const torrent::Object& rawArgs) { control->ui()->download_list()->set_current_view(rawArgs.as_string()); @@ -451,6 +466,8 @@ initialize_command_ui() { CMD_G_STRING("view.size", rak::ptr_fn(&cmd_view_size)); CMD_G_STRING("view.size_not_visible", rak::ptr_fn(&cmd_view_size_not_visible)); + CMD_G_STRING("view.persistent", rak::ptr_fn(&cmd_view_persistent)); + CMD_D_STRING("view.filter_download", rak::ptr_fn(&cmd_view_filter_download)); CMD_D_STRING("view.set_visible", rak::ptr_fn(&cmd_view_set_visible)); CMD_D_STRING("view.set_not_visible", rak::ptr_fn(&cmd_view_set_not_visible)); diff --git a/src/core/download_factory.cc b/src/core/download_factory.cc index 78e92b41..25590882 100644 --- a/src/core/download_factory.cc +++ b/src/core/download_factory.cc @@ -337,6 +337,7 @@ DownloadFactory::initialize_rtorrent(Download* download, torrent::Object* rtorre download->download()->file_list()->size_chunks())); rtorrent->insert_preserve_copy("ignore_commands", (int64_t)0); + rtorrent->insert_preserve_copy("views", torrent::Object::create_list()); rtorrent->insert_preserve_type("connection_leech", m_variables["connection_leech"]); rtorrent->insert_preserve_type("connection_seed", m_variables["connection_seed"]); diff --git a/src/core/view.h b/src/core/view.h index 8f2540e5..f0bbde35 100644 --- a/src/core/view.h +++ b/src/core/view.h @@ -122,11 +122,14 @@ public: void filter(); void filter_download(core::Download* download); + const std::string& get_filter() const { return m_filter; } void set_filter(const std::string& s) { m_filter = s; } void set_filter_on_event(const std::string& event); void clear_filter_on(); + const std::string& get_event_added() const { return m_eventAdded; } + const std::string& get_event_removed() const { return m_eventRemoved; } void set_event_added(const std::string& cmd) { m_eventAdded = cmd; } void set_event_removed(const std::string& cmd) { m_eventRemoved = cmd; } diff --git a/src/display/text_element_string.cc b/src/display/text_element_string.cc index acdf6e79..11c8f6aa 100644 --- a/src/display/text_element_string.cc +++ b/src/display/text_element_string.cc @@ -122,7 +122,7 @@ TextElementCommand::print(char* first, char* last, Canvas::attributes_list* attr } case torrent::Object::TYPE_VALUE: { - first += std::max(snprintf(first, last - first + 1, "%lld", result.as_value()), 0); + first += std::min(std::max(snprintf(first, last - first + 1, "%lld", result.as_value()), 0), last - first + 1); break; } default: diff --git a/src/display/text_element_value.cc b/src/display/text_element_value.cc index ae5efa39..380022d7 100644 --- a/src/display/text_element_value.cc +++ b/src/display/text_element_value.cc @@ -82,28 +82,28 @@ TextElementValueBase::print(char* first, char* last, Canvas::attributes_list* at } else if (m_flags & flag_kb) { // Just use a default width of 5 for now. - first += std::max(snprintf(first, last - first + 1, "%5.1f", (double)val / (1 << 10)), 0); + first += std::min(std::max(snprintf(first, last - first + 1, "%5.1f", (double)val / (1 << 10)), 0), last - first + 1); } else if (m_flags & flag_mb) { // Just use a default width of 8 for now. - first += std::max(snprintf(first, last - first + 1, "%8.1f", (double)val / (1 << 20)), 0); + first += std::min(std::max(snprintf(first, last - first + 1, "%8.1f", (double)val / (1 << 20)), 0), last - first + 1); } else if (m_flags & flag_xb) { if (val < (int64_t(1000) << 10)) - first += std::max(snprintf(first, last - first + 1, "%5.1f KB", (double)val / (int64_t(1) << 10)), 0); + first += std::min(std::max(snprintf(first, last - first + 1, "%5.1f KB", (double)val / (int64_t(1) << 10)), 0), last - first + 1); else if (val < (int64_t(1000) << 20)) - first += std::max(snprintf(first, last - first + 1, "%5.1f MB", (double)val / (int64_t(1) << 20)), 0); + first += std::min(std::max(snprintf(first, last - first + 1, "%5.1f MB", (double)val / (int64_t(1) << 20)), 0), last - first + 1); else if (val < (int64_t(1000) << 30)) - first += std::max(snprintf(first, last - first + 1, "%5.1f GB", (double)val / (int64_t(1) << 30)), 0); + first += std::min(std::max(snprintf(first, last - first + 1, "%5.1f GB", (double)val / (int64_t(1) << 30)), 0), last - first + 1); else - first += std::max(snprintf(first, last - first + 1, "%5.1f TB", (double)val / (int64_t(1) << 40)), 0); + first += std::min(std::max(snprintf(first, last - first + 1, "%5.1f TB", (double)val / (int64_t(1) << 40)), 0), last - first + 1); } else if (m_flags & flag_timer) { if (val == 0) - first += std::max(snprintf(first, last - first + 1, "--:--:--"), 0); + first += std::min(std::max(snprintf(first, last - first + 1, "--:--:--"), 0), last - first + 1); else - first += std::max(snprintf(first, last - first + 1, "%2d:%02d:%02d", (int)(val / 3600), (int)((val / 60) % 60), (int)(val % 60)), 0); + first += std::min(std::max(snprintf(first, last - first + 1, "%2d:%02d:%02d", (int)(val / 3600), (int)((val / 60) % 60), (int)(val % 60)), 0), last - first + 1); } else if (m_flags & flag_date) { time_t t = val; @@ -112,7 +112,7 @@ TextElementValueBase::print(char* first, char* last, Canvas::attributes_list* at if (u == NULL) return first; - first += std::max(snprintf(first, last - first + 1, "%02u/%02u/%04u", u->tm_mday, (u->tm_mon + 1), (1900 + u->tm_year)), 0); + first += std::min(std::max(snprintf(first, last - first + 1, "%02u/%02u/%04u", u->tm_mday, (u->tm_mon + 1), (1900 + u->tm_year)), 0), last - first + 1);; } else if (m_flags & flag_time) { time_t t = val; @@ -121,10 +121,10 @@ TextElementValueBase::print(char* first, char* last, Canvas::attributes_list* at if (u == NULL) return first; - first += std::max(snprintf(first, last - first + 1, "%2d:%02d:%02d", u->tm_hour, u->tm_min, u->tm_sec), 0); + first += std::min(std::max(snprintf(first, last - first + 1, "%2d:%02d:%02d", u->tm_hour, u->tm_min, u->tm_sec), 0), last - first + 1); } else { - first += std::max(snprintf(first, last - first + 1, "%lld", val), 0); + first += std::min(std::max(snprintf(first, last - first + 1, "%lld", val), 0), last - first + 1); } push_attribute(attributes, Attributes(first, baseAttribute)); diff --git a/src/display/window_download_statusbar.cc b/src/display/window_download_statusbar.cc index 498b8522..cf5a4784 100644 --- a/src/display/window_download_statusbar.cc +++ b/src/display/window_download_statusbar.cc @@ -68,7 +68,7 @@ WindowDownloadStatusbar::redraw() { position = print_download_info(buffer, last, m_download); m_canvas->print(0, 0, "%s", buffer); - position = buffer + std::max(snprintf(buffer, last - buffer, "Peers: %i(%i) Min/Max: %i/%i Uploads: %i U/I/C/A: %i/%i/%i/%i Failed: %i", + position = buffer + std::min(std::max(snprintf(buffer, last - buffer, "Peers: %i(%i) Min/Max: %i/%i Uploads: %i U/I/C/A: %i/%i/%i/%i Failed: %i", (int)m_download->download()->connection_list()->size(), (int)m_download->download()->peer_list()->available_list_size(), (int)m_download->download()->connection_list()->min_size(), @@ -79,7 +79,7 @@ WindowDownloadStatusbar::redraw() { (int)m_download->download()->peers_complete(), (int)m_download->download()->peers_accounted(), (int)m_download->chunks_failed()), - 0); + 0), last - buffer); m_canvas->print(0, 1, "%s", buffer); diff --git a/src/main.cc b/src/main.cc index 53566108..1867fc15 100644 --- a/src/main.cc +++ b/src/main.cc @@ -212,14 +212,15 @@ main(int argc, char** argv) { "group.insert = seeding,seeding\n" -// "system.method.insert = group.seeding.view,string|static|const,seeding\n" - -// "system.method.insert = group.seeding.ratio.enable ,simple,\"schedule=group.seeding.ratio,5,60,on_ratio=seeding\"\n" -// "system.method.insert = group.seeding.ratio.disable,simple,\"schedule_remove=group.seeding.ratio\"\n" -// "system.method.insert = group.seeding.ratio.command,simple|static,\"d.try_close= ;d.set_ignore_commands=1\"\n" -// "system.method.insert = group.seeding.ratio.min,value,200\n" -// "system.method.insert = group.seeding.ratio.max,value,300\n" -// "system.method.insert = group.seeding.ratio.upload,value,20M\n" + "system.method.insert = ratio.enable, simple|static|const,group.seeding.ratio.enable=\n" + "system.method.insert = ratio.disable,simple|static|const,group.seeding.ratio.disable=\n" + "system.method.insert = ratio.command,simple|static|const,group.seeding.ratio.command=\n" + "system.method.insert = ratio.min, simple|static|const,group.seeding.ratio.min=\n" + "system.method.insert = ratio.max, simple|static|const,group.seeding.ratio.max=\n" + "system.method.insert = ratio.upload, simple|static|const,group.seeding.ratio.upload=\n" + "system.method.insert = ratio.min.set, simple|static|const,group.seeding.ratio.min.set=$argument.0=\n" + "system.method.insert = ratio.max.set, simple|static|const,group.seeding.ratio.max.set=$argument.0=\n" + "system.method.insert = ratio.upload.set,simple|static|const,group.seeding.ratio.upload.set=$argument.0=\n" "set_name = \"$cat=$system.hostname=,:,$system.pid=\"\n"