From 8dc1d552c0bab7b72371c3a1529e365410c7548c Mon Sep 17 00:00:00 2001 From: Florian Pritz Date: Mon, 17 May 2010 12:03:46 +0200 Subject: add rtorrent-extended Signed-off-by: Florian Pritz --- rtorrent-extended/PKGBUILD | 122 +++ rtorrent-extended/bad_peer_handling.patch | 219 +++++ rtorrent-extended/canvas_color.patch | 282 ++++++ rtorrent-extended/dht_pex_static_map.patch | 12 + rtorrent-extended/ip_filter_no_boost_fast.patch | 1083 +++++++++++++++++++++++ rtorrent-extended/ipv6.patch | 572 ++++++++++++ rtorrent-extended/karabaja_mod.patch | 438 +++++++++ rtorrent-extended/magnet_uri.patch | 228 +++++ rtorrent-extended/rtorrent-extended.install | 9 + rtorrent-extended/trackerinfo.patch | 21 + rtorrent-extended/vi_kb_akston.patch | 176 ++++ rtorrent-extended/vi_kb_tjwoosta.patch | 175 ++++ 12 files changed, 3337 insertions(+) create mode 100644 rtorrent-extended/PKGBUILD create mode 100644 rtorrent-extended/bad_peer_handling.patch create mode 100644 rtorrent-extended/canvas_color.patch create mode 100644 rtorrent-extended/dht_pex_static_map.patch create mode 100644 rtorrent-extended/ip_filter_no_boost_fast.patch create mode 100644 rtorrent-extended/ipv6.patch create mode 100644 rtorrent-extended/karabaja_mod.patch create mode 100644 rtorrent-extended/magnet_uri.patch create mode 100644 rtorrent-extended/rtorrent-extended.install create mode 100644 rtorrent-extended/trackerinfo.patch create mode 100644 rtorrent-extended/vi_kb_akston.patch create mode 100644 rtorrent-extended/vi_kb_tjwoosta.patch (limited to 'rtorrent-extended') diff --git a/rtorrent-extended/PKGBUILD b/rtorrent-extended/PKGBUILD new file mode 100644 index 0000000..532273f --- /dev/null +++ b/rtorrent-extended/PKGBUILD @@ -0,0 +1,122 @@ +# Maintainer: Lucky +# Contributor: Ashren +# Contributor: Daenyth +# Contributor: Jeff Mickey +# Contributor: sh__ + +pkgname=rtorrent-extended +_pkgname=rtorrent +pkgver=0.8.6 +pkgrel=4 +pkgdesc="Ncurses BitTorrent client based on libTorrent with magnet link, IPv6, ipfilter, bad peers, color and trackerinfo patch." +arch=('i686' 'x86_64') +url="http://libtorrent.rakshasa.no" +license=('GPL') +depends=('libtorrent-extended=0.12.6-3' 'curl>=7.14.3' 'xmlrpc-c>=1858') +conflicts=('rtorrent') +provides=('rtorrent') +install=${pkgname}.install +source=(http://libtorrent.rakshasa.no/downloads/${_pkgname}-${pkgver}.tar.gz + # support for magnet links + # http://libtorrent.rakshasa.no/ticket/955 (deleted) + # http://libtorrent.rakshasa.no/ticket/2100 + dht_pex_static_map.patch + magnet_uri.patch + # support for IPv6 + # http://libtorrent.rakshasa.no/ticket/1111 + ipv6.patch + # support for ipfilter.dat + # http://libtorrent.rakshasa.no/ticket/239 + ip_filter_no_boost_fast.patch + # support for bad peers (kick/ban) + # http://ovh.ttdpatch.net/~jdrexler/rt/experimental/bad_peer_handling.diff + # https://calomel.org/rtorrent_mods.html + bad_peer_handling.patch + # support for colors + # http://libtorrent.rakshasa.no/ticket/1382 + canvas_color.patch + # karabaja mod patchset (color, interface) + # http://aur.archlinux.org/packages.php?ID=36604 + karabaja_mod.patch + # more infos at tracker list + trackerinfo.patch + # http://aur.archlinux.org/packages.php?ID=33756 + vi_kb_akston.patch + # http://aur.archlinux.org/packages.php?ID=35642 + vi_kb_tjwoosta.patch) +md5sums=('b804c45c01c40312926bcea6b55bb084' + '795c2818d4d869f90f1b7734f94347fc' + '8dcd2c76ee2ed48e86707a2b94b34c34' + 'cd9f4ce9202f6e03699b962f372a09e0' + '142f39586cd916608a7d569473bce1b0' + 'e698da498f306435fb8f89824c47e0e8' + '8721dea9d4da9a4f11051de7a2490e40' + 'fdcee74cc8dc4abb1a5b627ae00b8345' + '1fc3b40153450f34f8f4c4e1a161b6b8' + '752590f272e48a2828a7e64746778df8' + 'c5620714083fff8d609924c6bdb37fa0') + +build() { + cd "${srcdir}/${_pkgname}-${pkgver}" + + echo -e "\e[1;32m===> \e[0m Do you want magentlink patch? [Y/n]" + echo -e "\e[1;31m===> \e[0m !!! needed by ipv6 patch !!!" + read _answer_ml + if [[ ! ${_answer_ml} = [n,N] ]]; then + patch -p1 < ${srcdir}/dht_pex_static_map.patch + patch -p1 < ${srcdir}/magnet_uri.patch + fi + echo -e "\e[1;32m===> \e[0m Do you want ipv6 patch? [y/N]" + echo -e "\e[1;33m===> \e[0m !!! scgi/xmlrpc-c won't work with this patch !!!" + read _answer_ipv6 + if [[ ${_answer_ipv6} = [y,Y] ]]; then + patch -p1 < ${srcdir}/ipv6.patch + _option_ipv6="--enable-ipv6" + fi + echo -e "\e[1;32m===> \e[0m Do you want ipfilter patch? [Y/n]" + read _answer_ipf + if [[ ! ${_answer_ipf} = [n,N] ]]; then + patch -p1 < ${srcdir}/ip_filter_no_boost_fast.patch + fi + echo -e "\e[1;32m===> \e[0m Do you want bad peer handling patch? [Y/n]" + read _answer_bph + if [[ ! ${_answer_bph} = [n,N] ]]; then + patch -p1 < ${srcdir}/bad_peer_handling.patch + fi + echo -e "\e[1;32m===> \e[0m Do you want color/mod patch? [1/2/n]" + echo -e "\e[1;33m===> \e[0m 1) karabaja mod (default)" + echo -e "\e[1;33m===> \e[0m 2) canvas color" + read _answer_cc + if [[ ! ${_answer_cc} = [n,N] ]]; then + if [[ ! ${_answer_cc} = [2] ]]; then + patch -p1 < ${srcdir}/karabaja_mod.patch + else + patch -p1 < ${srcdir}/canvas_color.patch + fi + fi + echo -e "\e[1;32m===> \e[0m Do you want tracker info patch? [Y/n]" + read _answer_ti + if [[ ! ${_answer_ti} = [n,N] ]]; then + patch -p1 < ${srcdir}/trackerinfo.patch + fi + echo -e "\e[1;32m===> \e[0m Do you want keybindings patch? [1/2/n]" + echo -e "\e[1;33m===> \e[0m 1) vi_kb_tjwoosta (default)" + echo -e "\e[1;33m===> \e[0m 2) vi_kb_akston" + read _answer_kb + if [[ ! ${_answer_kb} = [n,N] ]]; then + if [[ ! ${_answer_kb} = [2] ]]; then + patch -p1 < ${srcdir}/vi_kb_tjwoosta.patch + else + patch -p1 < ${srcdir}/vi_kb_akston.patch + fi + fi + + sed -i 's/rTorrent \" VERSION/rTorrent-eXtended " VERSION/' src/ui/download_list.cc + + ./autogen.sh + CXXFLAGS="${CXXFLAGS} -fno-strict-aliasing" \ + ./configure --prefix=/usr --disable-debug ${_option_ipv6} --with-xmlrpc-c || return 1 + make || return 1 + make DESTDIR="${pkgdir}" install +} +# vim:set ts=2 sw=2 et: diff --git a/rtorrent-extended/bad_peer_handling.patch b/rtorrent-extended/bad_peer_handling.patch new file mode 100644 index 0000000..293cfad --- /dev/null +++ b/rtorrent-extended/bad_peer_handling.patch @@ -0,0 +1,219 @@ +diff --git a/src/command_download.cc b/src/command_download.cc +index 6043662..9afd9b0 100644 +--- a/src/command_download.cc ++++ b/src/command_download.cc +@@ -51,12 +51,14 @@ + #include + #include + #include ++#include + #include + + #include "core/download.h" + #include "core/download_store.h" + #include "core/manager.h" + #include "rpc/command_variable.h" ++#include "rpc/parse.h" + + #include "globals.h" + #include "control.h" +@@ -170,6 +172,120 @@ apply_d_delete_tied(core::Download* download) { + rpc::call_command("d.set_tied_to_file", std::string(), rpc::make_target(download)); + } + ++torrent::Object ++apply_d_snub_leechers(core::Download* download, const torrent::Object& rawArgs) { ++ if (!download->is_open() || download->is_done() || rpc::call_command_value("d.get_ignore_commands", rpc::make_target(download)) != 0) ++ return torrent::Object(); ++ ++ const torrent::Object::list_type& args = rawArgs.as_list(); ++ ++ if (args.size() < 3) ++ throw torrent::input_error("Too few arguments."); ++ ++ torrent::Object::list_type::const_iterator argItr = args.begin(); ++ uint64_t snub_ratio = rpc::convert_to_value(*argItr++); ++ uint64_t unsnub_ratio = rpc::convert_to_value(*argItr++); ++ uint64_t min_transfer = rpc::convert_to_value(*argItr++); ++ ++ for (torrent::ConnectionList::iterator itr = download->download()->connection_list()->begin(); itr != download->download()->connection_list()->end(); ++itr) { ++#if LIBTORRENT_FRIENDS ++ if (((*itr)->bitfield() && (*itr)->bitfield()->is_all_set()) || (*itr)->peer_info()->is_friend()) ++#else ++ if (((*itr)->bitfield() && (*itr)->bitfield()->is_all_set())) ++#endif ++ continue; ++ ++ uint64_t up = (*itr)->up_rate()->total(); ++ uint64_t down = (*itr)->down_rate()->total(); ++ ++ if ((*itr)->is_snubbed()) { ++ if (down * unsnub_ratio >= std::max(up, min_transfer)) ++ (*itr)->set_snubbed(false); ++ ++ } else if (up > min_transfer && down * snub_ratio < up) { ++ (*itr)->set_snubbed(true); ++ } ++ } ++ ++ return torrent::Object(); ++} ++ ++torrent::Object ++apply_d_ban_slow_peers(core::Download* download, const torrent::Object& rawArgs) { ++ if (!download->is_open() || download->is_done() || rpc::call_command_value("d.get_ignore_commands", rpc::make_target(download)) != 0) ++ return torrent::Object(); ++ ++ const torrent::Object::list_type& args = rawArgs.as_list(); ++ ++ if (args.size() < 4) ++ throw torrent::input_error("Too few arguments."); ++ ++ torrent::ConnectionList* clist = download->download()->connection_list(); ++ int extraPeers = clist->size() - clist->min_size(); ++ if (extraPeers <= 0) ++ return torrent::Object(); ++ ++ torrent::Object::list_type::const_iterator argItrStart = args.begin(); ++ int extraSeeds = download->download()->peers_complete() - rpc::convert_to_value(*argItrStart++); ++ uint32_t minRate = rpc::convert_to_value(*argItrStart++); ++ ++ for (torrent::ConnectionList::iterator itr = clist->begin(); extraPeers > 0 && itr != clist->end(); ++itr) { ++#if LIBTORRENT_FRIENDS ++ if ((*itr)->peer_info()->is_friend()) ++ continue; ++#endif ++ ++ bool isSeed = (*itr)->bitfield() && (*itr)->bitfield()->is_all_set(); ++ if (isSeed && extraSeeds <= 0) ++ continue; ++ ++ int64_t down = (*itr)->down_rate()->total(); ++ uint32_t rate = (*itr)->down_rate()->rate(); ++ ++ for (torrent::Object::list_type::const_iterator argItr = argItrStart; argItr != args.end(); ++argItr) { ++ if (rate >= minRate || down >= rpc::convert_to_value(*argItr++)) ++ break; ++ ++ if (cachedTime.seconds() - (*itr)->peer_info()->last_connection() < rpc::convert_to_value(*argItr) * 60) ++ continue; ++ ++ (*itr)->set_banned(); ++ ++ extraSeeds -= isSeed; ++ extraPeers--; ++ break; ++ } ++ } ++ ++ // Need to go by indices because erasing may invalidate iterators. ++ for (size_t pId = 0; pId < clist->size(); ) ++ if ((*(clist->begin() + pId))->is_banned()) ++ download->connection_list()->erase(*(clist->begin() + pId), 0); ++ else ++ pId++; ++ ++ return torrent::Object(); ++} ++ ++void ++apply_d_unban_peers(core::Download* download) { ++ torrent::PeerList* list = download->download()->peer_list(); ++ ++ for (torrent::PeerList::const_iterator itr = list->begin(); itr != list->end(); ++itr) ++ if (itr->second->is_banned()) ++ itr->second->set_unbanned(); ++} ++ ++void ++apply_d_unsnub_peers(core::Download* download) { ++ if (!download->is_open()) ++ return; ++ ++ for (torrent::ConnectionList::iterator itr = download->download()->connection_list()->begin(); itr != download->download()->connection_list()->end(); ++itr) ++ if ((*itr)->is_snubbed()) ++ (*itr)->set_snubbed(false); ++} ++ + void + apply_d_connection_type(core::Download* download, const std::string& name) { + torrent::Download::ConnectionType connType; +@@ -580,6 +696,11 @@ initialize_command_download() { + ADD_CD_LIST("delete_link", rak::bind_ptr_fn(&apply_d_change_link, 1)); + ADD_CD_V_VOID("delete_tied", &apply_d_delete_tied); + ++ ADD_CD_LIST("ban_slow_peers", rak::ptr_fn(&apply_d_ban_slow_peers)); ++ ADD_CD_LIST("snub_leechers", rak::ptr_fn(&apply_d_snub_leechers)); ++ ADD_CD_V_VOID("unban_peers", &apply_d_unban_peers); ++ ADD_CD_V_VOID("unsnub_peers", &apply_d_unsnub_peers); ++ + CMD_FUNC_SINGLE("d.start", "d.set_hashing_failed=0 ;view.set_visible=started"); + CMD_FUNC_SINGLE("d.stop", "view.set_visible=stopped"); + CMD_FUNC_SINGLE("d.try_start", "branch=\"or={d.get_hashing_failed=,d.get_ignore_commands=}\",{},{view.set_visible=started}"); +diff --git a/src/command_events.cc b/src/command_events.cc +index b25dfbc..a0250d9 100644 +--- a/src/command_events.cc ++++ b/src/command_events.cc +@@ -42,6 +42,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + +@@ -276,6 +278,31 @@ apply_close_low_diskspace(int64_t arg) { + control->core()->push_log("Closed torrents due to low diskspace."); + } + ++// Should call the d.* commands via RPC, but there doesn't seem to be a way to ++// pass variable-sized argument lists, so call the functions directly for now. ++torrent::Object apply_d_snub_leechers(core::Download*, const torrent::Object&); ++torrent::Object apply_d_ban_slow_peers(core::Download*, const torrent::Object&); ++ ++torrent::Object ++apply_snub_leechers(const torrent::Object& rawArgs) { ++ for (core::Manager::DListItr ditr = control->core()->download_list()->begin(); ditr != control->core()->download_list()->end(); ditr++) { ++ if ((*ditr)->is_open() && !(*ditr)->is_done() && rpc::call_command_value("d.get_ignore_commands", rpc::make_target(*ditr)) == 0) ++ apply_d_snub_leechers(*ditr, rawArgs); ++ } ++ ++ return torrent::Object(); ++} ++ ++torrent::Object ++apply_ban_slow_peers(const torrent::Object& rawArgs) { ++ for (core::Manager::DListItr ditr = control->core()->download_list()->begin(); ditr != control->core()->download_list()->end(); ditr++) { ++ if ((*ditr)->is_open() && !(*ditr)->is_done() && rpc::call_command_value("d.get_ignore_commands", rpc::make_target(*ditr)) == 0) ++ apply_d_ban_slow_peers(*ditr, rawArgs); ++ } ++ ++ return torrent::Object(); ++} ++ + torrent::Object + apply_download_list(const torrent::Object& rawArgs) { + const torrent::Object::list_type& args = rawArgs.as_list(); +@@ -369,6 +396,8 @@ initialize_command_events() { + ADD_COMMAND_LIST("on_finished", rak::bind_ptr_fn(&apply_on_state_change, "event.download.finished")); + + ADD_COMMAND_STRING("on_ratio", rak::ptr_fn(&apply_on_ratio)); ++ ADD_COMMAND_LIST("snub_leechers", rak::ptr_fn(&apply_snub_leechers)); ++ ADD_COMMAND_LIST("ban_slow_peers", rak::ptr_fn(&apply_ban_slow_peers)); + + ADD_COMMAND_VOID("start_tied", &apply_start_tied); + ADD_COMMAND_VOID("stop_untied", &apply_stop_untied); +diff --git a/src/command_helpers.h b/src/command_helpers.h +index a807b5f..cda29d8 100644 +--- a/src/command_helpers.h ++++ b/src/command_helpers.h +@@ -54,7 +54,7 @@ namespace rpc { + #define COMMAND_DOWNLOAD_SLOTS_SIZE 150 + #define COMMAND_FILE_SLOTS_SIZE 30 + #define COMMAND_FILE_ITR_SLOTS_SIZE 10 +-#define COMMAND_PEER_SLOTS_SIZE 20 ++#define COMMAND_PEER_SLOTS_SIZE 30 + #define COMMAND_TRACKER_SLOTS_SIZE 15 + #define COMMAND_ANY_SLOTS_SIZE 50 + diff --git a/rtorrent-extended/canvas_color.patch b/rtorrent-extended/canvas_color.patch new file mode 100644 index 0000000..88423b1 --- /dev/null +++ b/rtorrent-extended/canvas_color.patch @@ -0,0 +1,282 @@ +diff --git a/src/command_network.cc b/src/command_network.cc +index 2494988..7b105ca 100644 +--- a/src/command_network.cc ++++ b/src/command_network.cc +@@ -560,4 +560,8 @@ initialize_command_network() { + // Not really network stuff: + ADD_VARIABLE_BOOL ("handshake_log", false); + ADD_VARIABLE_STRING("log.tracker", ""); ++ ADD_COMMAND_VALUE_TRI("done_fg_color", rak::make_mem_fun(control->ui(), &ui::Root::set_done_fg_color), rak::make_mem_fun(control->ui(), &ui::Root::get_done_fg_color)); ++ ADD_COMMAND_VALUE_TRI("done_bg_color", rak::make_mem_fun(control->ui(), &ui::Root::set_done_bg_color), rak::make_mem_fun(control->ui(), &ui::Root::get_done_bg_color)); ++ ADD_COMMAND_VALUE_TRI("active_fg_color", rak::make_mem_fun(control->ui(), &ui::Root::set_active_fg_color), rak::make_mem_fun(control->ui(), &ui::Root::get_active_fg_color)); ++ ADD_COMMAND_VALUE_TRI("active_bg_color", rak::make_mem_fun(control->ui(), &ui::Root::set_active_bg_color), rak::make_mem_fun(control->ui(), &ui::Root::get_active_bg_color)); + } +diff --git a/src/display/canvas.cc b/src/display/canvas.cc +index 4e621df..0c97eea 100644 +--- a/src/display/canvas.cc ++++ b/src/display/canvas.cc +@@ -92,6 +92,10 @@ Canvas::initialize() { + m_isInitialized = true; + + initscr(); ++ start_color(); ++ use_default_colors(); ++ init_pair(2, -1, -1); ++ init_pair(1, -1, -1); + raw(); + noecho(); + nodelay(stdscr, TRUE); +diff --git a/src/display/window_download_list.cc b/src/display/window_download_list.cc +index 71efec0..06e7cd4 100644 +--- a/src/display/window_download_list.cc ++++ b/src/display/window_download_list.cc +@@ -37,6 +37,7 @@ + #include "config.h" + + #include ++#include + + #include "core/download.h" + #include "core/view.h" +@@ -96,12 +97,30 @@ WindowDownloadList::redraw() { + char* position; + char* last = buffer + m_canvas->width() - 2 + 1; + ++ if( pos >= m_canvas->height() ) break; + position = print_download_title(buffer, last, *range.first); +- m_canvas->print(0, pos++, "%c %s", range.first == m_view->focus() ? '*' : ' ', buffer); ++ m_canvas->print(0, pos, "%c %s", range.first == m_view->focus() ? '*' : ' ', buffer); ++ if( (*range.first)->is_done() ) { ++ if( (*range.first)->download()->up_rate()->rate() != 0 ) { ++ m_canvas->set_attr(0, pos, m_canvas->width()-1, A_BOLD, 2); ++ } else { ++ m_canvas->set_attr(0, pos, m_canvas->width()-1, A_NORMAL, 2); ++ } ++ } else if( (*range.first)->download()->is_active() ) { ++ if( (*range.first)->download()->down_rate()->rate() != 0 ) { ++ m_canvas->set_attr(0, pos, m_canvas->width()-1, A_BOLD, 1); ++ } else { ++ m_canvas->set_attr(0, pos, m_canvas->width()-1, A_NORMAL, 1); ++ } ++ } ++ pos++; + ++ if( pos >= m_canvas->height() ) break; ++ + position = print_download_info(buffer, last, *range.first); + m_canvas->print(0, pos++, "%c %s", range.first == m_view->focus() ? '*' : ' ', buffer); + ++ if( pos >= m_canvas->height() ) break; + position = print_download_status(buffer, last, *range.first); + m_canvas->print(0, pos++, "%c %s", range.first == m_view->focus() ? '*' : ' ', buffer); + +@@ -109,4 +128,40 @@ WindowDownloadList::redraw() { + } + } + ++void ++WindowDownloadList::set_done_fg_color(int64_t color) { ++ short fg, bg; ++ pair_content(2, &fg, &bg); ++ if( color < 0 ) color = -1; ++ color = color % 8; ++ init_pair(2, (short)color, bg); ++} ++ ++void ++WindowDownloadList::set_done_bg_color(int64_t color) { ++ short fg, bg; ++ pair_content(2, &fg, &bg); ++ if( color < 0 ) color = -1; ++ color = color % 8; ++ init_pair(2, fg, (short)color); ++} ++ ++void ++WindowDownloadList::set_active_fg_color(int64_t color) { ++ short fg, bg; ++ pair_content(1, &fg, &bg); ++ if( color < 0 ) color = -1; ++ color = color % 8; ++ init_pair(1, (short)color, bg); ++} ++ ++void ++WindowDownloadList::set_active_bg_color(int64_t color) { ++ short fg, bg; ++ pair_content(1, &fg, &bg); ++ if( color < 0 ) color = -1; ++ color = color % 8; ++ init_pair(1, fg, (short)color); ++} ++ + } +diff --git a/src/display/window_download_list.h b/src/display/window_download_list.h +index 4ce5ea1..313e87b 100644 +--- a/src/display/window_download_list.h ++++ b/src/display/window_download_list.h +@@ -59,6 +59,10 @@ public: + virtual void redraw(); + + void set_view(core::View* l); ++ void set_done_fg_color(int64_t color); ++ void set_done_bg_color(int64_t color); ++ void set_active_fg_color(int64_t color); ++ void set_active_bg_color(int64_t color); + + private: + core::View* m_view; +diff --git a/src/ui/download_list.cc b/src/ui/download_list.cc +index b7d6983..e72bff6 100644 +--- a/src/ui/download_list.cc ++++ b/src/ui/download_list.cc +@@ -137,6 +137,11 @@ DownloadList::unfocus_download(core::Download* d) { + current_view()->next_focus(); + } + ++display::WindowDownloadList* ++DownloadList::current_window_list() { ++ return dynamic_cast(m_uiArray[DISPLAY_DOWNLOAD_LIST])->window(); ++} ++ + void + DownloadList::activate_display(Display displayType) { + if (!is_active()) +diff --git a/src/ui/download_list.h b/src/ui/download_list.h +index dda1b34..11137fa 100644 +--- a/src/ui/download_list.h ++++ b/src/ui/download_list.h +@@ -101,6 +101,7 @@ public: + void activate_display(Display d); + + core::View* current_view(); ++ display::WindowDownloadList* current_window_list(); + void set_current_view(const std::string& name); + + void slot_open_uri(SlotOpenUri s) { m_slotOpenUri = s; } +diff --git a/src/ui/element_download_list.h b/src/ui/element_download_list.h +index ed5de30..7c0fb9d 100644 +--- a/src/ui/element_download_list.h ++++ b/src/ui/element_download_list.h +@@ -60,6 +60,7 @@ public: + void disable(); + + core::View* view() { return m_view; } ++ WDownloadList* window() { return m_window; } + void set_view(core::View* l); + + void receive_command(const char* cmd); +diff --git a/src/ui/root.cc b/src/ui/root.cc +index b01f4ed..d344718 100644 +--- a/src/ui/root.cc ++++ b/src/ui/root.cc +@@ -44,6 +44,7 @@ + + #include "core/manager.h" + #include "display/frame.h" ++#include "display/window_download_list.h" + #include "display/window_http_queue.h" + #include "display/window_title.h" + #include "display/window_input.h" +@@ -65,7 +66,11 @@ Root::Root() : + m_windowTitle(NULL), + m_windowHttpQueue(NULL), + m_windowInput(NULL), +- m_windowStatusbar(NULL) { ++ m_windowStatusbar(NULL), ++ done_fg_color(-1), ++ done_bg_color(-1), ++ active_fg_color(-1), ++ active_bg_color(-1) { + } + + void +@@ -97,6 +102,10 @@ Root::init(Control* c) { + setup_keys(); + + m_downloadList->activate(rootFrame->frame(1)); ++ m_downloadList->current_window_list()->set_done_fg_color(done_fg_color); ++ m_downloadList->current_window_list()->set_done_bg_color(done_bg_color); ++ m_downloadList->current_window_list()->set_active_fg_color(active_fg_color); ++ m_downloadList->current_window_list()->set_active_bg_color(active_bg_color); + } + + void +@@ -219,6 +228,46 @@ Root::set_up_throttle(unsigned int throttle) { + torrent::set_max_unchoked(maxUnchoked); + } + ++int ++Root::get_done_fg_color() { ++ return done_fg_color; ++} ++ ++void ++Root::set_done_fg_color(int64_t color) { ++ done_fg_color = color; ++} ++ ++int ++Root::get_done_bg_color() { ++ return done_bg_color; ++} ++ ++void ++Root::set_done_bg_color(int64_t color) { ++ done_bg_color = color; ++} ++ ++int ++Root::get_active_fg_color() { ++ return active_fg_color; ++} ++ ++void ++Root::set_active_fg_color(int64_t color) { ++ active_fg_color = color; ++} ++ ++int ++Root::get_active_bg_color() { ++ return active_bg_color; ++} ++ ++void ++Root::set_active_bg_color(int64_t color) { ++ active_bg_color = color; ++} ++ + void + Root::adjust_down_throttle(int throttle) { + set_down_throttle(std::max(torrent::down_throttle_global()->max_rate() / 1024 + throttle, 0)); +diff --git a/src/ui/root.h b/src/ui/root.h +index e9a7907..4eef1df 100644 +--- a/src/ui/root.h ++++ b/src/ui/root.h +@@ -83,6 +83,15 @@ public: + void set_down_throttle_i64(int64_t throttle) { set_down_throttle(throttle >> 10); } + void set_up_throttle_i64(int64_t throttle) { set_up_throttle(throttle >> 10); } + ++ int get_done_fg_color(); ++ void set_done_fg_color(int64_t color); ++ int get_done_bg_color(); ++ void set_done_bg_color(int64_t color); ++ int get_active_fg_color(); ++ void set_active_fg_color(int64_t color); ++ int get_active_bg_color(); ++ void set_active_bg_color(int64_t color); ++ + void adjust_down_throttle(int throttle); + void adjust_up_throttle(int throttle); + +@@ -105,6 +114,10 @@ private: + WStatusbar* m_windowStatusbar; + + input::Bindings m_bindings; ++ int64_t done_fg_color; ++ int64_t done_bg_color; ++ int64_t active_fg_color; ++ int64_t active_bg_color; + }; + + } diff --git a/rtorrent-extended/dht_pex_static_map.patch b/rtorrent-extended/dht_pex_static_map.patch new file mode 100644 index 0000000..e302b4c --- /dev/null +++ b/rtorrent-extended/dht_pex_static_map.patch @@ -0,0 +1,12 @@ +diff --git a/src/core/view.cc b/src/core/view.cc +index 8e2d997..f3b4f0d 100644 +--- a/src/core/view.cc ++++ b/src/core/view.cc +@@ -90,6 +90,7 @@ struct view_downloads_filter : std::unary_function { + case torrent::Object::TYPE_STRING: return !result.as_string().empty(); + case torrent::Object::TYPE_LIST: return !result.as_list().empty(); + case torrent::Object::TYPE_MAP: return !result.as_map().empty(); ++ case torrent::Object::TYPE_SSTRING:return !result.as_sstring().empty(); + } + + // The default filter action is to return true, to not filter diff --git a/rtorrent-extended/ip_filter_no_boost_fast.patch b/rtorrent-extended/ip_filter_no_boost_fast.patch new file mode 100644 index 0000000..d13fb69 --- /dev/null +++ b/rtorrent-extended/ip_filter_no_boost_fast.patch @@ -0,0 +1,1083 @@ +diff --git a/src/command_network.cc b/src/command_network.cc +index 34f75ab..2494988 100644 +--- a/src/command_network.cc ++++ b/src/command_network.cc +@@ -36,6 +36,11 @@ + + #include "config.h" + ++#include ++#include ++#include ++#include ++ + #include + #include + #include +@@ -62,6 +67,10 @@ + #include "control.h" + #include "command_helpers.h" + ++#include "utils/pattern.h" ++#include "core/ip_filter.h" ++ ++ + torrent::Object + apply_throttle(bool up, const torrent::Object& rawArgs) { + const torrent::Object::list_type& args = rawArgs.as_list(); +@@ -209,6 +218,59 @@ apply_encryption(const torrent::Object& rawArgs) { + } + + torrent::Object ++apply_ip_filter(const torrent::Object& rawArgs) { ++ const torrent::Object::list_type& args = rawArgs.as_list(); ++ ++ std::list files; ++ ++ for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) { ++ std::string file( itr->as_string() ); ++ utils::trim( file ); ++ if( access(file.c_str(),F_OK | R_OK) ) ++ throw torrent::input_error("IpFilter file '" + file + "' does not exist or not readable. Filter could not be loaded"); ++ files.push_back( file ); ++ } ++ ++ std::stringstream logMsg; ++ if( files.empty() ) { ++ logMsg << "IpFilter is empty"; ++ control->core()->push_log( logMsg.str().c_str() ); ++ } ++ else { ++ core::IpFilter* f = new core::IpFilter(); ++ logMsg << "IpFilter is initialized with files: "; ++ int entries = 0; ++ clock_t time_start = clock(); ++ for( std::list::iterator itr = files.begin(); itr != files.end(); itr++) { ++ std::cout << "Loading IP filters from '" << *itr << "'..."; ++ std::cout.flush(); ++ if( itr != files.begin() ) ++ logMsg << ", "; ++ logMsg << *itr; ++ int merges = f->add_from_file( *itr ); ++ if( merges < 0 ) { ++ std::cout << "error" << std::endl; ++ std::cout.flush(); ++ throw torrent::input_error("IpFilter could not load file '" + *itr + "'"); ++ } ++ std::cout << "done. Loaded " << (f->size()-entries) << " ranges. " << merges << " ranges were merged." << std::endl; ++ std::cout.flush(); ++ entries = f->size(); ++ } ++ control->core()->push_log( logMsg.str().c_str() ); ++ std::stringstream logMsg2("IpFilter loaded with "); ++ logMsg2 << f->size() << " ranges total. " << f->get_merges() << " ranges were merged."; ++ control->core()->push_log( logMsg2.str().c_str() ); ++ std::cout << logMsg2.str() << std::endl; ++ std::cout << "IP_Filters loaded in " << (double)(clock()-time_start)/CLOCKS_PER_SEC << " seconds" << std::endl; ++ std::cout.flush(); ++ control->core()->set_ip_filter( f ); ++ } ++ ++ return torrent::Object(); ++} ++ ++torrent::Object + apply_tos(const torrent::Object& rawArg) { + rpc::Command::value_type value; + torrent::ConnectionManager* cm = torrent::connection_manager(); +@@ -492,6 +554,9 @@ initialize_command_network() { + + ADD_VARIABLE_BOOL("peer_exchange", true); + ++ ADD_COMMAND_VOID("reload_ip_filter", rak::make_mem_fun(control->core(), &core::Manager::reload_ip_filter)); ++ ADD_COMMAND_LIST("ip_filter", rak::ptr_fn(&apply_ip_filter)); ++ + // Not really network stuff: + ADD_VARIABLE_BOOL ("handshake_log", false); + ADD_VARIABLE_STRING("log.tracker", ""); +diff --git a/src/core/Makefile.am b/src/core/Makefile.am +index fe07cbf..fb26380 100644 +--- a/src/core/Makefile.am ++++ b/src/core/Makefile.am +@@ -36,6 +36,15 @@ libsub_core_a_SOURCES = \ + view.cc \ + view.h \ + view_manager.cc \ +- view_manager.h ++ view_manager.h \ ++ ip_address.cc \ ++ ip_address.h \ ++ ip_filter.cc \ ++ ip_filter.h \ ++ ip_range.cc \ ++ ip_range.h \ ++ printable.h \ ++ regex_namespace.h \ ++ ip_filter_statics.cc + + INCLUDES = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) +diff --git a/src/core/Makefile.in b/src/core/Makefile.in +index 24b7eb4..32b0f6b 100644 +--- a/src/core/Makefile.in ++++ b/src/core/Makefile.in +@@ -63,7 +63,11 @@ am_libsub_core_a_OBJECTS = curl_get.$(OBJEXT) curl_socket.$(OBJEXT) \ + manager.$(OBJEXT) poll_manager.$(OBJEXT) \ + poll_manager_epoll.$(OBJEXT) poll_manager_kqueue.$(OBJEXT) \ + poll_manager_select.$(OBJEXT) view.$(OBJEXT) \ +- view_manager.$(OBJEXT) ++ view_manager.$(OBJEXT) \ ++ ip_address.$(OBJEXT) \ ++ ip_filter.$(OBJEXT) \ ++ ip_range.$(OBJEXT) \ ++ ip_filter_statics.$(OBJEXT) + libsub_core_a_OBJECTS = $(am_libsub_core_a_OBJECTS) + DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) + depcomp = $(SHELL) $(top_srcdir)/depcomp +@@ -252,7 +256,16 @@ libsub_core_a_SOURCES = \ + view.cc \ + view.h \ + view_manager.cc \ +- view_manager.h ++ view_manager.h \ ++ ip_address.cc \ ++ ip_address.h \ ++ ip_filter.cc \ ++ ip_filter.h \ ++ ip_range.cc \ ++ ip_range.h \ ++ printable.h \ ++ regex_namespace.h \ ++ ip_filter_statics.cc + + INCLUDES = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) + all: all-am +@@ -320,6 +333,9 @@ distclean-compile: + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/poll_manager_select.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/view.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/view_manager.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_address.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_range.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ip_filter_statics.Po@am__quote@ + + .cc.o: + @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +diff --git a/src/core/ip_address.cc b/src/core/ip_address.cc +new file mode 100644 +index 0000000..a9a9251 +--- /dev/null ++++ b/src/core/ip_address.cc +@@ -0,0 +1,25 @@ ++#include ++#include ++#include ++ ++#include "ip_address.h" ++#include "utils/pattern.h" ++ ++namespace core { ++ ++std::pair IpAddress::to_int( const std::string& address ) { ++ uint32_t a; ++ int r = inet_pton( AF_INET, address.c_str(), &a); ++ if( r ) ++ a = ntohl( a ); ++ return std::pair( (r!=0), a ); ++} ++ ++std::string IpAddress::to_string() const { ++ char buf[128] = ""; ++ uint32_t a = htonl( m_address ); ++ inet_ntop( AF_INET, &a, buf, sizeof(buf) ); ++ return std::string( buf ); ++} ++ ++} +diff --git a/src/core/ip_address.h b/src/core/ip_address.h +new file mode 100644 +index 0000000..a9ad142 +--- /dev/null ++++ b/src/core/ip_address.h +@@ -0,0 +1,65 @@ ++#ifndef IPADDRESS_H ++#define IPADDRESS_H ++ ++#include ++#include ++ ++#include "printable.h" ++#include "utils/pattern.h" ++#include "regex_namespace.h" ++ ++namespace core { ++ ++class IpAddress : public Printable { ++ friend class IpRange; ++ ++ private: // constants ++ static const std::string PATTERN_IP_EXPRESSION; ++ static const std::string PATTERN_IP_BYTES_EXPRESSION; ++ static const regex::Pattern PATTERN_IP_BYTES; ++ ++ static const int GRP_IP_FIRST_BYTE; ++ static const int GRP_IP_BYTES_COUNT; ++ ++ private: // fields ++ uint32_t m_address; ++ ++ private: // static methods ++ ++ private: // dynamic methods ++ IpAddress() : m_address(0) {} ++ ++ void copy( const IpAddress& addr ) { m_address = addr.m_address;} ++ ++ public: // static methods ++ static std::pair to_int( const std::string& strAddress ); ++ static IpAddress* parse( const std::string& strAddress ) { ++ std::pair result = to_int( strAddress ); ++ return ( !result.first ) ? NULL : new IpAddress( result.second ); ++ } ++ ++ public: // dynamic methods ++ IpAddress( uint32_t address ) : m_address(address) {} ++ IpAddress( const IpAddress& addr ) { copy( addr ); } ++ IpAddress& operator= ( const IpAddress& addr ) { copy( addr ); return *this; } ++ ++ operator uint32_t() const { return m_address; } ++ ++ bool operator>= ( const IpAddress& ip ) const { return (m_address >= ip.m_address); } ++ bool operator<= ( const IpAddress& ip ) const { return (m_address <= ip.m_address); } ++ bool operator< ( const IpAddress& ip ) const { return (m_address < ip.m_address); } ++ bool operator> ( const IpAddress& ip ) const { return (m_address > ip.m_address); } ++ bool operator== ( const IpAddress& ip ) const { return (m_address == ip.m_address); } ++ bool operator!= ( const IpAddress& ip ) const { return (m_address != ip.m_address); } ++ ++ bool operator>= ( uint32_t ip ) const { return (m_address >= ip); } ++ bool operator<= ( uint32_t ip ) const { return (m_address <= ip); } ++ bool operator< ( uint32_t ip ) const { return (m_address < ip); } ++ bool operator> ( uint32_t ip ) const { return (m_address > ip); } ++ bool operator== ( uint32_t ip ) const { return (m_address == ip); } ++ bool operator!= ( uint32_t ip ) const { return (m_address != ip); } ++ ++ std::string to_string() const; ++ }; ++} ++#endif +diff --git a/src/core/ip_filter.cc b/src/core/ip_filter.cc +new file mode 100644 +index 0000000..8f46a42 +--- /dev/null ++++ b/src/core/ip_filter.cc +@@ -0,0 +1,165 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ip_filter.h" ++ ++namespace core { ++ ++int IpFilter::merge_and_insert( range_map* rs, IpRange* r ) { ++ if( !r || !r->get_from() ) ++ return 0; ++ ++ std::pair p( *r->get_from(), IpRange::ptr(r) ); ++ std::pair duo = rs->insert( p ); ++ ++ range_itr idx = duo.first; ++ bool wasInserted = duo.second; ++ IpRange* curr = NULL; ++ int mergeCount = 0; ++ ++ if( !wasInserted ) { // exactly the same start address already exists ++ curr = idx->second; ++ if( *curr->get_to() < *r->get_to() ) ++ curr->set_to( r->get_to() ); ++ delete r; ++ r = curr; ++ mergeCount++; ++ } ++ else { ++ if( idx != rs->begin() ) { ++ --idx; ++ curr = idx->second; // previous ++ if( *r->get_from() <= *curr->get_to() ) ++ r = curr; ++ else ++ ++idx; ++ } ++ } ++ ++ if( idx != rs->end() ) ++ ++idx; ++ ++ while( idx != rs->end() ) { ++ curr = idx->second; ++ if( *r->get_to() < *curr->get_from() ) ++ break; ++ ++ std::string d = r->get_description(); ++ d += " / " + curr->get_description(); ++ r->set_description( d ); ++ if( *r->get_to() < *curr->get_to() ) ++ r->set_to( curr->get_to() ); ++ rs->erase( idx++ ); ++ delete curr; ++ mergeCount++; ++ } ++ return mergeCount; ++} ++ ++int IpFilter::add_from_file( const std::string& fileName, range_map* rs, str_list* files ) { ++ FILE *f = fopen(fileName.c_str(),"r"); ++ int mergeCount = 0; ++ if (f==0) return -1; ++ char *line = (char *)malloc(64); ++ size_t sz=64; ++ int charsread = 0; ++ int linesread=0; ++ while( (charsread=getline(&line,&sz,f)) >=0 ) { ++ if( (line[0] == '#' ) || ( charsread <= 1 ) ) ++ continue; ++ ++ IpRange* ir = IpRange::parse( line, charsread ); ++ if( !ir || !ir->get_from() || !ir->get_to() ) ++ continue; ++ ++ mergeCount += merge_and_insert( rs, ir ); ++ } ++ free(line); ++ files->push_back( std::string(fileName) ); ++ fclose(f); ++ m_merges += mergeCount; ++ return mergeCount; ++} ++ ++int IpFilter::add_from_file( const std::string& fileName ) { ++ if( !m_ranges ) ++ m_ranges = new range_map(); ++ if( !m_loadedFiles ) ++ m_loadedFiles = new std::list(); ++ ++ return add_from_file( fileName, m_ranges, m_loadedFiles ); ++} ++ ++int IpFilter::reload() { ++ if( !m_loadedFiles || m_loadedFiles->empty() ) ++ return 0; ++ ++ range_map* rs = new range_map(); ++ str_list* files = new str_list(); ++ int mergeCount = 0; ++ for( str_list::const_iterator it = m_loadedFiles->begin(), end = m_loadedFiles->end(); it != end; it++ ) ++ mergeCount += add_from_file( *it, rs, files ); ++ ++ range_map* rsOld = m_ranges; ++ m_ranges = rs; ++ if( rsOld ) { ++ clear( rsOld ); ++ delete rsOld; ++ } ++ ++ str_list* filesOld = m_loadedFiles; ++ m_loadedFiles = files; ++ if( filesOld ) { ++ clear( filesOld ); ++ delete filesOld; ++ } ++ ++ m_merges = mergeCount; ++ return mergeCount; ++} ++ ++IpRange* IpFilter::find_range( uint32_t ip ) const { ++ if( (ip >= 0) && m_ranges && !m_ranges->empty() ) { ++ range_itr idx = m_ranges->upper_bound( ip ); ++ if( idx != m_ranges->begin() ) ++ --idx; ++ IpRange* curr = idx->second; ++ if( curr->includes( ip ) ) ++ return curr; ++ } ++ return NULL; ++} ++ ++std::string IpFilter::to_string() const { ++ std::stringstream result; ++ if( !m_ranges ) ++ result << "NULL" << std::endl; ++ else { ++ for( range_map::const_iterator it = m_ranges->begin() ; it != m_ranges->end(); it++ ) { ++ const IpAddress a = it->first; ++ IpRange* ir = it->second; ++ result << a << ": " << *ir << std::endl; ++ } ++ } ++ return result.str(); ++} ++ ++void IpFilter::clear( range_map* map ) { ++ if( map ) { ++ for( range_itr i = map->begin(), j = map->end(); i != j; i++ ) ++ delete i->second; ++ map->clear(); ++ } ++} ++ ++void IpFilter::clear( str_list* list ) { ++ if( list ) ++ list->clear(); ++} ++ ++} +diff --git a/src/core/ip_filter.h b/src/core/ip_filter.h +new file mode 100644 +index 0000000..07f2004 +--- /dev/null ++++ b/src/core/ip_filter.h +@@ -0,0 +1,85 @@ ++#ifndef IPFILTER_H ++#define IPFILTER_H ++ ++#include ++#include ++#include ++ ++#include "printable.h" ++#include "ip_address.h" ++#include "ip_range.h" ++ ++namespace core { ++ ++typedef std::map range_map; ++typedef range_map::iterator range_itr; ++typedef std::list str_list; ++ ++class IpFilter : public Printable { ++ private: // fields ++ int m_merges; ++ range_map* m_ranges; ++ str_list* m_loadedFiles; ++ ++ private: // static methods ++ static void clear( range_map* map ); ++ static void clear( str_list* list ); ++ ++ private: // dynamic methods ++ void init_members(void) { // to avoid long constructor lines for every ctor ++ m_ranges = NULL; ++ m_loadedFiles = NULL; ++ m_merges = 0; ++ } ++ int merge_and_insert( range_map* rs, IpRange* r ); ++ int add_from_file( const std::string& fileName, range_map* rs, str_list* files ); ++ ++ public: // static methods ++ ++ public: // dynamic methods ++ IpFilter() { init_members(); } ++ ~IpFilter() { ++ clear(); ++ if( m_ranges ) delete m_ranges; ++ if( m_loadedFiles ) delete m_loadedFiles; ++ m_ranges = NULL; ++ m_loadedFiles = NULL; ++ } ++ IpFilter( std::string* files, int size ) { ++ init_members(); ++ for( int i = 0; i < size; i++, files++ ) ++ add_from_file( *files ); ++ } ++ IpFilter( str_list& files ) { ++ init_members(); ++ for( str_list::const_iterator i = files.begin(), last = files.end(); i != last; i++ ) ++ add_from_file( *i ); ++ } ++ IpFilter( IpFilter& f ) { ++ init_members(); ++ m_ranges = new range_map( *f.m_ranges ); ++ m_loadedFiles = new str_list( *f.m_loadedFiles ); ++ } ++ ++ int reload(); ++ int add_from_file( const std::string& fileName ); ++ int add_from_file( char* fileName ) { std::string s( fileName ); return add_from_file(s); } ++ void clear() { clear( m_ranges ); clear( m_loadedFiles ); } ++ ++ IpRange* find_range( uint32_t ip ) const; ++ ++ bool is_filtered( uint32_t ip ) const { return (find_range( ip ) != NULL); } ++ bool is_filtered( std::string ip ) const { ++ static std::pair ipInt = IpAddress::to_int( ip ); ++ return (!ipInt.first ? false : is_filtered( ipInt.second )); ++ } ++ ++ std::string to_string() const; ++ ++ int size(void) { return ( m_ranges ? m_ranges->size() : 0 ); } ++ int get_merges(void) { return m_merges; } ++ void set_files( str_list& files) { m_loadedFiles = new str_list( files ); } ++}; ++ ++} ++#endif +diff --git a/src/core/ip_filter_statics.cc b/src/core/ip_filter_statics.cc +new file mode 100644 +index 0000000..042377d +--- /dev/null ++++ b/src/core/ip_filter_statics.cc +@@ -0,0 +1,21 @@ ++#include "ip_address.h" ++#include "ip_range.h" ++#include "utils/pattern.h" ++ ++namespace core { ++ ++const std::string IpAddress::PATTERN_IP_EXPRESSION = "(([0-9]{1,3}\\.){3}[0-9]{1,3})"; ++const std::string IpAddress::PATTERN_IP_BYTES_EXPRESSION = "([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})"; ++const regex::Pattern IpAddress::PATTERN_IP_BYTES = PATTERN_IP_BYTES_EXPRESSION; ++ ++const int IpAddress::GRP_IP_FIRST_BYTE = 1; ++const int IpAddress::GRP_IP_BYTES_COUNT = 4; ++ ++const std::string IpRange::PATTERN_RANGE_EXPRESSION = "[[:space:]]*(.*)[[:space:]]*:[[:space:]]*" + IpAddress::PATTERN_IP_EXPRESSION + "[[:space:]]*-[[:space:]]*" + IpAddress::PATTERN_IP_EXPRESSION + "[[:space:]]*"; ++const regex::Pattern IpRange::PATTERN_RANGE = PATTERN_RANGE_EXPRESSION; ++ ++const int IpRange::GRP_DESCRIPTION = 1; ++const int IpRange::GRP_FIRST_IP = 2; ++const int IpRange::GRP_SECOND_IP = 4; ++ ++} +diff --git a/src/core/ip_range.cc b/src/core/ip_range.cc +new file mode 100644 +index 0000000..3923e53 +--- /dev/null ++++ b/src/core/ip_range.cc +@@ -0,0 +1,112 @@ ++#include ++#include ++ ++#include "ip_range.h" ++#include "utils/pattern.h" ++#include "regex_namespace.h" ++ ++namespace core { ++ ++IpRange* IpRange::parse( const std::string& s ) { ++ regex::Match m = PATTERN_RANGE.match( s ); ++ ++ if( !m.matches() ) { ++ std::cout << "!! range format is invalid: '" << s << "'" << std::endl; ++ return NULL; ++ } ++ ++ std::string description = m.group( GRP_DESCRIPTION ); ++ std::string ip1 = m.group( GRP_FIRST_IP ); ++ std::string ip2 = m.group( GRP_SECOND_IP ); ++ IpAddress* from = IpAddress::parse( ip1 ); ++ IpAddress* to = IpAddress::parse( ip2 ); ++ ++ if( !from ) { ++ std::cout << "!! from is invalid: description='" << description << ", ip1=" << ip1 << ", ip2=" << ip2 << std::endl; ++ return NULL; ++ } ++ if( !to ) { ++ std::cout << "!! to is invalid: description='" << description << ", ip1=" << ip1 << ", ip2=" << ip2 << std::endl; ++ return NULL; ++ } ++ ++// if( !from || !to || (*to < *from) ) ++// return NULL; ++ ++ IpRange* r = new IpRange(); ++ ++ r->m_description = description; ++ r->m_from = from; ++ r->m_to = to; ++ ++ if( to && from && (*to < *from) ) { ++ std::cout << "!! to < from: " << r->to_string() << std::endl; ++ delete r; ++ return NULL; ++ } ++ ++ return r; ++} ++ ++//fast version ++IpRange* IpRange::parse( const char *s, const int size ){ ++ static char description[256]; ++ static char ip1[24], ip2[24]; ++ int pos=0, post=0, enddesc=size-1; ++ while (enddesc>0 && s[enddesc]!=':') enddesc--; //find last ':' in the line ++ while((pos' '){ ++ if (post<23) ip2[post++]=s[pos]; ++ pos++; ++ } ++ ip2[post]=0; ++ } else ip2[0]=0; ++ ++ IpAddress* from = IpAddress::parse(ip1); ++ IpAddress* to = IpAddress::parse(ip2); ++ ++ if( !from ) { ++ std::cout << "!! from is invalid: description='" << description << ", ip1=" << ip1 << ", ip2=" << ip2 << std::endl; ++ return NULL; ++ } ++ if( !to ) { ++ std::cout << "!! to is invalid: description='" << description << ", ip1=" << ip1 << ", ip2=" << ip2 << std::endl; ++ return NULL; ++ } ++ ++ IpRange* r = new IpRange(); ++ r->m_description = description; ++ r->m_from = from; ++ r->m_to = to; ++ ++ if( (*to < *from) ) { ++ std::cout << "!! to < from: " << r->to_string() << std::endl; ++ delete r; ++ return NULL; ++ } ++ ++ return r; ++} ++ ++std::string IpRange::to_string() const { ++ std::stringstream result; ++ result << m_description << ": [" << m_from->to_string() << " - " << m_to->to_string() << ']'; ++ return result.str(); ++} ++ ++} +diff --git a/src/core/ip_range.h b/src/core/ip_range.h +new file mode 100644 +index 0000000..1fb2322 +--- /dev/null ++++ b/src/core/ip_range.h +@@ -0,0 +1,67 @@ ++#ifndef IPRANGE_H ++#define IPRANGE_H ++ ++#include ++ ++#include "printable.h" ++#include "ip_address.h" ++#include "utils/pattern.h" ++#include "regex_namespace.h" ++ ++namespace core { ++ ++class IpRange : public Printable { ++ public: // constants ++ static const std::string PATTERN_RANGE_EXPRESSION; ++ static const regex::Pattern PATTERN_RANGE; ++ ++ static const int GRP_DESCRIPTION; ++ static const int GRP_FIRST_IP; ++ static const int GRP_SECOND_IP; ++ ++ private: // fields ++ std::string m_description; ++ const IpAddress* m_from; ++ const IpAddress* m_to; ++ ++ private: // dynamic methods ++ IpRange() : m_description(), m_from(NULL), m_to(NULL) {} ++ ++ public: // static methods ++ typedef IpRange* ptr; ++ static IpRange* parse( const std::string& s ); ++ static IpRange* parse( const char *s, const int size ); ++ ++ public: // dynamic methods ++ IpRange( IpRange& rng ) { copy(rng); } ++ IpRange& operator= ( IpRange& rng ) { copy(rng); return *this; } ++ ++ void copy( IpRange& rng ) { ++ m_description = rng.m_description; ++ m_from = (!rng.m_from) ? NULL : new IpAddress( *rng.m_from ); ++ m_to = (!rng.m_to) ? NULL : new IpAddress( *rng.m_to ); ++ } ++ ++ const std::string& get_description ( void ) const { return m_description; } ++ const IpAddress* get_from ( void ) const { return m_from; } ++ const IpAddress* get_to ( void ) const { return m_to; } ++ ++ void set_description ( const std::string& description ) { m_description = description; } ++ void set_from ( const IpAddress* from ) { if( m_from ) delete m_from; m_from = new IpAddress( *from ); } ++ void set_to ( const IpAddress* to ) { if( m_to ) delete m_to; m_to = new IpAddress( *to ); } ++ ++ bool includes( const IpAddress& ip ) const { return includes((uint32_t)ip); } ++ bool includes( uint32_t ip ) const { return (*m_from <= ip) && (*m_to >= ip); } ++ ++ ~IpRange() { ++ delete m_from; ++ m_from = NULL; ++ delete m_to; ++ m_to = NULL; ++ } ++ ++ std::string to_string() const; ++}; ++ ++} ++#endif +diff --git a/src/core/manager.cc b/src/core/manager.cc +index 2a422c8..9c1d004 100644 +--- a/src/core/manager.cc ++++ b/src/core/manager.cc +@@ -153,6 +153,24 @@ Manager::handshake_log(const sockaddr* sa, int msg, int err, const torrent::Hash + } + } + ++uint32_t ++Manager::filter_ip(const sockaddr* sa) { ++ IpRange* r = NULL; ++ // if something's wrong with filter or address it's gonna be allowed ++ if( m_ipFilter && sa ) { ++ const rak::socket_address* socketAddress = rak::socket_address::cast_from(sa); ++ if( socketAddress->is_valid() && (socketAddress->family() == rak::socket_address::af_inet) ) ++ r = m_ipFilter->find_range( socketAddress->sa_inet()->address_h() ); ++ if( r ) ++ m_logComplete.push_front("Address '" + socketAddress->address_str() + "' is rejected by IP filter range '" + r->to_string()); ++ else ++ if( rpc::call_command_value("get_handshake_log") ) ++ m_logComplete.push_front("IP Filter allowed connection with '" + socketAddress->address_str() + "'"); ++ } ++ return (r==NULL); ++} ++ ++ + void + Manager::push_log(const char* msg) { + m_logImportant.push_front(msg); +@@ -160,7 +178,8 @@ Manager::push_log(const char* msg) { + } + + Manager::Manager() : +- m_hashingView(NULL) ++ m_hashingView(NULL), ++ m_ipFilter(NULL) + // m_pollManager(NULL) { + { + m_downloadStore = new DownloadStore(); +@@ -181,6 +200,8 @@ Manager::~Manager() { + delete m_downloadStore; + delete m_httpQueue; + delete m_fileStatusCache; ++ ++ set_ip_filter( NULL ); + } + + void +@@ -226,6 +247,7 @@ Manager::initialize_second() { + CurlStack::global_init(); + + torrent::connection_manager()->set_signal_handshake_log(sigc::mem_fun(this, &Manager::handshake_log)); ++ torrent::connection_manager()->set_filter(sigc::mem_fun(this, &Manager::filter_ip)); + } + + void +@@ -585,4 +607,13 @@ Manager::receive_hashing_changed() { + } + } + ++void Manager::reload_ip_filter(void) { ++ if( m_ipFilter ) { ++ push_log("Reloading IP filter"); ++ m_ipFilter->reload(); ++ std::stringstream logMsg("IpFilter reloaded with "); ++ logMsg << m_ipFilter->size() << " ranges total. " << m_ipFilter->get_merges() << " ranges were merged."; ++ push_log( logMsg.str().c_str() ); ++} ++} + } +diff --git a/src/core/manager.h b/src/core/manager.h +index 16902af..ac01981 100644 +--- a/src/core/manager.h ++++ b/src/core/manager.h +@@ -47,6 +47,8 @@ + #include "range_map.h" + #include "log.h" + ++#include "ip_filter.h" ++ + namespace torrent { + class Bencode; + } +@@ -118,6 +120,15 @@ public: + + void handshake_log(const sockaddr* sa, int msg, int err, const torrent::HashString* hash); + ++ uint32_t filter_ip(const sockaddr* sa); ++ ++ void set_ip_filter( IpFilter* ipFilter ) { ++ IpFilter* old = m_ipFilter; ++ m_ipFilter = ipFilter; ++ if( old ) delete old; ++ } ++ void reload_ip_filter(void); ++ + static const int create_start = 0x1; + static const int create_tied = 0x2; + static const int create_quiet = 0x4; +@@ -154,6 +165,8 @@ private: + + Log m_logImportant; + Log m_logComplete; ++ ++ IpFilter* m_ipFilter; + }; + + // Meh, cleanup. +diff --git a/src/core/printable.h b/src/core/printable.h +new file mode 100644 +index 0000000..8520af4 +--- /dev/null ++++ b/src/core/printable.h +@@ -0,0 +1,16 @@ ++#ifndef PRINTABLE_H ++#define PRINTABLE_H ++ ++#include ++ ++class Printable { ++ public: ++ virtual std::string to_string() const = 0; ++}; ++ ++template inline std::basic_ostream<_CharT,_Traits>& ++ operator<<( std::basic_ostream<_CharT,_Traits>& out, const Printable& val) { ++ return out << val.to_string(); ++} ++ ++#endif +diff --git a/src/core/regex_namespace.h b/src/core/regex_namespace.h +new file mode 100644 +index 0000000..6e10b3c +--- /dev/null ++++ b/src/core/regex_namespace.h +@@ -0,0 +1,6 @@ ++#ifndef REGEXNAMESPACE_H ++#define REGEXNAMESPACE_H ++ ++namespace regex = utils; ++ ++#endif +diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am +index 55ea9ff..cdad48a 100644 +--- a/src/utils/Makefile.am ++++ b/src/utils/Makefile.am +@@ -9,6 +9,8 @@ libsub_utils_a_SOURCES = \ + lockfile.cc \ + lockfile.h \ + socket_fd.cc \ +- socket_fd.h ++ socket_fd.h \ ++ pattern.cc \ ++ pattern.h + + INCLUDES = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) +diff --git a/src/utils/Makefile.in b/src/utils/Makefile.in +index a684a9c..5424965 100644 +--- a/src/utils/Makefile.in ++++ b/src/utils/Makefile.in +@@ -58,7 +58,7 @@ libsub_utils_a_AR = $(AR) $(ARFLAGS) + libsub_utils_a_LIBADD = + am_libsub_utils_a_OBJECTS = directory.$(OBJEXT) \ + file_status_cache.$(OBJEXT) lockfile.$(OBJEXT) \ +- socket_fd.$(OBJEXT) ++ socket_fd.$(OBJEXT) pattern.$(OBJEXT) + libsub_utils_a_OBJECTS = $(am_libsub_utils_a_OBJECTS) + DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) + depcomp = $(SHELL) $(top_srcdir)/depcomp +@@ -220,7 +220,9 @@ libsub_utils_a_SOURCES = \ + lockfile.cc \ + lockfile.h \ + socket_fd.cc \ +- socket_fd.h ++ socket_fd.h \ ++ pattern.cc \ ++ pattern.h + + INCLUDES = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir) + all: all-am +@@ -275,6 +277,7 @@ distclean-compile: + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file_status_cache.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lockfile.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket_fd.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pattern.@am__quote@ + + .cc.o: + @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +diff --git a/src/utils/pattern.cc b/src/utils/pattern.cc +new file mode 100644 +index 0000000..c44bb38 +--- /dev/null ++++ b/src/utils/pattern.cc +@@ -0,0 +1,79 @@ ++#include ++#include ++#include ++#include ++ ++#include "pattern.h" ++ ++namespace utils { ++ ++int Pattern::countGroups( const std::string& str ) { ++ int count1 = 0; ++ int count2 = 0; ++ ++ for( size_t index = -1; (index = str.find( '(', index+1 )) != std::string::npos; ) ++ count1++; ++ for( size_t index = -1; (index = str.find( ')', index+1 )) != std::string::npos; ) ++ count2++; ++ ++ return (count1 < count2) ? count1 : count2; ++} ++ ++Pattern::Pattern( const std::string& pattern, Flags flags ) : lastResult(-1), ++ preg(NULL) { ++ int regFlags = REG_EXTENDED | REG_ICASE | REG_NEWLINE; ++ if( !(flags & CASE_SENSITIVE) ) ++ regFlags ^= REG_ICASE; ++ if( (flags & DOT_MATCH_NEWLINE) ) ++ regFlags ^= REG_NEWLINE; ++ ++ preg = new regex_t; ++ numGroups = countGroups( pattern ) + 1; ++ ++ lastResult = regcomp( preg, pattern.c_str(), regFlags ); ++} ++ ++Pattern::~Pattern() { ++ regfree( preg ); ++ delete( preg ); ++} ++ ++std::string Pattern::getLastError() const { ++ char errBuf[1024]; ++ regerror( lastResult, preg, errBuf, sizeof(errBuf) ); ++ return std::string(errBuf); ++} ++ ++Match Pattern::match( const std::string& expression ) const { ++ ++ regmatch_t* pmatch = new regmatch_t[numGroups]; ++ int res = regexec( preg, expression.c_str(), numGroups, pmatch, 0 ); ++ return Match( expression, numGroups, pmatch, res, getLastError() ); ++} ++ ++Match::Match( const std::string& expr, int ngroups, regmatch_t* groups, int result, const std::string& message ) : ++ expression( expr ), ++ nmatch( ngroups ), ++ pmatch( groups ), ++ matchResult( result ), ++ matchMessage( message ) { ++} ++ ++std::string Match::group( int i ) { ++ if( (i >= nmatch) || (pmatch[i].rm_so < 0) ) ++ return ""; ++ ++ return expression.substr( pmatch[i].rm_so, pmatch[i].rm_eo - pmatch[i].rm_so ); ++} ++ ++std::string& trim( std::string& str ) { ++ std::string::iterator it; ++ for( it = str.begin(); (it < str.end()) && ( isspace(*it) || (*it == 0) ) ; it++ ); ++ str.erase( str.begin(), it ); ++ for( it = str.end()-1; (it >= str.begin()) && ( isspace(*it) || (*it == 0) ) ; it-- ); ++ str.erase( ++it, str.end() ); ++ return str; ++} ++ ++} ++ +diff --git a/src/utils/pattern.h b/src/utils/pattern.h +new file mode 100644 +index 0000000..99e95a4 +--- /dev/null ++++ b/src/utils/pattern.h +@@ -0,0 +1,59 @@ ++#ifndef PATTERN_H ++#define PATTERN_H ++ ++#include ++#include ++#include ++#include ++ ++namespace utils { ++ ++class Match { ++ public: ++ Match( const std::string& expr, int ngroups, regmatch_t* pmatch, int matchResult, const std::string& matchMessage ); ++ ~Match() { delete[] pmatch; } ++ std::string group( int i ); ++ bool found() { return (matchResult == 0); } ++ bool matches() { return found(); } ++ std::string& getMatchMessage() { return matchMessage; } ++ ++ private: ++ std::string expression; ++ int nmatch; ++ regmatch_t* pmatch; ++ int matchResult; ++ std::string matchMessage; ++}; ++ ++class Pattern { ++ public: ++ enum Flags { ++ DEFAULT = 0, // REG_EXTENDED | REG_ICASE | REG_NEWLINE ++ CASE_SENSITIVE, ++ DOT_MATCH_NEWLINE ++ }; ++ ++ public: ++ Pattern( const std::string& pattern, Flags f = Pattern::DEFAULT ); ++ ~Pattern(); ++ bool isSuccess() { return (lastResult == 0); } ++ std::string getLastError() const; ++ Match match( const std::string& expression ) const; ++ ++ private: ++ int countGroups( const std::string& str ); ++ ++ private: ++ regex_t* preg; ++ int lastResult; ++ int numGroups; ++}; ++ ++ ++std::string& trim( std::string& str ); ++ ++} ++ ++// end of ifdef PATTERN_H ++#endif ++ diff --git a/rtorrent-extended/ipv6.patch b/rtorrent-extended/ipv6.patch new file mode 100644 index 0000000..de72d94 --- /dev/null +++ b/rtorrent-extended/ipv6.patch @@ -0,0 +1,572 @@ +diff --git a/rak/socket_address.h b/rak/socket_address.h +index 25fdb37..d38533e 100644 +--- a/rak/socket_address.h ++++ b/rak/socket_address.h +@@ -145,7 +145,7 @@ private: + }; + }; + +-// Remeber to set the AF_INET. ++// Remember to set the AF_INET. + + class socket_address_inet { + public: +@@ -184,6 +184,10 @@ public: + + const sockaddr* c_sockaddr() const { return reinterpret_cast(&m_sockaddr); } + const sockaddr_in* c_sockaddr_inet() const { return &m_sockaddr; } ++ ++#ifdef RAK_USE_INET6 ++ socket_address_inet6 to_mapped_address() const; ++#endif + + bool operator == (const socket_address_inet& rhs) const; + bool operator < (const socket_address_inet& rhs) const; +@@ -192,6 +196,52 @@ private: + struct sockaddr_in m_sockaddr; + }; + ++#ifdef RAK_USE_INET6 ++// Remember to set the AF_INET6. ++ ++class socket_address_inet6 { ++public: ++ bool is_any() const { return is_port_any() && is_address_any(); } ++ bool is_valid() const { return !is_port_any() && !is_address_any(); } ++ bool is_port_any() const { return port() == 0; } ++ bool is_address_any() const { return std::memcmp(&m_sockaddr.sin6_addr, &in6addr_any, sizeof(in6_addr)) == 0; } ++ ++ void clear() { std::memset(this, 0, sizeof(socket_address_inet6)); set_family(); } ++ ++ uint16_t port() const { return ntohs(m_sockaddr.sin6_port); } ++ uint16_t port_n() const { return m_sockaddr.sin6_port; } ++ void set_port(uint16_t p) { m_sockaddr.sin6_port = htons(p); } ++ void set_port_n(uint16_t p) { m_sockaddr.sin6_port = p; } ++ ++ in6_addr address() const { return m_sockaddr.sin6_addr; } ++ std::string address_str() const; ++ bool address_c_str(char* buf, socklen_t size) const; ++ ++ void set_address(in6_addr a) { m_sockaddr.sin6_addr = a; } ++ bool set_address_str(const std::string& a) { return set_address_c_str(a.c_str()); } ++ bool set_address_c_str(const char* a); ++ ++ void set_address_any() { set_port(0); set_address(in6addr_any); } ++ ++ sa_family_t family() const { return m_sockaddr.sin6_family; } ++ void set_family() { m_sockaddr.sin6_family = AF_INET6; } ++ ++ sockaddr* c_sockaddr() { return reinterpret_cast(&m_sockaddr); } ++ sockaddr_in6* c_sockaddr_inet6() { return &m_sockaddr; } ++ ++ const sockaddr* c_sockaddr() const { return reinterpret_cast(&m_sockaddr); } ++ const sockaddr_in6* c_sockaddr_inet6() const { return &m_sockaddr; } ++ ++ socket_address normalize_address() const; ++ ++ bool operator == (const socket_address_inet6& rhs) const; ++ bool operator < (const socket_address_inet6& rhs) const; ++ ++private: ++ struct sockaddr_in6 m_sockaddr; ++}; ++#endif ++ + // Unique key for the address, excluding port numbers etc. + class socket_address_key { + public: +@@ -241,8 +291,10 @@ socket_address::is_valid() const { + switch (family()) { + case af_inet: + return sa_inet()->is_valid(); +-// case af_inet6: +-// return sa_inet6().is_valid(); ++#ifdef RAK_USE_INET6 ++ case af_inet6: ++ return sa_inet6()->is_valid(); ++#endif + default: + return false; + } +@@ -253,6 +305,10 @@ socket_address::is_bindable() const { + switch (family()) { + case af_inet: + return !sa_inet()->is_address_any(); ++#ifdef RAK_USE_INET6 ++ case af_inet6: ++ return !sa_inet6()->is_address_any(); ++#endif + default: + return false; + } +@@ -263,6 +319,10 @@ socket_address::is_address_any() const { + switch (family()) { + case af_inet: + return sa_inet()->is_address_any(); ++#ifdef RAK_USE_INET6 ++ case af_inet6: ++ return sa_inet6()->is_address_any(); ++#endif + default: + return true; + } +@@ -273,6 +333,10 @@ socket_address::port() const { + switch (family()) { + case af_inet: + return sa_inet()->port(); ++#ifdef RAK_USE_INET6 ++ case af_inet6: ++ return sa_inet6()->port(); ++#endif + default: + return 0; + } +@@ -283,6 +347,10 @@ socket_address::set_port(uint16_t p) { + switch (family()) { + case af_inet: + return sa_inet()->set_port(p); ++#ifdef RAK_USE_INET6 ++ case af_inet6: ++ return sa_inet6()->set_port(p); ++#endif + default: + break; + } +@@ -293,6 +361,10 @@ socket_address::address_str() const { + switch (family()) { + case af_inet: + return sa_inet()->address_str(); ++#ifdef RAK_USE_INET6 ++ case af_inet6: ++ return sa_inet6()->address_str(); ++#endif + default: + return std::string(); + } +@@ -303,6 +375,10 @@ socket_address::address_c_str(char* buf, socklen_t size) const { + switch (family()) { + case af_inet: + return sa_inet()->address_c_str(buf, size); ++#ifdef RAK_USE_INET6 ++ case af_inet6: ++ return sa_inet6()->address_c_str(buf, size); ++#endif + default: + return false; + } +@@ -314,6 +390,12 @@ socket_address::set_address_c_str(const char* a) { + sa_inet()->set_family(); + return true; + ++#ifdef RAK_USE_INET6 ++ } else if (sa_inet6()->set_address_c_str(a)) { ++ sa_inet6()->set_family(); ++ return true; ++#endif ++ + } else { + return false; + } +@@ -325,6 +407,10 @@ socket_address::length() const { + switch(family()) { + case af_inet: + return sizeof(sockaddr_in); ++#ifdef RAK_USE_INET6 ++ case af_inet6: ++ return sizeof(sockaddr_in6); ++#endif + default: + return 0; + } +@@ -349,8 +435,10 @@ socket_address::operator == (const socket_address& rhs) const { + switch (family()) { + case af_inet: + return *sa_inet() == *rhs.sa_inet(); +-// case af_inet6: +-// return *sa_inet6() == *rhs.sa_inet6(); ++#ifdef RAK_USE_INET6 ++ case af_inet6: ++ return *sa_inet6() == *rhs.sa_inet6(); ++#endif + default: + throw std::logic_error("socket_address::operator == (rhs) invalid type comparison."); + } +@@ -364,8 +452,10 @@ socket_address::operator < (const socket_address& rhs) const { + switch (family()) { + case af_inet: + return *sa_inet() < *rhs.sa_inet(); +-// case af_inet6: +-// return *sa_inet6() < *rhs.sa_inet6(); ++#ifdef RAK_USE_INET6 ++ case af_inet6: ++ return *sa_inet6() < *rhs.sa_inet6(); ++#endif + default: + throw std::logic_error("socket_address::operator < (rhs) invalid type comparison."); + } +@@ -391,6 +481,23 @@ socket_address_inet::set_address_c_str(const char* a) { + return inet_pton(AF_INET, a, &m_sockaddr.sin_addr); + } + ++#ifdef RAK_USE_INET6 ++inline socket_address_inet6 ++socket_address_inet::to_mapped_address() const { ++ uint32_t addr32[4]; ++ addr32[0] = 0; ++ addr32[1] = 0; ++ addr32[2] = htonl(0xffff); ++ addr32[3] = m_sockaddr.sin_addr.s_addr; ++ ++ socket_address_inet6 sa; ++ sa.clear(); ++ sa.set_address(*reinterpret_cast(addr32)); ++ sa.set_port_n(m_sockaddr.sin_port); ++ return sa; ++} ++#endif ++ + inline bool + socket_address_inet::operator == (const socket_address_inet& rhs) const { + return +@@ -406,6 +513,59 @@ socket_address_inet::operator < (const socket_address_inet& rhs) const { + m_sockaddr.sin_port < rhs.m_sockaddr.sin_port); + } + ++#ifdef RAK_USE_INET6 ++ ++inline std::string ++socket_address_inet6::address_str() const { ++ char buf[INET6_ADDRSTRLEN]; ++ ++ if (!address_c_str(buf, INET6_ADDRSTRLEN)) ++ return std::string(); ++ ++ return std::string(buf); ++} ++ ++inline bool ++socket_address_inet6::address_c_str(char* buf, socklen_t size) const { ++ return inet_ntop(family(), &m_sockaddr.sin6_addr, buf, size); ++} ++ ++inline bool ++socket_address_inet6::set_address_c_str(const char* a) { ++ return inet_pton(AF_INET6, a, &m_sockaddr.sin6_addr); ++} ++ ++inline socket_address ++socket_address_inet6::normalize_address() const { ++ const uint32_t *addr32 = reinterpret_cast(m_sockaddr.sin6_addr.s6_addr); ++ if (addr32[0] == 0 && addr32[1] == 0 && addr32[2] == htonl(0xffff)) { ++ socket_address addr4; ++ addr4.sa_inet()->set_family(); ++ addr4.sa_inet()->set_address_n(addr32[3]); ++ addr4.sa_inet()->set_port_n(m_sockaddr.sin6_port); ++ return addr4; ++ } ++ return *reinterpret_cast(this); ++} ++ ++inline bool ++socket_address_inet6::operator == (const socket_address_inet6& rhs) const { ++ return ++ memcmp(&m_sockaddr.sin6_addr, &rhs.m_sockaddr.sin6_addr, sizeof(in6_addr)) == 0 && ++ m_sockaddr.sin6_port == rhs.m_sockaddr.sin6_port; ++} ++ ++inline bool ++socket_address_inet6::operator < (const socket_address_inet6& rhs) const { ++ int addr_comp = memcmp(&m_sockaddr.sin6_addr, &rhs.m_sockaddr.sin6_addr, sizeof(in6_addr)); ++ return ++ addr_comp < 0 || ++ (addr_comp == 0 || ++ m_sockaddr.sin6_port < rhs.m_sockaddr.sin6_port); ++} ++ ++#endif ++ + } + + #endif +diff --git a/src/command_peer.cc b/src/command_peer.cc +index 9708a8d..0aae8e0 100644 +--- a/src/command_peer.cc ++++ b/src/command_peer.cc +@@ -68,7 +68,13 @@ retrieve_p_id_html(torrent::Peer* peer) { + + torrent::Object + retrieve_p_address(torrent::Peer* peer) { +- return rak::socket_address::cast_from(peer->peer_info()->socket_address())->address_str(); ++ const rak::socket_address *addr = rak::socket_address::cast_from(peer->peer_info()->socket_address()); ++#ifdef RAK_USE_INET6 ++ if (addr->family() == rak::socket_address::af_inet6) ++ return "[" + addr->address_str() + "]"; ++ else ++#endif ++ return addr->address_str(); + } + + torrent::Object +diff --git a/src/core/curl_get.cc b/src/core/curl_get.cc +index f0767b7..4a904e1 100644 +--- a/src/core/curl_get.cc ++++ b/src/core/curl_get.cc +@@ -88,8 +88,20 @@ CurlGet::start() { + curl_easy_setopt(m_handle, CURLOPT_NOSIGNAL, (long)1); + curl_easy_setopt(m_handle, CURLOPT_FOLLOWLOCATION, (long)1); + curl_easy_setopt(m_handle, CURLOPT_MAXREDIRS, (long)5); ++ ++ // Even when IPv6-enabled, we don't want to use CURL_IPRESOLVE_WHATEVER, ++ // since that will usually prefer connecting over IPv6 to the tracker. ++ // Since it's usually a lot easier to find our global IPv6 address ++ // (if we have one) than our global IPv4 address, we prefer connecting ++ // over IPv4 if we can, so that the tracker will get our IPv4 address ++ // that way. If the resolve fails, CurlStack will call retry_ipv6() ++ // on us and we'll make a second attempt with CURL_IPRESOLVE_V6. + curl_easy_setopt(m_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); ++ + curl_easy_setopt(m_handle, CURLOPT_ENCODING, ""); ++#ifdef RAK_USE_INET6 ++ m_ipv6 = false; ++#endif + + m_stack->add_get(this); + } +@@ -107,6 +119,17 @@ CurlGet::close() { + m_handle = NULL; + } + ++#ifdef RAK_USE_INET6 ++void ++CurlGet::retry_ipv6() { ++ CURL* nhandle = curl_easy_duphandle(m_handle); ++ curl_easy_setopt(nhandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); ++ curl_easy_cleanup(m_handle); ++ m_handle = nhandle; ++ m_ipv6 = true; ++} ++#endif ++ + void + CurlGet::receive_timeout() { + return m_stack->transfer_done(m_handle, "Timed out"); +diff --git a/src/core/curl_get.h b/src/core/curl_get.h +index 1d3a0d5..2b7836b 100644 +--- a/src/core/curl_get.h ++++ b/src/core/curl_get.h +@@ -56,6 +56,10 @@ public: + + void start(); + void close(); ++#ifdef RAK_USE_INET6 ++ bool is_using_ipv6() { return m_ipv6; } ++ void retry_ipv6(); ++#endif + + bool is_busy() const { return m_handle; } + bool is_active() const { return m_active; } +@@ -74,6 +78,9 @@ private: + void receive_timeout(); + + bool m_active; ++#ifdef RAK_USE_INET6 ++ bool m_ipv6; ++#endif + + rak::priority_item m_taskTimeout; + +diff --git a/src/core/curl_stack.cc b/src/core/curl_stack.cc +index 3adfab5..915b11a 100644 +--- a/src/core/curl_stack.cc ++++ b/src/core/curl_stack.cc +@@ -111,6 +111,21 @@ CurlStack::receive_action(CurlSocket* socket, int events) { + if (msg->msg != CURLMSG_DONE) + throw torrent::internal_error("CurlStack::receive_action() msg->msg != CURLMSG_DONE."); + ++#ifdef RAK_USE_INET6 ++ if (msg->data.result == CURLE_COULDNT_RESOLVE_HOST) { ++ iterator itr = std::find_if(begin(), end(), rak::equal(msg->easy_handle, std::mem_fun(&CurlGet::handle))); ++ ++ if (itr == end()) ++ throw torrent::internal_error("Could not find CurlGet when calling CurlStack::receive_action."); ++ ++ if (!(*itr)->is_using_ipv6()) { ++ (*itr)->retry_ipv6(); ++ if (curl_multi_add_handle((CURLM*)m_handle, (*itr)->handle()) > 0) ++ throw torrent::internal_error("Error calling curl_multi_add_handle."); ++ continue; ++ } ++ } else ++#endif + transfer_done(msg->easy_handle, msg->data.result == CURLE_OK ? NULL : curl_easy_strerror(msg->data.result)); + } + +diff --git a/src/display/window_peer_list.cc b/src/display/window_peer_list.cc +index 628b74b..63a22ef 100644 +--- a/src/display/window_peer_list.cc ++++ b/src/display/window_peer_list.cc +@@ -68,7 +68,11 @@ WindowPeerList::redraw() { + int x = 2; + int y = 0; + +- m_canvas->print(x, y, "IP"); x += 16; ++#ifdef RAK_USE_INET6 ++ m_canvas->print(x, y, "IP"); x += 25; ++#else ++ m_canvas->print(x, y, "IP"); x += 16; ++#endif + m_canvas->print(x, y, "UP"); x += 7; + m_canvas->print(x, y, "DOWN"); x += 7; + m_canvas->print(x, y, "PEER"); x += 7; +@@ -99,10 +103,21 @@ WindowPeerList::redraw() { + + x = 0; + ++ std::string ip_address = rak::socket_address::cast_from(p->address())->address_str(); ++#ifdef RAK_USE_INET6 ++ if (ip_address.size() >= 24) { ++ ip_address.replace(ip_address.begin() + 21, ip_address.end(), "..."); ++ } ++#endif ++ + m_canvas->print(x, y, "%c %s", + range.first == *m_focus ? '*' : ' ', +- rak::socket_address::cast_from(p->address())->address_str().c_str()); ++ ip_address.c_str()); ++#ifdef RAK_USE_INET6 ++ x += 27; ++#else + x += 18; ++#endif + + m_canvas->print(x, y, "%.1f", (double)p->up_rate()->rate() / 1024); x += 7; + m_canvas->print(x, y, "%.1f", (double)p->down_rate()->rate() / 1024); x += 7; +diff --git a/src/utils/socket_fd.cc b/src/utils/socket_fd.cc +index 414d4f7..2545304 100644 +--- a/src/utils/socket_fd.cc ++++ b/src/utils/socket_fd.cc +@@ -71,6 +71,11 @@ SocketFd::set_priority(priority_type p) { + check_valid(); + int opt = p; + ++#ifdef RAK_USE_INET6 ++ if (m_ipv6_socket) ++ return setsockopt(m_fd, IPPROTO_IPV6, IPV6_TCLASS, &opt, sizeof(opt)) == 0; ++ else ++#endif + return setsockopt(m_fd, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)) == 0; + } + +@@ -130,12 +135,36 @@ SocketFd::get_error() const { + + bool + SocketFd::open_stream() { ++#ifdef RAK_USE_INET6 ++ m_fd = socket(rak::socket_address::pf_inet6, SOCK_STREAM, IPPROTO_TCP); ++ if (m_fd == -1) { ++ m_ipv6_socket = false; ++ return (m_fd = socket(rak::socket_address::pf_inet, SOCK_STREAM, IPPROTO_TCP)) != -1; ++ } ++ m_ipv6_socket = true; ++ ++ int zero = 0; ++ return setsockopt(m_fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) != -1; ++#else + return (m_fd = socket(rak::socket_address::pf_inet, SOCK_STREAM, IPPROTO_TCP)) != -1; ++#endif + } + + bool + SocketFd::open_datagram() { ++#ifdef RAK_USE_INET6 ++ m_fd = socket(rak::socket_address::pf_inet6, SOCK_DGRAM, 0); ++ if (m_fd == -1) { ++ m_ipv6_socket = false; ++ return (m_fd = socket(rak::socket_address::pf_inet, SOCK_DGRAM, 0)) != -1; ++ } ++ m_ipv6_socket = true; ++ ++ int zero = 0; ++ return setsockopt(m_fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) != -1; ++#else + return (m_fd = socket(rak::socket_address::pf_inet, SOCK_DGRAM, 0)) != -1; ++#endif + } + + bool +@@ -167,10 +196,34 @@ bool + SocketFd::connect(const rak::socket_address& sa) { + check_valid(); + ++#ifdef RAK_USE_INET6 ++ if (m_ipv6_socket && sa.family() == rak::socket_address::pf_inet) { ++ rak::socket_address_inet6 sa_mapped = sa.sa_inet()->to_mapped_address(); ++ return !::connect(m_fd, sa_mapped.c_sockaddr(), sizeof(sa_mapped)) || errno == EINPROGRESS; ++ } ++#endif + return !::connect(m_fd, sa.c_sockaddr(), sa.length()) || errno == EINPROGRESS; + } + + bool ++SocketFd::getsockname(rak::socket_address *sa) { ++ check_valid(); ++ ++ socklen_t len = sizeof(rak::socket_address); ++ if (::getsockname(m_fd, sa->c_sockaddr(), &len)) { ++ return false; ++ } ++ ++#ifdef RAK_USE_INET6 ++ if (m_ipv6_socket && sa->family() == rak::socket_address::af_inet6) { ++ *sa = sa->sa_inet6()->normalize_address(); ++ } ++#endif ++ ++ return true; ++} ++ ++bool + SocketFd::listen(int size) { + check_valid(); + +@@ -182,7 +235,18 @@ SocketFd::accept(rak::socket_address* sa) { + check_valid(); + socklen_t len = sizeof(rak::socket_address); + ++#ifdef RAK_USE_INET6 ++ if (sa == NULL) { ++ return SocketFd(::accept(m_fd, NULL, &len)); ++ } ++ int fd = ::accept(m_fd, sa->c_sockaddr(), &len); ++ if (fd != -1 && m_ipv6_socket && sa->family() == rak::socket_address::af_inet6) { ++ *sa = sa->sa_inet6()->normalize_address(); ++ } ++ return SocketFd(fd); ++#else + return SocketFd(::accept(m_fd, sa != NULL ? sa->c_sockaddr() : NULL, &len)); ++#endif + } + + // unsigned int +diff --git a/src/utils/socket_fd.h b/src/utils/socket_fd.h +index 7898712..35d2eda 100644 +--- a/src/utils/socket_fd.h ++++ b/src/utils/socket_fd.h +@@ -80,6 +80,7 @@ public: + bool bind(const rak::socket_address& sa); + bool bind(const rak::socket_address& sa, unsigned int length); + bool connect(const rak::socket_address& sa); ++ bool getsockname(rak::socket_address* sa); + + bool listen(int size); + SocketFd accept(rak::socket_address* sa); +@@ -91,6 +92,9 @@ private: + inline void check_valid() const; + + int m_fd; ++#ifdef RAK_USE_INET6 ++ bool m_ipv6_socket; ++#endif + }; + + } diff --git a/rtorrent-extended/karabaja_mod.patch b/rtorrent-extended/karabaja_mod.patch new file mode 100644 index 0000000..e309fff --- /dev/null +++ b/rtorrent-extended/karabaja_mod.patch @@ -0,0 +1,438 @@ +diff --git a/src/display/canvas.cc b/src/display/canvas.cc +index 4e621df..35244eb 100644 +--- a/src/display/canvas.cc ++++ b/src/display/canvas.cc +@@ -92,6 +92,14 @@ Canvas::initialize() { + m_isInitialized = true; + + initscr(); ++ ++ //colors ++ start_color(); ++ use_default_colors(); ++ init_pair(1, COLOR_RED, -1); ++ init_pair(2, COLOR_YELLOW, -1); ++ init_pair(3, COLOR_GREEN, -1); ++ + raw(); + noecho(); + nodelay(stdscr, TRUE); +diff --git a/src/display/utils.cc b/src/display/utils.cc +index d2f1af4..400b940 100644 +--- a/src/display/utils.cc ++++ b/src/display/utils.cc +@@ -34,6 +34,9 @@ + // Skomakerveien 33 + // 3185 Skoppum, NORWAY + ++// interface modifications by karabaja4 ++// ++ + #include "config.h" + + #include +@@ -52,6 +55,10 @@ + #include + #include + ++//peers ++#include ++#include ++ + #include "core/download.h" + #include "core/manager.h" + #include "rpc/parse_commands.h" +@@ -96,9 +103,9 @@ print_hhmmss_local(char* first, char* last, time_t t) { + char* + print_ddhhmm(char* first, char* last, time_t t) { + if (t / (24 * 3600) < 100) +- return print_buffer(first, last, "%2id %2i:%02i", (int)t / (24 * 3600), ((int)t / 3600) % 24, ((int)t / 60) % 60); ++ return print_buffer(first, last, "%id:%ih:%im", (int)t / (24 * 3600), ((int)t / 3600) % 24, ((int)t / 60) % 60); + else +- return print_buffer(first, last, "--d --:--"); ++ return print_buffer(first, last, "--d:--h:--m"); + } + + char* +@@ -127,91 +134,121 @@ print_address(char* first, char* last, const sockaddr* sa) { + + char* + print_download_title(char* first, char* last, core::Download* d) { +- return print_buffer(first, last, " %s", d->download()->name().c_str()); ++ ++ first = print_buffer(first, last, " %s", d->download()->name().c_str()); ++ ++ if (first > last) ++ throw torrent::internal_error("print_download_status(...) wrote past end of the buffer."); ++ ++ return first; ++ + } + + char* +-print_download_info(char* first, char* last, core::Download* d) { +- if (!d->download()->is_open()) +- first = print_buffer(first, last, "[CLOSED] "); +- else if (!d->download()->is_active()) +- first = print_buffer(first, last, "[OPEN] "); +- else +- first = print_buffer(first, last, " "); ++print_download_title_extra(char* first, char* last, core::Download* d) { ++ ++ if (d->is_hash_checking()) { ++ first = print_buffer(first, last, " | Checking hash [%2i%%]", ++ (d->download()->chunks_hashed() * 100) / d->download()->file_list()->size_chunks()); ++ } ++ else if (d->tracker_list()->has_active() && d->tracker_list()->focus() < d->tracker_list()->end()) { ++ ++ torrent::TrackerList* tl = d->tracker_list(); ++ char status[128]; ++ ++ (*tl->focus())->get_status(status, sizeof(status)); ++ first = print_buffer(first, last, " | Tracker[%i:%i]: Connecting to %s %s", ++ (*tl->focus())->group(), tl->focus_index(), (*tl->focus())->url().c_str(), status); ++ } ++ else if (!d->message().empty()) { ++ first = print_buffer(first, last, " | %s", d->message().c_str()); ++ } ++ else { ++ *first = '\0'; ++ } ++ ++ if (first > last) ++ throw torrent::internal_error("print_download_status(...) wrote past end of the buffer."); ++ ++ return first; ++ ++} + +- if (d->is_done()) +- first = print_buffer(first, last, "done %10.1f MB", (double)d->download()->file_list()->size_bytes() / (double)(1 << 20)); +- else +- first = print_buffer(first, last, "%6.1f / %6.1f MB", +- (double)d->download()->bytes_done() / (double)(1 << 20), +- (double)d->download()->file_list()->size_bytes() / (double)(1 << 20)); +- +- first = print_buffer(first, last, " Rate: %5.1f / %5.1f KB Uploaded: %7.1f MB", +- (double)d->download()->up_rate()->rate() / (1 << 10), +- (double)d->download()->down_rate()->rate() / (1 << 10), +- (double)d->download()->up_rate()->total() / (1 << 20)); +- +- if (d->download()->is_active() && !d->is_done()) { +- first = print_buffer(first, last, " "); +- first = print_download_percentage_done(first, last, d); +- +- first = print_buffer(first, last, " "); +- first = print_download_time_left(first, last, d); +- } else { +- first = print_buffer(first, last, " "); +- } ++char* ++print_download_info(char* first, char* last, core::Download* d) { ++ ++ if (!d->download()->is_open()) { ++ first = print_buffer(first, last, " CLOSED |"); ++ } ++ else if (!d->download()->is_active()) { ++ first = print_buffer(first, last, " PAUSED |"); ++ } ++ else { ++ first = print_buffer(first, last, " ACTIVE |"); ++ } ++ ++ if (d->is_done()) { ++ first = print_buffer(first, last, " finished %.1f MB [100%%] |", (double)d->download()->file_list()->size_bytes() / (double)(1 << 20)); ++ } ++ else { ++ first = print_buffer(first, last, " %.1f / %.1f MB [%i%%] |", ++ (double)d->download()->bytes_done() / (double)(1 << 20), ++ (double)d->download()->file_list()->size_bytes() / (double)(1 << 20), ++ (int)(((double)d->download()->bytes_done() / (double)d->download()->file_list()->size_bytes()) * 100)); ++ } ++ ++ //speed ++ first = print_buffer(first, last, " Speed: %.1f / %.1f KB", ++ (double)d->download()->down_rate()->rate() / (1 << 10), ++ (double)d->download()->up_rate()->rate() / (1 << 10)); ++ ++ if (d->download()->is_active() && !d->is_done()) { ++ ++ //peers ++ first = print_buffer(first, last, " | Peers: %i(%i)", ++ (int)d->download()->connection_list()->size(), ++ (int)d->download()->peer_list()->available_list_size()); ++ ++ //eta ++ first = print_buffer(first, last, " | ETA: "); ++ first = print_download_time_left(first, last, d); ++ ++ } ++ ++ if (first > last) ++ throw torrent::internal_error("print_download_info(...) wrote past end of the buffer."); ++ ++ return first; ++} + +- first = print_buffer(first, last, " [%c%c R: %4.2f", +- rpc::call_command_string("d.get_tied_to_file", rpc::make_target(d)).empty() ? ' ' : 'T', +- rpc::call_command_value("d.get_ignore_commands", rpc::make_target(d)) == 0 ? ' ' : 'I', +- (double)rpc::call_command_value("d.get_ratio", rpc::make_target(d)) / 1000.0); ++char* ++print_download_info_extra(char* first, char* last, core::Download* d) { ++ ++ first = print_buffer(first, last, "[%c%c R: %4.2f", ++ rpc::call_command_string("d.get_tied_to_file", rpc::make_target(d)).empty() ? ' ' : 'T', ++ rpc::call_command_value("d.get_ignore_commands", rpc::make_target(d)) == 0 ? ' ' : 'I', ++ (double)rpc::call_command_value("d.get_ratio", rpc::make_target(d)) / 1000.0); + +- if (d->priority() != 2) +- first = print_buffer(first, last, " %s", rpc::call_command_string("d.get_priority_str", rpc::make_target(d)).c_str()); ++ if (d->priority() != 2) ++ first = print_buffer(first, last, " %s", rpc::call_command_string("d.get_priority_str", rpc::make_target(d)).c_str()); + +- if (!d->bencode()->get_key("rtorrent").get_key_string("throttle_name").empty()) +- first = print_buffer(first, last , " %s", rpc::call_command_string("d.get_throttle_name", rpc::make_target(d)).c_str()); ++ if (!d->bencode()->get_key("rtorrent").get_key_string("throttle_name").empty()) ++ first = print_buffer(first, last , " %s", rpc::call_command_string("d.get_throttle_name", rpc::make_target(d)).c_str()); + +- first = print_buffer(first, last , "]"); ++ first = print_buffer(first, last , "]"); + +- if (first > last) +- throw torrent::internal_error("print_download_info(...) wrote past end of the buffer."); ++ if (first > last) ++ throw torrent::internal_error("print_download_info(...) wrote past end of the buffer."); + +- return first; ++ return first; + } + + char* + print_download_status(char* first, char* last, core::Download* d) { +- if (d->is_active()) +- ; +- else if (rpc::call_command_value("d.get_hashing", rpc::make_target(d)) != 0) +- first = print_buffer(first, last, "Hashing: "); +- else if (!d->is_active()) +- first = print_buffer(first, last, "Inactive: "); +- +- if (d->is_hash_checking()) { +- first = print_buffer(first, last, "Checking hash [%2i%%]", +- (d->download()->chunks_hashed() * 100) / d->download()->file_list()->size_chunks()); +- +- } else if (d->tracker_list()->has_active() && d->tracker_list()->focus() < d->tracker_list()->end()) { +- torrent::TrackerList* tl = d->tracker_list(); +- char status[128]; +- +- (*tl->focus())->get_status(status, sizeof(status)); +- first = print_buffer(first, last, "Tracker[%i:%i]: Connecting to %s %s", +- (*tl->focus())->group(), tl->focus_index(), (*tl->focus())->url().c_str(), status); +- +- } else if (!d->message().empty()) { +- first = print_buffer(first, last, "%s", d->message().c_str()); +- +- } else { +- *first = '\0'; +- } +- +- if (first > last) +- throw torrent::internal_error("print_download_status(...) wrote past end of the buffer."); + ++ *first = '\0'; + return first; ++ + } + + char* +@@ -219,7 +256,7 @@ print_download_time_left(char* first, char* last, core::Download* d) { + uint32_t rate = d->download()->down_rate()->rate(); + + if (rate < 512) +- return print_buffer(first, last, "--d --:--"); ++ return print_buffer(first, last, "--d:--h:--m"); + + time_t remaining = (d->download()->file_list()->size_bytes() - d->download()->bytes_done()) / (rate & ~(uint32_t)(512 - 1)); + +@@ -230,9 +267,9 @@ char* + print_download_percentage_done(char* first, char* last, core::Download* d) { + if (!d->is_open() || d->is_done()) + //return print_buffer(first, last, "[--%%]"); +- return print_buffer(first, last, " "); ++ return print_buffer(first, last, " "); + else +- return print_buffer(first, last, "[%2u%%]", (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks()); ++ return print_buffer(first, last, "%u%%", (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks()); + } + + char* +@@ -255,19 +292,19 @@ print_client_version(char* first, char* last, const torrent::ClientInfo& clientI + + char* + print_status_info(char* first, char* last) { +- if (!torrent::up_throttle_global()->is_throttled()) ++ if (!torrent::down_throttle_global()->is_throttled()) + first = print_buffer(first, last, "[Throttle off"); + else +- first = print_buffer(first, last, "[Throttle %3i", torrent::up_throttle_global()->max_rate() / 1024); ++ first = print_buffer(first, last, "[Throttle %3i", torrent::down_throttle_global()->max_rate() / 1024); + +- if (!torrent::down_throttle_global()->is_throttled()) ++ if (!torrent::up_throttle_global()->is_throttled()) + first = print_buffer(first, last, "/off KB]"); + else +- first = print_buffer(first, last, "/%3i KB]", torrent::down_throttle_global()->max_rate() / 1024); ++ first = print_buffer(first, last, "/%3i KB]", torrent::up_throttle_global()->max_rate() / 1024); + + first = print_buffer(first, last, " [Rate %5.1f/%5.1f KB]", +- (double)torrent::up_rate()->rate() / 1024.0, +- (double)torrent::down_rate()->rate() / 1024.0); ++ (double)torrent::down_rate()->rate() / 1024.0, ++ (double)torrent::up_rate()->rate() / 1024.0); + + first = print_buffer(first, last, " [Port: %i]", (unsigned int)torrent::connection_manager()->listen_port()); + +diff --git a/src/display/utils.h b/src/display/utils.h +index 3d69021..c9145dd 100644 +--- a/src/display/utils.h ++++ b/src/display/utils.h +@@ -66,7 +66,9 @@ char* print_ddhhmm(char* first, char* last, time_t t); + char* print_ddmmyyyy(char* first, char* last, time_t t); + + char* print_download_title(char* first, char* last, core::Download* d); ++char* print_download_title_extra(char* first, char* last, core::Download* d); + char* print_download_info(char* first, char* last, core::Download* d); ++char* print_download_info_extra(char* first, char* last, core::Download* d); + char* print_download_status(char* first, char* last, core::Download* d); + char* print_download_time_left(char* first, char* last, core::Download* d); + char* print_download_percentage_done(char* first, char* last, core::Download* d); +diff --git a/src/display/window_download_list.cc b/src/display/window_download_list.cc +index 71efec0..353f6b5 100644 +--- a/src/display/window_download_list.cc ++++ b/src/display/window_download_list.cc +@@ -34,6 +34,9 @@ + // Skomakerveien 33 + // 3185 Skoppum, NORWAY + ++// interface modifications by karabaja4 ++// ++ + #include "config.h" + + #include +@@ -81,30 +84,90 @@ WindowDownloadList::redraw() { + Range range = rak::advance_bidirectional(m_view->begin_visible(), + m_view->focus() != m_view->end_visible() ? m_view->focus() : m_view->begin_visible(), + m_view->end_visible(), +- m_canvas->height() / 3); ++ (m_canvas->height() - 1) / 3); + + // Make sure we properly fill out the last lines so it looks like + // there are more torrents, yet don't hide it if we got the last one + // in focus. + if (range.second != m_view->end_visible()) +- ++range.second; ++ ++range.second; + +- int pos = 1; ++ int pos = 2; + + while (range.first != range.second) { + char buffer[m_canvas->width() + 1]; + char* position; + char* last = buffer + m_canvas->width() - 2 + 1; +- ++ int title_length; ++ ++ //1 = red ++ //2 = yellow ++ //3 = green ++ ++ //do not print on last lines if cannot show whole torrent ++ if (pos >= (m_canvas->height() - 1)) ++ break; ++ ++ //print title + position = print_download_title(buffer, last, *range.first); +- m_canvas->print(0, pos++, "%c %s", range.first == m_view->focus() ? '*' : ' ', buffer); ++ title_length = strlen(buffer); ++ m_canvas->print(0, pos, "%c %s", range.first == m_view->focus() ? '*' : ' ', buffer); ++ ++ //title color ++ if ((*range.first)->is_done()) { ++ //finished ++ m_canvas->set_attr(3, pos, (title_length - 1), A_NORMAL, 3); ++ } ++ else { ++ //not finished ++ m_canvas->set_attr(3, pos, (title_length - 1), A_NORMAL, 2); ++ } ++ ++ //print title extra ++ position = print_download_title_extra(buffer, last, *range.first); ++ ++ //do not let title extra get off screen ++ buffer[m_canvas->width() - title_length - 2] = '\0'; + ++ m_canvas->print((title_length + 2), pos++, "%s", buffer); ++ ++ //print info + position = print_download_info(buffer, last, *range.first); +- m_canvas->print(0, pos++, "%c %s", range.first == m_view->focus() ? '*' : ' ', buffer); +- +- position = print_download_status(buffer, last, *range.first); +- m_canvas->print(0, pos++, "%c %s", range.first == m_view->focus() ? '*' : ' ', buffer); +- ++ m_canvas->print(0, pos, "%c %s", range.first == m_view->focus() ? '*' : ' ', buffer); ++ ++ //info color ++ if (!(*range.first)->download()->is_open()) { ++ //closed ++ m_canvas->set_attr(3, pos, 6, A_NORMAL, 1); ++ } ++ else if (!(*range.first)->download()->is_active()) { ++ //paused ++ m_canvas->set_attr(3, pos, 6, A_NORMAL, 2); ++ } ++ else { ++ //active ++ m_canvas->set_attr(3, pos, 6, A_NORMAL, 3); ++ } ++ ++ if ((*range.first)->is_done()) { ++ //finished ++ m_canvas->set_attr(12, pos, 8, A_NORMAL, 3); ++ } ++ ++ //do not print info extra if it collides with info ++ if ((strlen(buffer) + 2) <= (m_canvas->width() - 16)) { ++ ++ //print info extra ++ position = print_download_info_extra(buffer, last, *range.first); ++ m_canvas->print((m_canvas->width() - 16), pos++, "%s", buffer); ++ ++ } ++ else { ++ pos++; ++ } ++ ++ //skip one line ++ pos++; + ++range.first; + } + } +diff --git a/src/display/window_title.cc b/src/display/window_title.cc +index 300f655..c0e7fe5 100644 +--- a/src/display/window_title.cc ++++ b/src/display/window_title.cc +@@ -48,6 +48,10 @@ WindowTitle::redraw() { + + m_canvas->print(std::max(0, ((int)m_canvas->width() - (int)m_title.size()) / 2 - 4), 0, + "*** %s ***", m_title.c_str()); ++ ++ //set color in title ++ m_canvas->set_attr((((int)m_canvas->width() - (int)m_title.size()) / 2 - 4), 0, 3, A_NORMAL, 2); ++ m_canvas->set_attr( ((((int)m_canvas->width() - (int)m_title.size()) / 2) + (int)m_title.size() + 1), 0, 3, A_NORMAL, 2); + } + + } diff --git a/rtorrent-extended/magnet_uri.patch b/rtorrent-extended/magnet_uri.patch new file mode 100644 index 0000000..32d45f0 --- /dev/null +++ b/rtorrent-extended/magnet_uri.patch @@ -0,0 +1,228 @@ +diff --git a/src/core/download_factory.cc b/src/core/download_factory.cc +index e2d8ee8..49ac3fa 100644 +--- a/src/core/download_factory.cc ++++ b/src/core/download_factory.cc +@@ -69,6 +69,12 @@ is_network_uri(const std::string& uri) { + std::strncmp(uri.c_str(), "ftp://", 6) == 0; + } + ++bool ++is_magnet_uri(const std::string& uri) { ++ return ++ std::strncmp(uri.c_str(), "magnet:?", 8) == 0; ++} ++ + DownloadFactory::DownloadFactory(Manager* m) : + m_manager(m), + m_stream(NULL), +@@ -133,6 +139,13 @@ DownloadFactory::receive_load() { + + m_variables["tied_to_file"] = (int64_t)false; + ++ } else if (is_magnet_uri(m_uri)) { ++ m_stream = new std::stringstream(); ++ *m_stream << "d10:magnet-uri" << m_uri.length() << ":" << m_uri << "e"; ++ ++ m_variables["tied_to_file"] = (int64_t)false; ++ receive_loaded(); ++ + } else { + std::fstream* stream = new std::fstream(rak::path_expand(m_uri).c_str(), std::ios::in | std::ios::binary); + m_stream = stream; +@@ -177,6 +190,16 @@ DownloadFactory::receive_success() { + + torrent::Object* root = download->bencode(); + ++ if (download->download()->is_meta_download()) { ++ torrent::Object& meta = root->insert_key("rtorrent_meta_download", torrent::Object::create_map()); ++ meta.insert_key("start", m_start); ++ meta.insert_key("print_log", m_printLog); ++ ++ torrent::Object::list_type& commands = meta.insert_key("commands", torrent::Object::create_list()).as_list(); ++ for (command_list_type::iterator itr = m_commands.begin(); itr != m_commands.end(); ++itr) ++ commands.push_back(*itr); ++ } ++ + if (!m_session) { + // We only allow session torrents to keep their + // 'rtorrent/libtorrent' sections. The "fast_resume" section +@@ -229,7 +252,7 @@ DownloadFactory::receive_success() { + rpc::call_command("d.set_directory_base", rtorrent->get_key("directory"), rpc::make_target(download)); + + if (!m_session && m_variables["tied_to_file"].as_value()) +- rpc::call_command("d.set_tied_to_file", m_uri, rpc::make_target(download)); ++ rpc::call_command("d.set_tied_to_file", m_uri.empty() ? m_variables["tied_file"] : m_uri, rpc::make_target(download)); + + rpc::call_command("d.set_peer_exchange", rpc::call_command_value("get_peer_exchange"), rpc::make_target(download)); + +diff --git a/src/core/download_factory.h b/src/core/download_factory.h +index 045c9dc..3cc9622 100644 +--- a/src/core/download_factory.h ++++ b/src/core/download_factory.h +@@ -112,6 +112,7 @@ private: + }; + + bool is_network_uri(const std::string& uri); ++bool is_magnet_uri(const std::string& uri); + + } + +diff --git a/src/core/download_list.cc b/src/core/download_list.cc +index 551f873..13df725 100644 +--- a/src/core/download_list.cc ++++ b/src/core/download_list.cc +@@ -37,10 +37,12 @@ + #include "config.h" + + #include ++#include + #include + #include + #include + #include ++#include + #include + #include + #include +@@ -452,6 +454,9 @@ DownloadList::hash_done(Download* download) { + int64_t hashing = rpc::call_command_value("d.get_hashing", rpc::make_target(download)); + rpc::call_command_set_value("d.set_hashing", Download::variable_hashing_stopped, rpc::make_target(download)); + ++ if (download->is_done() && download->download()->is_meta_download()) ++ return process_meta_download(download); ++ + switch (hashing) { + case Download::variable_hashing_initial: + case Download::variable_hashing_rehash: +@@ -543,6 +548,9 @@ void + DownloadList::confirm_finished(Download* download) { + check_contains(download); + ++ if (download->download()->is_meta_download()) ++ return process_meta_download(download); ++ + rpc::call_command("d.set_complete", (int64_t)1, rpc::make_target(download)); + + rpc::call_command("d.set_connection_current", rpc::call_command_void("d.get_connection_seed", rpc::make_target(download)), rpc::make_target(download)); +@@ -576,4 +584,36 @@ DownloadList::confirm_finished(Download* download) { + resume(download, torrent::Download::start_no_create | torrent::Download::start_skip_tracker | torrent::Download::start_keep_baseline); + } + ++void ++DownloadList::process_meta_download(Download* download) { ++ rpc::call_command("d.stop", torrent::Object(), rpc::make_target(download)); ++ rpc::call_command("d.close", torrent::Object(), rpc::make_target(download)); ++ ++ std::string metafile = (*download->file_list()->begin())->frozen_path(); ++ std::fstream file(metafile.c_str(), std::ios::in | std::ios::binary); ++ if (!file.is_open()) { ++ control->core()->push_log("Could not read download metadata."); ++ return; ++ } ++ ++ torrent::Object* bencode = new torrent::Object(torrent::Object::create_map()); ++ file >> bencode->insert_key("info", torrent::Object()); ++ if (file.fail()) { ++ delete bencode; ++ control->core()->push_log("Could not create download, the input is not a valid torrent."); ++ return; ++ } ++ file.close(); ++ ++ // Steal the keys we still need. The old download has no use for them. ++ bencode->insert_key("rtorrent_meta_download", torrent::Object()).swap(download->bencode()->get_key("rtorrent_meta_download")); ++ if (download->bencode()->has_key("announce")) ++ bencode->insert_key("announce", torrent::Object()).swap(download->bencode()->get_key("announce")); ++ if (download->bencode()->has_key("announce-list")) ++ bencode->insert_key("announce-list", torrent::Object()).swap(download->bencode()->get_key("announce-list")); ++ ++ erase_ptr(download); ++ control->core()->try_create_download_from_meta_download(bencode, metafile); ++} ++ + } +diff --git a/src/core/download_list.h b/src/core/download_list.h +index f7828ea..8ecffa0 100644 +--- a/src/core/download_list.h ++++ b/src/core/download_list.h +@@ -161,6 +161,8 @@ private: + + void received_finished(Download* d); + void confirm_finished(Download* d); ++ ++ void process_meta_download(Download* d); + }; + + } +diff --git a/src/core/manager.cc b/src/core/manager.cc +index 62738ca..2a422c8 100644 +--- a/src/core/manager.cc ++++ b/src/core/manager.cc +@@ -39,6 +39,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -52,6 +53,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -395,6 +397,7 @@ Manager::try_create_download(const std::string& uri, int flags, const command_li + if ((flags & create_tied) && + !(flags & create_raw_data) && + !is_network_uri(uri) && ++ !is_magnet_uri(uri) && + !file_status_cache()->insert(uri, 0)) + return; + +@@ -416,6 +419,31 @@ Manager::try_create_download(const std::string& uri, int flags, const command_li + f->commit(); + } + ++void ++Manager::try_create_download_from_meta_download(torrent::Object* bencode, const std::string& metafile) { ++ DownloadFactory* f = new DownloadFactory(this); ++ ++ f->variables()["tied_to_file"] = (int64_t)true; ++ f->variables()["tied_file"] = metafile; ++ ++ torrent::Object& meta = bencode->get_key("rtorrent_meta_download"); ++ torrent::Object::list_type& commands = meta.get_key_list("commands"); ++ for (torrent::Object::list_type::const_iterator itr = commands.begin(); itr != commands.end(); ++itr) ++ f->commands().insert(f->commands().end(), itr->as_string()); ++ ++ f->set_start(meta.get_key_value("start")); ++ f->set_print_log(meta.get_key_value("print_log")); ++ f->slot_finished(sigc::bind(sigc::ptr_fun(&rak::call_delete_func), f)); ++ ++ // Bit of a waste to create the bencode repesentation here ++ // only to have the DownloadFactory decode it. ++ std::stringstream s; ++ s.imbue(std::locale::classic()); ++ s << *bencode; ++ f->load_raw_data(s.str()); ++ f->commit(); ++} ++ + utils::Directory + path_expand_transform(std::string path, const utils::directory_entry& entry) { + return path + entry.d_name; +diff --git a/src/core/manager.h b/src/core/manager.h +index 3b23da3..16902af 100644 +--- a/src/core/manager.h ++++ b/src/core/manager.h +@@ -128,6 +128,7 @@ public: + // Temporary, find a better place for this. + void try_create_download(const std::string& uri, int flags, const command_list_type& commands); + void try_create_download_expand(const std::string& uri, int flags, command_list_type commands = command_list_type()); ++ void try_create_download_from_meta_download(torrent::Object* bencode, const std::string& metafile); + + private: + typedef RangeMap AddressThrottleMap; diff --git a/rtorrent-extended/rtorrent-extended.install b/rtorrent-extended/rtorrent-extended.install new file mode 100644 index 0000000..811a690 --- /dev/null +++ b/rtorrent-extended/rtorrent-extended.install @@ -0,0 +1,9 @@ +post_install() { + echo "" + echo "rTorrent-eXtended new options in .rtorrent.rc supported" + echo "examples at http://lky.cc/rtorrent-extended/" +} +post_upgrade() { + post_install +} + diff --git a/rtorrent-extended/trackerinfo.patch b/rtorrent-extended/trackerinfo.patch new file mode 100644 index 0000000..63998a5 --- /dev/null +++ b/rtorrent-extended/trackerinfo.patch @@ -0,0 +1,21 @@ +diff --git a/src/display/window_tracker_list.cc b/src/display/window_tracker_list.cc +index 1903d67..2860f12 100644 +--- a/src/display/window_tracker_list.cc ++++ b/src/display/window_tracker_list.cc +@@ -89,13 +89,14 @@ WindowTrackerList::redraw() { + tracker->url().c_str()); + + if (pos < m_canvas->height()) +- m_canvas->print(4, pos++, "Id: %s Focus: %s Enabled: %s Open: %s S/L: %u/%u", ++ m_canvas->print(4, pos++, "Id: %s Focus: %s Enabled: %s Open: %s S/L/D: %u/%u/%u", + rak::copy_escape_html(tracker->tracker_id()).c_str(), + range.first == tl->focus_index() ? "yes" : " no", + tracker->is_usable() ? "yes" : tracker->is_enabled() ? "off" : " no", + tracker->is_busy() ? "yes" : " no", + tracker->scrape_complete(), +- tracker->scrape_incomplete()); ++ tracker->scrape_incomplete(), ++ tracker->scrape_downloaded()); + + // m_canvas->print(4, pos++, "Id: %s Focus: %s Enabled: %s Open: %s Timer: %u/%u", + // rak::copy_escape_html(tracker->tracker_id()).c_str(), diff --git a/rtorrent-extended/vi_kb_akston.patch b/rtorrent-extended/vi_kb_akston.patch new file mode 100644 index 0000000..46ff7ea --- /dev/null +++ b/rtorrent-extended/vi_kb_akston.patch @@ -0,0 +1,176 @@ +diff --git a/src/ui/download_list.cc b/src/ui/download_list.cc +index e72bff6..04b8625 100644 +--- a/src/ui/download_list.cc ++++ b/src/ui/download_list.cc +@@ -346,13 +346,13 @@ DownloadList::setup_keys() { + m_bindings['\x0F'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::receive_view_input), INPUT_CHANGE_DIRECTORY); + m_bindings['X' - '@'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::receive_view_input), INPUT_COMMAND); + +- m_uiArray[DISPLAY_LOG]->bindings()[KEY_LEFT] = ++ m_uiArray[DISPLAY_LOG]->bindings()['h'] = + m_uiArray[DISPLAY_LOG]->bindings()['B' - '@'] = + m_uiArray[DISPLAY_LOG]->bindings()[' '] = sigc::bind(sigc::mem_fun(*this, &DownloadList::activate_display), DISPLAY_DOWNLOAD_LIST); + +- m_uiArray[DISPLAY_DOWNLOAD_LIST]->bindings()[KEY_RIGHT] = ++ m_uiArray[DISPLAY_DOWNLOAD_LIST]->bindings()['l'] = + m_uiArray[DISPLAY_DOWNLOAD_LIST]->bindings()['F' - '@'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::activate_display), DISPLAY_DOWNLOAD); +- m_uiArray[DISPLAY_DOWNLOAD_LIST]->bindings()['l'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::activate_display), DISPLAY_LOG); ++ m_uiArray[DISPLAY_DOWNLOAD_LIST]->bindings()['L'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::activate_display), DISPLAY_LOG); + } + + } +diff --git a/src/ui/element_chunks_seen.cc b/src/ui/element_chunks_seen.cc +index 8ee1dd9..d2636db 100644 +--- a/src/ui/element_chunks_seen.cc ++++ b/src/ui/element_chunks_seen.cc +@@ -52,10 +52,10 @@ ElementChunksSeen::ElementChunksSeen(core::Download* d) : + m_window(NULL), + m_focus(0) { + +- m_bindings[KEY_LEFT] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); ++ m_bindings['h'] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); + +- m_bindings[KEY_DOWN] = m_bindings['N' - '@'] = sigc::mem_fun(*this, &ElementChunksSeen::receive_next); +- m_bindings[KEY_UP] = m_bindings['P' - '@'] = sigc::mem_fun(*this, &ElementChunksSeen::receive_prev); ++ m_bindings['j'] = m_bindings['N' - '@'] = sigc::mem_fun(*this, &ElementChunksSeen::receive_next); ++ m_bindings['k'] = m_bindings['P' - '@'] = sigc::mem_fun(*this, &ElementChunksSeen::receive_prev); + m_bindings[KEY_NPAGE] = sigc::mem_fun(*this, &ElementChunksSeen::receive_pagenext); + m_bindings[KEY_PPAGE] = sigc::mem_fun(*this, &ElementChunksSeen::receive_pageprev); + } +diff --git a/src/ui/element_download_list.cc b/src/ui/element_download_list.cc +index 1dc9ece..0c789f1 100644 +--- a/src/ui/element_download_list.cc ++++ b/src/ui/element_download_list.cc +@@ -95,8 +95,8 @@ ElementDownloadList::ElementDownloadList() : + m_bindings['8'] = sigc::bind(sigc::mem_fun(*this, &ElementDownloadList::receive_change_view), "seeding"); + m_bindings['9'] = sigc::bind(sigc::mem_fun(*this, &ElementDownloadList::receive_change_view), "active"); + +- m_bindings[KEY_UP] = m_bindings['P' - '@'] = sigc::mem_fun(*this, &ElementDownloadList::receive_prev); +- m_bindings[KEY_DOWN] = m_bindings['N' - '@'] = sigc::mem_fun(*this, &ElementDownloadList::receive_next); ++ m_bindings['k'] = m_bindings['P' - '@'] = sigc::mem_fun(*this, &ElementDownloadList::receive_prev); ++ m_bindings['j'] = m_bindings['N' - '@'] = sigc::mem_fun(*this, &ElementDownloadList::receive_next); + } + + void +diff --git a/src/ui/element_file_list.cc b/src/ui/element_file_list.cc +index 75ecec0..f23ef23 100644 +--- a/src/ui/element_file_list.cc ++++ b/src/ui/element_file_list.cc +@@ -64,8 +64,8 @@ ElementFileList::ElementFileList(core::Download* d) : + m_selected(iterator(d->download()->file_list()->begin())), + m_collapsed(false) { + +- m_bindings[KEY_LEFT] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); +- m_bindings[KEY_RIGHT] = m_bindings['F' - '@'] = sigc::mem_fun(*this, &ElementFileList::receive_select); ++ m_bindings['h'] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); ++ m_bindings['l'] = m_bindings['F' - '@'] = sigc::mem_fun(*this, &ElementFileList::receive_select); + + m_bindings[' '] = sigc::mem_fun(*this, &ElementFileList::receive_priority); + m_bindings['*'] = sigc::mem_fun(*this, &ElementFileList::receive_change_all); +@@ -73,8 +73,8 @@ ElementFileList::ElementFileList(core::Download* d) : + m_bindings[KEY_NPAGE] = sigc::mem_fun(*this, &ElementFileList::receive_pagenext); + m_bindings[KEY_PPAGE] = sigc::mem_fun(*this, &ElementFileList::receive_pageprev); + +- m_bindings[KEY_DOWN] = m_bindings['N' - '@'] = sigc::mem_fun(*this, &ElementFileList::receive_next); +- m_bindings[KEY_UP] = m_bindings['P' - '@'] = sigc::mem_fun(*this, &ElementFileList::receive_prev); ++ m_bindings['j'] = m_bindings['N' - '@'] = sigc::mem_fun(*this, &ElementFileList::receive_next); ++ m_bindings['k'] = m_bindings['P' - '@'] = sigc::mem_fun(*this, &ElementFileList::receive_prev); + } + + inline ElementText* +diff --git a/src/ui/element_menu.cc b/src/ui/element_menu.cc +index 04130f3..41779eb 100644 +--- a/src/ui/element_menu.cc ++++ b/src/ui/element_menu.cc +@@ -72,11 +72,11 @@ ElementMenu::ElementMenu() : + m_entry(entry_invalid) { + + // Move bindings into a function that defines default bindings. +- m_bindings[KEY_LEFT] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); +- m_bindings[KEY_RIGHT] = m_bindings['F' - '@'] = sigc::mem_fun(this, &ElementMenu::entry_select); ++ m_bindings['h'] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); ++ m_bindings['l'] = m_bindings['F' - '@'] = sigc::mem_fun(this, &ElementMenu::entry_select); + +- m_bindings[KEY_UP] = m_bindings['P' - '@'] = sigc::mem_fun(this, &ElementMenu::entry_prev); +- m_bindings[KEY_DOWN] = m_bindings['N' - '@'] = sigc::mem_fun(this, &ElementMenu::entry_next); ++ m_bindings['k'] = m_bindings['P' - '@'] = sigc::mem_fun(this, &ElementMenu::entry_prev); ++ m_bindings['j'] = m_bindings['N' - '@'] = sigc::mem_fun(this, &ElementMenu::entry_next); + } + + ElementMenu::~ElementMenu() { +diff --git a/src/ui/element_peer_list.cc b/src/ui/element_peer_list.cc +index be0e0b4..e38f3a2 100644 +--- a/src/ui/element_peer_list.cc ++++ b/src/ui/element_peer_list.cc +@@ -73,14 +73,14 @@ ElementPeerList::ElementPeerList(core::Download* d) : + + m_elementInfo->slot_exit(sigc::bind(sigc::mem_fun(this, &ElementPeerList::activate_display), DISPLAY_LIST)); + +- m_bindings['k'] = sigc::mem_fun(this, &ElementPeerList::receive_disconnect_peer); ++ m_bindings['K'] = sigc::mem_fun(this, &ElementPeerList::receive_disconnect_peer); + m_bindings['*'] = sigc::mem_fun(this, &ElementPeerList::receive_snub_peer); + m_bindings['B'] = sigc::mem_fun(this, &ElementPeerList::receive_ban_peer); +- m_bindings[KEY_LEFT] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); +- m_bindings[KEY_RIGHT] = m_bindings['F' - '@'] = sigc::bind(sigc::mem_fun(this, &ElementPeerList::activate_display), DISPLAY_INFO); ++ m_bindings['h'] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); ++ m_bindings['l'] = m_bindings['F' - '@'] = sigc::bind(sigc::mem_fun(this, &ElementPeerList::activate_display), DISPLAY_INFO); + +- m_bindings[KEY_UP] = m_bindings['P' - '@'] = sigc::mem_fun(this, &ElementPeerList::receive_prev); +- m_bindings[KEY_DOWN] = m_bindings['N' - '@'] = sigc::mem_fun(this, &ElementPeerList::receive_next); ++ m_bindings['k'] = m_bindings['P' - '@'] = sigc::mem_fun(this, &ElementPeerList::receive_prev); ++ m_bindings['j'] = m_bindings['N' - '@'] = sigc::mem_fun(this, &ElementPeerList::receive_next); + } + + ElementPeerList::~ElementPeerList() { +diff --git a/src/ui/element_text.cc b/src/ui/element_text.cc +index a7496f2..a564a81 100644 +--- a/src/ui/element_text.cc ++++ b/src/ui/element_text.cc +@@ -54,7 +54,7 @@ ElementText::ElementText(rpc::target_type target) : + m_columnWidth(0) { + + // Move bindings into a function that defines default bindings. +- m_bindings[KEY_LEFT] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); ++ m_bindings['h'] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); + + // m_bindings[KEY_UP] = sigc::mem_fun(this, &ElementText::entry_prev); + // m_bindings[KEY_DOWN] = sigc::mem_fun(this, &ElementText::entry_next); +diff --git a/src/ui/element_tracker_list.cc b/src/ui/element_tracker_list.cc +index 7d81b89..8a51e80 100644 +--- a/src/ui/element_tracker_list.cc ++++ b/src/ui/element_tracker_list.cc +@@ -54,13 +54,13 @@ ElementTrackerList::ElementTrackerList(core::Download* d) : + m_window(NULL), + m_focus(0) { + +- m_bindings[KEY_LEFT] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); ++ m_bindings['h'] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); + + m_bindings[' '] = sigc::mem_fun(*this, &ElementTrackerList::receive_cycle_group); + m_bindings['*'] = sigc::mem_fun(*this, &ElementTrackerList::receive_disable); + +- m_bindings[KEY_DOWN] = m_bindings['N' - '@'] = sigc::mem_fun(*this, &ElementTrackerList::receive_next); +- m_bindings[KEY_UP] = m_bindings['P' - '@'] = sigc::mem_fun(*this, &ElementTrackerList::receive_prev); ++ m_bindings['j'] = m_bindings['N' - '@'] = sigc::mem_fun(*this, &ElementTrackerList::receive_next); ++ m_bindings['k'] = m_bindings['P' - '@'] = sigc::mem_fun(*this, &ElementTrackerList::receive_prev); + } + + void +diff --git a/src/ui/element_transfer_list.cc b/src/ui/element_transfer_list.cc +index 50eaec6..83ae26d 100644 +--- a/src/ui/element_transfer_list.cc ++++ b/src/ui/element_transfer_list.cc +@@ -52,10 +52,10 @@ ElementTransferList::ElementTransferList(core::Download* d) : + m_window(NULL), + m_focus(0) { + +- m_bindings[KEY_LEFT] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); ++ m_bindings['h'] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); + +- m_bindings[KEY_DOWN] = sigc::mem_fun(*this, &ElementTransferList::receive_next); +- m_bindings[KEY_UP] = sigc::mem_fun(*this, &ElementTransferList::receive_prev); ++ m_bindings['j'] = sigc::mem_fun(*this, &ElementTransferList::receive_next); ++ m_bindings['k'] = sigc::mem_fun(*this, &ElementTransferList::receive_prev); + m_bindings[KEY_NPAGE] = sigc::mem_fun(*this, &ElementTransferList::receive_pagenext); + m_bindings[KEY_PPAGE] = sigc::mem_fun(*this, &ElementTransferList::receive_pageprev); + } diff --git a/rtorrent-extended/vi_kb_tjwoosta.patch b/rtorrent-extended/vi_kb_tjwoosta.patch new file mode 100644 index 0000000..b7ea5ac --- /dev/null +++ b/rtorrent-extended/vi_kb_tjwoosta.patch @@ -0,0 +1,175 @@ +diff --git a/src/ui/download_list.cc b/src/ui/download_list.cc +index e72bff6..30b5987 100644 +--- a/src/ui/download_list.cc ++++ b/src/ui/download_list.cc +@@ -350,9 +350,16 @@ DownloadList::setup_keys() { + m_uiArray[DISPLAY_LOG]->bindings()['B' - '@'] = + m_uiArray[DISPLAY_LOG]->bindings()[' '] = sigc::bind(sigc::mem_fun(*this, &DownloadList::activate_display), DISPLAY_DOWNLOAD_LIST); + ++ m_uiArray[DISPLAY_LOG]->bindings()['h'] = ++ m_uiArray[DISPLAY_LOG]->bindings()['B' - '@'] = ++ m_uiArray[DISPLAY_LOG]->bindings()[' '] = sigc::bind(sigc::mem_fun(*this, &DownloadList::activate_display), DISPLAY_DOWNLOAD_LIST); ++ + m_uiArray[DISPLAY_DOWNLOAD_LIST]->bindings()[KEY_RIGHT] = + m_uiArray[DISPLAY_DOWNLOAD_LIST]->bindings()['F' - '@'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::activate_display), DISPLAY_DOWNLOAD); +- m_uiArray[DISPLAY_DOWNLOAD_LIST]->bindings()['l'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::activate_display), DISPLAY_LOG); ++ ++ m_uiArray[DISPLAY_DOWNLOAD_LIST]->bindings()['l'] = ++ m_uiArray[DISPLAY_DOWNLOAD_LIST]->bindings()['F' - '@'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::activate_display), DISPLAY_DOWNLOAD); ++ m_uiArray[DISPLAY_DOWNLOAD_LIST]->bindings()['L'] = sigc::bind(sigc::mem_fun(*this, &DownloadList::activate_display), DISPLAY_LOG); + } + + } +diff --git a/src/ui/element_chunks_seen.cc b/src/ui/element_chunks_seen.cc +index 8ee1dd9..5336b05 100644 +--- a/src/ui/element_chunks_seen.cc ++++ b/src/ui/element_chunks_seen.cc +@@ -54,8 +54,12 @@ ElementChunksSeen::ElementChunksSeen(core::Download* d) : + + m_bindings[KEY_LEFT] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); + ++ m_bindings['h'] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); ++ + m_bindings[KEY_DOWN] = m_bindings['N' - '@'] = sigc::mem_fun(*this, &ElementChunksSeen::receive_next); ++ m_bindings['j'] = m_bindings['N' - '@'] = sigc::mem_fun(*this, &ElementChunksSeen::receive_next); + m_bindings[KEY_UP] = m_bindings['P' - '@'] = sigc::mem_fun(*this, &ElementChunksSeen::receive_prev); ++ m_bindings['k'] = m_bindings['P' - '@'] = sigc::mem_fun(*this, &ElementChunksSeen::receive_prev); + m_bindings[KEY_NPAGE] = sigc::mem_fun(*this, &ElementChunksSeen::receive_pagenext); + m_bindings[KEY_PPAGE] = sigc::mem_fun(*this, &ElementChunksSeen::receive_pageprev); + } +diff --git a/src/ui/element_download_list.cc b/src/ui/element_download_list.cc +index 1dc9ece..f2763ca 100644 +--- a/src/ui/element_download_list.cc ++++ b/src/ui/element_download_list.cc +@@ -96,7 +96,9 @@ ElementDownloadList::ElementDownloadList() : + m_bindings['9'] = sigc::bind(sigc::mem_fun(*this, &ElementDownloadList::receive_change_view), "active"); + + m_bindings[KEY_UP] = m_bindings['P' - '@'] = sigc::mem_fun(*this, &ElementDownloadList::receive_prev); ++ m_bindings['k'] = m_bindings['P' - '@'] = sigc::mem_fun(*this, &ElementDownloadList::receive_prev); + m_bindings[KEY_DOWN] = m_bindings['N' - '@'] = sigc::mem_fun(*this, &ElementDownloadList::receive_next); ++ m_bindings['j'] = m_bindings['N' - '@'] = sigc::mem_fun(*this, &ElementDownloadList::receive_next); + } + + void +diff --git a/src/ui/element_file_list.cc b/src/ui/element_file_list.cc +index 75ecec0..a24f429 100644 +--- a/src/ui/element_file_list.cc ++++ b/src/ui/element_file_list.cc +@@ -65,7 +65,9 @@ ElementFileList::ElementFileList(core::Download* d) : + m_collapsed(false) { + + m_bindings[KEY_LEFT] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); ++ m_bindings['h'] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); + m_bindings[KEY_RIGHT] = m_bindings['F' - '@'] = sigc::mem_fun(*this, &ElementFileList::receive_select); ++ m_bindings['l'] = m_bindings['F' - '@'] = sigc::mem_fun(*this, &ElementFileList::receive_select); + + m_bindings[' '] = sigc::mem_fun(*this, &ElementFileList::receive_priority); + m_bindings['*'] = sigc::mem_fun(*this, &ElementFileList::receive_change_all); +@@ -74,7 +76,9 @@ ElementFileList::ElementFileList(core::Download* d) : + m_bindings[KEY_PPAGE] = sigc::mem_fun(*this, &ElementFileList::receive_pageprev); + + m_bindings[KEY_DOWN] = m_bindings['N' - '@'] = sigc::mem_fun(*this, &ElementFileList::receive_next); ++ m_bindings['j'] = m_bindings['N' - '@'] = sigc::mem_fun(*this, &ElementFileList::receive_next); + m_bindings[KEY_UP] = m_bindings['P' - '@'] = sigc::mem_fun(*this, &ElementFileList::receive_prev); ++ m_bindings['k'] = m_bindings['P' - '@'] = sigc::mem_fun(*this, &ElementFileList::receive_prev); + } + + inline ElementText* +diff --git a/src/ui/element_menu.cc b/src/ui/element_menu.cc +index 04130f3..b6b3ee9 100644 +--- a/src/ui/element_menu.cc ++++ b/src/ui/element_menu.cc +@@ -72,11 +72,15 @@ ElementMenu::ElementMenu() : + m_entry(entry_invalid) { + + // Move bindings into a function that defines default bindings. +- m_bindings[KEY_LEFT] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); ++ m_bindings[KEY_LEFT] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); ++ m_bindings['h'] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); + m_bindings[KEY_RIGHT] = m_bindings['F' - '@'] = sigc::mem_fun(this, &ElementMenu::entry_select); ++ m_bindings['l'] = m_bindings['F' - '@'] = sigc::mem_fun(this, &ElementMenu::entry_select); + + m_bindings[KEY_UP] = m_bindings['P' - '@'] = sigc::mem_fun(this, &ElementMenu::entry_prev); ++ m_bindings['k'] = m_bindings['P' - '@'] = sigc::mem_fun(this, &ElementMenu::entry_prev); + m_bindings[KEY_DOWN] = m_bindings['N' - '@'] = sigc::mem_fun(this, &ElementMenu::entry_next); ++ m_bindings['j'] = m_bindings['N' - '@'] = sigc::mem_fun(this, &ElementMenu::entry_next); + } + + ElementMenu::~ElementMenu() { +diff --git a/src/ui/element_peer_list.cc b/src/ui/element_peer_list.cc +index be0e0b4..8b7303e 100644 +--- a/src/ui/element_peer_list.cc ++++ b/src/ui/element_peer_list.cc +@@ -73,14 +73,18 @@ ElementPeerList::ElementPeerList(core::Download* d) : + + m_elementInfo->slot_exit(sigc::bind(sigc::mem_fun(this, &ElementPeerList::activate_display), DISPLAY_LIST)); + +- m_bindings['k'] = sigc::mem_fun(this, &ElementPeerList::receive_disconnect_peer); ++ m_bindings['K'] = sigc::mem_fun(this, &ElementPeerList::receive_disconnect_peer); + m_bindings['*'] = sigc::mem_fun(this, &ElementPeerList::receive_snub_peer); + m_bindings['B'] = sigc::mem_fun(this, &ElementPeerList::receive_ban_peer); +- m_bindings[KEY_LEFT] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); ++ m_bindings[KEY_LEFT] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); ++ m_bindings['h'] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); + m_bindings[KEY_RIGHT] = m_bindings['F' - '@'] = sigc::bind(sigc::mem_fun(this, &ElementPeerList::activate_display), DISPLAY_INFO); ++ m_bindings['l'] = m_bindings['F' - '@'] = sigc::bind(sigc::mem_fun(this, &ElementPeerList::activate_display), DISPLAY_INFO); + + m_bindings[KEY_UP] = m_bindings['P' - '@'] = sigc::mem_fun(this, &ElementPeerList::receive_prev); ++ m_bindings['k'] = m_bindings['P' - '@'] = sigc::mem_fun(this, &ElementPeerList::receive_prev); + m_bindings[KEY_DOWN] = m_bindings['N' - '@'] = sigc::mem_fun(this, &ElementPeerList::receive_next); ++ m_bindings['j'] = m_bindings['N' - '@'] = sigc::mem_fun(this, &ElementPeerList::receive_next); + } + + ElementPeerList::~ElementPeerList() { +diff --git a/src/ui/element_text.cc b/src/ui/element_text.cc +index a7496f2..4c2e171 100644 +--- a/src/ui/element_text.cc ++++ b/src/ui/element_text.cc +@@ -54,7 +54,9 @@ ElementText::ElementText(rpc::target_type target) : + m_columnWidth(0) { + + // Move bindings into a function that defines default bindings. +- m_bindings[KEY_LEFT] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); ++ m_bindings[KEY_LEFT] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); ++ ++ m_bindings['h'] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); + + // m_bindings[KEY_UP] = sigc::mem_fun(this, &ElementText::entry_prev); + // m_bindings[KEY_DOWN] = sigc::mem_fun(this, &ElementText::entry_next); +diff --git a/src/ui/element_tracker_list.cc b/src/ui/element_tracker_list.cc +index 7d81b89..5584653 100644 +--- a/src/ui/element_tracker_list.cc ++++ b/src/ui/element_tracker_list.cc +@@ -55,12 +55,15 @@ ElementTrackerList::ElementTrackerList(core::Download* d) : + m_focus(0) { + + m_bindings[KEY_LEFT] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); ++ m_bindings['h'] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); + + m_bindings[' '] = sigc::mem_fun(*this, &ElementTrackerList::receive_cycle_group); + m_bindings['*'] = sigc::mem_fun(*this, &ElementTrackerList::receive_disable); + + m_bindings[KEY_DOWN] = m_bindings['N' - '@'] = sigc::mem_fun(*this, &ElementTrackerList::receive_next); ++ m_bindings['j'] = m_bindings['N' - '@'] = sigc::mem_fun(*this, &ElementTrackerList::receive_next); + m_bindings[KEY_UP] = m_bindings['P' - '@'] = sigc::mem_fun(*this, &ElementTrackerList::receive_prev); ++ m_bindings['k'] = m_bindings['P' - '@'] = sigc::mem_fun(*this, &ElementTrackerList::receive_prev); + } + + void +diff --git a/src/ui/element_transfer_list.cc b/src/ui/element_transfer_list.cc +index 50eaec6..f14a2d2 100644 +--- a/src/ui/element_transfer_list.cc ++++ b/src/ui/element_transfer_list.cc +@@ -54,8 +54,12 @@ ElementTransferList::ElementTransferList(core::Download* d) : + + m_bindings[KEY_LEFT] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); + ++ m_bindings['h'] = m_bindings['B' - '@'] = sigc::mem_fun(&m_slotExit, &slot_type::operator()); ++ + m_bindings[KEY_DOWN] = sigc::mem_fun(*this, &ElementTransferList::receive_next); ++ m_bindings['j'] = sigc::mem_fun(*this, &ElementTransferList::receive_next); + m_bindings[KEY_UP] = sigc::mem_fun(*this, &ElementTransferList::receive_prev); ++ m_bindings['k'] = sigc::mem_fun(*this, &ElementTransferList::receive_prev); + m_bindings[KEY_NPAGE] = sigc::mem_fun(*this, &ElementTransferList::receive_pagenext); + m_bindings[KEY_PPAGE] = sigc::mem_fun(*this, &ElementTransferList::receive_pageprev); + } -- cgit v1.2.3-24-g4f1b