diff --git a/configure.ac b/configure.ac index f0629038..cf9bc47f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,13 +1,16 @@ -AC_INIT(rtorrent, 0.9.6, sundell.software@gmail.com) +AC_INIT(rtorrent, 0.9.7, sundell.software@gmail.com) AC_DEFINE(API_VERSION, 9, api version) AM_INIT_AUTOMAKE AC_CONFIG_HEADERS(config.h) +AC_PROG_RANLIB AC_PROG_CXX AC_PROG_LIBTOOL +RAK_CHECK_CFLAGS +RAK_CHECK_CXXFLAGS RAK_ENABLE_DEBUG RAK_ENABLE_EXTRA_DEBUG RAK_ENABLE_WERROR @@ -32,15 +35,16 @@ AX_PTHREAD([], AC_MSG_ERROR([requires pthread])) AX_WITH_CURSES if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then - AC_MSG_ERROR([requires either NcursesW or Ncurses library]) + AC_MSG_ERROR([requires either NcursesW or Ncurses library]) fi +PKG_CHECK_MODULES([LIBCURL], [libcurl], , [LIBCURL_CHECK_CONFIG]) PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"]) -PKG_CHECK_MODULES([DEPENDENCIES], [libcurl >= 7.15.4 libtorrent >= 0.13.6]) +PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.13.7]) -CFLAGS="$CFLAGS $PTHREAD_CFLAGS $CPPUNIT_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS" -CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $CPPUNIT_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS" -LIBS="$PTHREAD_LIBS $CURSES_LIB $CPPUNIT_LIBS $DEPENDENCIES_LIBS $LIBS" +LIBS="$PTHREAD_LIBS $CURSES_LIB $CPPUNIT_LIBS $LIBCURL $LIBCURL_LIBS $DEPENDENCIES_LIBS $LIBS" +CFLAGS="$CFLAGS $PTHREAD_CFLAGS $CPPUNIT_CFLAGS $LIBCURL_CPPFLAGS $LIBCURL_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS" +CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $CPPUNIT_CFLAGS $LIBCURL_CPPFLAGS $LIBCURL_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS" AC_LANG_PUSH(C++) TORRENT_WITH_XMLRPC_C diff --git a/doc/rtorrent.rc b/doc/rtorrent.rc index 54383176..d0c17cf4 100644 --- a/doc/rtorrent.rc +++ b/doc/rtorrent.rc @@ -101,3 +101,12 @@ # Set downlad list layout style. ("full", "compact") # #ui.torrent_list.layout.set = "full" + +# SCGI Connectivity (for alternative rtorrent interfaces, XMLRPC) +# +# Use a IP socket with scgi_port, or a Unix socket with scgi_local. +# schedule can be used to set permissions on the unix socket. +# +#scgi_port = 127.0.0.1:5000 +#scgi_local = /home/user/rtorrent/rpc.socket +#schedule = scgi_permission,0,0,"execute.nothrow=chmod,\"g+w,o=\",/home/user/rtorrent/rpc.socket" diff --git a/rak/algorithm.h b/rak/algorithm.h index ad8e7158..98613820 100644 --- a/rak/algorithm.h +++ b/rak/algorithm.h @@ -176,6 +176,26 @@ inline int popcount_wrapper(T t) { #endif } +// Get the median of an unordered set of numbers of arbitrary +// type by modifing the underlying dataset +template +T median(_InputIter __first, _InputIter __last) { + T __med; + + unsigned int __size = __last - __first; + unsigned int __middle = __size / 2; + _InputIter __target1 = __first + __middle; + std::nth_element(__first, __target1, __last); + __med = *__target1; + + if (__size % 2 == 0) { + _InputIter __target2 = std::max_element(__first, __target1); + __med = (__med + *__target2) / 2.0; + } + + return __med; +} + } #endif diff --git a/scripts/common.m4 b/scripts/common.m4 index 9885b037..b1b815b1 100644 --- a/scripts/common.m4 +++ b/scripts/common.m4 @@ -153,7 +153,7 @@ dnl Need to fix this so that it uses the stuff defined by the system. AC_DEFUN([TORRENT_CHECK_EXECINFO], [ AC_MSG_CHECKING(for execinfo.h) - AC_RUN_IFELSE([AC_LANG_SOURCE([ + AC_COMPILE_IFELSE([AC_LANG_SOURCE([ #include int main() { backtrace((void**)0, 0); backtrace_symbols((char**)0, 0); return 0;} ])], diff --git a/scripts/libcurl.m4 b/scripts/libcurl.m4 new file mode 100644 index 00000000..53d694d0 --- /dev/null +++ b/scripts/libcurl.m4 @@ -0,0 +1,272 @@ +#*************************************************************************** +# _ _ ____ _ +# Project ___| | | | _ \| | +# / __| | | | |_) | | +# | (__| |_| | _ <| |___ +# \___|\___/|_| \_\_____| +# +# Copyright (C) 2006, David Shaw +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at https://curl.haxx.se/docs/copyright.html. +# +# You may opt to use, copy, modify, merge, publish, distribute and/or sell +# copies of the Software, and permit persons to whom the Software is +# furnished to do so, under the terms of the COPYING file. +# +# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +# KIND, either express or implied. +# +########################################################################### +# LIBCURL_CHECK_CONFIG ([DEFAULT-ACTION], [MINIMUM-VERSION], +# [ACTION-IF-YES], [ACTION-IF-NO]) +# ---------------------------------------------------------- +# David Shaw May-09-2006 +# +# Checks for libcurl. DEFAULT-ACTION is the string yes or no to +# specify whether to default to --with-libcurl or --without-libcurl. +# If not supplied, DEFAULT-ACTION is yes. MINIMUM-VERSION is the +# minimum version of libcurl to accept. Pass the version as a regular +# version number like 7.10.1. If not supplied, any version is +# accepted. ACTION-IF-YES is a list of shell commands to run if +# libcurl was successfully found and passed the various tests. +# ACTION-IF-NO is a list of shell commands that are run otherwise. +# Note that using --without-libcurl does run ACTION-IF-NO. +# +# This macro #defines HAVE_LIBCURL if a working libcurl setup is +# found, and sets @LIBCURL@ and @LIBCURL_CPPFLAGS@ to the necessary +# values. Other useful defines are LIBCURL_FEATURE_xxx where xxx are +# the various features supported by libcurl, and LIBCURL_PROTOCOL_yyy +# where yyy are the various protocols supported by libcurl. Both xxx +# and yyy are capitalized. See the list of AH_TEMPLATEs at the top of +# the macro for the complete list of possible defines. Shell +# variables $libcurl_feature_xxx and $libcurl_protocol_yyy are also +# defined to 'yes' for those features and protocols that were found. +# Note that xxx and yyy keep the same capitalization as in the +# curl-config list (e.g. it's "HTTP" and not "http"). +# +# Users may override the detected values by doing something like: +# LIBCURL="-lcurl" LIBCURL_CPPFLAGS="-I/usr/myinclude" ./configure +# +# For the sake of sanity, this macro assumes that any libcurl that is +# found is after version 7.7.2, the first version that included the +# curl-config script. Note that it is very important for people +# packaging binary versions of libcurl to include this script! +# Without curl-config, we can only guess what protocols are available, +# or use curl_version_info to figure it out at runtime. + +AC_DEFUN([LIBCURL_CHECK_CONFIG], +[ + AH_TEMPLATE([LIBCURL_FEATURE_SSL],[Defined if libcurl supports SSL]) + AH_TEMPLATE([LIBCURL_FEATURE_KRB4],[Defined if libcurl supports KRB4]) + AH_TEMPLATE([LIBCURL_FEATURE_IPV6],[Defined if libcurl supports IPv6]) + AH_TEMPLATE([LIBCURL_FEATURE_LIBZ],[Defined if libcurl supports libz]) + AH_TEMPLATE([LIBCURL_FEATURE_ASYNCHDNS],[Defined if libcurl supports AsynchDNS]) + AH_TEMPLATE([LIBCURL_FEATURE_IDN],[Defined if libcurl supports IDN]) + AH_TEMPLATE([LIBCURL_FEATURE_SSPI],[Defined if libcurl supports SSPI]) + AH_TEMPLATE([LIBCURL_FEATURE_NTLM],[Defined if libcurl supports NTLM]) + + AH_TEMPLATE([LIBCURL_PROTOCOL_HTTP],[Defined if libcurl supports HTTP]) + AH_TEMPLATE([LIBCURL_PROTOCOL_HTTPS],[Defined if libcurl supports HTTPS]) + AH_TEMPLATE([LIBCURL_PROTOCOL_FTP],[Defined if libcurl supports FTP]) + AH_TEMPLATE([LIBCURL_PROTOCOL_FTPS],[Defined if libcurl supports FTPS]) + AH_TEMPLATE([LIBCURL_PROTOCOL_FILE],[Defined if libcurl supports FILE]) + AH_TEMPLATE([LIBCURL_PROTOCOL_TELNET],[Defined if libcurl supports TELNET]) + AH_TEMPLATE([LIBCURL_PROTOCOL_LDAP],[Defined if libcurl supports LDAP]) + AH_TEMPLATE([LIBCURL_PROTOCOL_DICT],[Defined if libcurl supports DICT]) + AH_TEMPLATE([LIBCURL_PROTOCOL_TFTP],[Defined if libcurl supports TFTP]) + AH_TEMPLATE([LIBCURL_PROTOCOL_RTSP],[Defined if libcurl supports RTSP]) + AH_TEMPLATE([LIBCURL_PROTOCOL_POP3],[Defined if libcurl supports POP3]) + AH_TEMPLATE([LIBCURL_PROTOCOL_IMAP],[Defined if libcurl supports IMAP]) + AH_TEMPLATE([LIBCURL_PROTOCOL_SMTP],[Defined if libcurl supports SMTP]) + + AC_ARG_WITH(libcurl, + AS_HELP_STRING([--with-libcurl=PREFIX],[look for the curl library in PREFIX/lib and headers in PREFIX/include]), + [_libcurl_with=$withval],[_libcurl_with=ifelse([$1],,[yes],[$1])]) + + if test "$_libcurl_with" != "no" ; then + + AC_PROG_AWK + + _libcurl_version_parse="eval $AWK '{split(\$NF,A,\".\"); X=256*256*A[[1]]+256*A[[2]]+A[[3]]; print X;}'" + + _libcurl_try_link=yes + + if test -d "$_libcurl_with" ; then + LIBCURL_CPPFLAGS="-I$withval/include" + _libcurl_ldflags="-L$withval/lib" + AC_PATH_PROG([_libcurl_config],[curl-config],[], + ["$withval/bin"]) + else + AC_PATH_PROG([_libcurl_config],[curl-config],[],[$PATH]) + fi + + if test x$_libcurl_config != "x" ; then + AC_CACHE_CHECK([for the version of libcurl], + [libcurl_cv_lib_curl_version], + [libcurl_cv_lib_curl_version=`$_libcurl_config --version | $AWK '{print $[]2}'`]) + + _libcurl_version=`echo $libcurl_cv_lib_curl_version | $_libcurl_version_parse` + _libcurl_wanted=`echo ifelse([$2],,[0],[$2]) | $_libcurl_version_parse` + + if test $_libcurl_wanted -gt 0 ; then + AC_CACHE_CHECK([for libcurl >= version $2], + [libcurl_cv_lib_version_ok], + [ + if test $_libcurl_version -ge $_libcurl_wanted ; then + libcurl_cv_lib_version_ok=yes + else + libcurl_cv_lib_version_ok=no + fi + ]) + fi + + if test $_libcurl_wanted -eq 0 || test x$libcurl_cv_lib_version_ok = xyes ; then + if test x"$LIBCURL_CPPFLAGS" = "x" ; then + LIBCURL_CPPFLAGS=`$_libcurl_config --cflags` + fi + if test x"$LIBCURL" = "x" ; then + LIBCURL=`$_libcurl_config --libs` + + # This is so silly, but Apple actually has a bug in their + # curl-config script. Fixed in Tiger, but there are still + # lots of Panther installs around. + case "${host}" in + powerpc-apple-darwin7*) + LIBCURL=`echo $LIBCURL | sed -e 's|-arch i386||g'` + ;; + esac + fi + + # All curl-config scripts support --feature + _libcurl_features=`$_libcurl_config --feature` + + # Is it modern enough to have --protocols? (7.12.4) + if test $_libcurl_version -ge 461828 ; then + _libcurl_protocols=`$_libcurl_config --protocols` + fi + else + _libcurl_try_link=no + fi + + unset _libcurl_wanted + fi + + if test $_libcurl_try_link = yes ; then + + # we didn't find curl-config, so let's see if the user-supplied + # link line (or failing that, "-lcurl") is enough. + LIBCURL=${LIBCURL-"$_libcurl_ldflags -lcurl"} + + AC_CACHE_CHECK([whether libcurl is usable], + [libcurl_cv_lib_curl_usable], + [ + _libcurl_save_cppflags=$CPPFLAGS + CPPFLAGS="$LIBCURL_CPPFLAGS $CPPFLAGS" + _libcurl_save_libs=$LIBS + LIBS="$LIBCURL $LIBS" + + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]],[[ +/* Try and use a few common options to force a failure if we are + missing symbols or can't link. */ +int x; +curl_easy_setopt(NULL,CURLOPT_URL,NULL); +x=CURL_ERROR_SIZE; +x=CURLOPT_WRITEFUNCTION; +x=CURLOPT_WRITEDATA; +x=CURLOPT_ERRORBUFFER; +x=CURLOPT_STDERR; +x=CURLOPT_VERBOSE; +if (x) {;} +]])],libcurl_cv_lib_curl_usable=yes,libcurl_cv_lib_curl_usable=no) + + CPPFLAGS=$_libcurl_save_cppflags + LIBS=$_libcurl_save_libs + unset _libcurl_save_cppflags + unset _libcurl_save_libs + ]) + + if test $libcurl_cv_lib_curl_usable = yes ; then + + # Does curl_free() exist in this version of libcurl? + # If not, fake it with free() + + _libcurl_save_cppflags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $LIBCURL_CPPFLAGS" + _libcurl_save_libs=$LIBS + LIBS="$LIBS $LIBCURL" + + AC_CHECK_FUNC(curl_free,, + AC_DEFINE(curl_free,free, + [Define curl_free() as free() if our version of curl lacks curl_free.])) + + CPPFLAGS=$_libcurl_save_cppflags + LIBS=$_libcurl_save_libs + unset _libcurl_save_cppflags + unset _libcurl_save_libs + + AC_DEFINE(HAVE_LIBCURL,1, + [Define to 1 if you have a functional curl library.]) + AC_SUBST(LIBCURL_CPPFLAGS) + AC_SUBST(LIBCURL) + + for _libcurl_feature in $_libcurl_features ; do + AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_feature_$_libcurl_feature),[1]) + eval AS_TR_SH(libcurl_feature_$_libcurl_feature)=yes + done + + if test "x$_libcurl_protocols" = "x" ; then + + # We don't have --protocols, so just assume that all + # protocols are available + _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT TFTP" + + if test x$libcurl_feature_SSL = xyes ; then + _libcurl_protocols="$_libcurl_protocols HTTPS" + + # FTPS wasn't standards-compliant until version + # 7.11.0 (0x070b00 == 461568) + if test $_libcurl_version -ge 461568; then + _libcurl_protocols="$_libcurl_protocols FTPS" + fi + fi + + # RTSP, IMAP, POP3 and SMTP were added in + # 7.20.0 (0x071400 == 463872) + if test $_libcurl_version -ge 463872; then + _libcurl_protocols="$_libcurl_protocols RTSP IMAP POP3 SMTP" + fi + fi + + for _libcurl_protocol in $_libcurl_protocols ; do + AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_protocol_$_libcurl_protocol),[1]) + eval AS_TR_SH(libcurl_protocol_$_libcurl_protocol)=yes + done + else + unset LIBCURL + unset LIBCURL_CPPFLAGS + fi + fi + + unset _libcurl_try_link + unset _libcurl_version_parse + unset _libcurl_config + unset _libcurl_feature + unset _libcurl_features + unset _libcurl_protocol + unset _libcurl_protocols + unset _libcurl_version + unset _libcurl_ldflags + fi + + if test x$_libcurl_with = xno || test x$libcurl_cv_lib_curl_usable != xyes ; then + # This is the IF-NO path + ifelse([$4],,:,[$4]) + else + # This is the IF-YES path + ifelse([$3],,:,[$3]) + fi + + unset _libcurl_with +])dnl diff --git a/scripts/rak_compiler.m4 b/scripts/rak_compiler.m4 index 39bd19a7..9a361bed 100644 --- a/scripts/rak_compiler.m4 +++ b/scripts/rak_compiler.m4 @@ -1,3 +1,29 @@ +AC_DEFUN([RAK_CHECK_CFLAGS], [ + + AC_MSG_CHECKING([for user-defined CFLAGS]) + + if test "$CFLAGS" = ""; then + unset CFLAGS + AC_MSG_RESULT([undefined]) + else + AC_MSG_RESULT([user-defined "$CFLAGS"]) + fi +]) + + +AC_DEFUN([RAK_CHECK_CXXFLAGS], [ + + AC_MSG_CHECKING([for user-defined CXXFLAGS]) + + if test "$CXXFLAGS" = ""; then + unset CXXFLAGS + AC_MSG_RESULT([undefined]) + else + AC_MSG_RESULT([user-defined "$CXXFLAGS"]) + fi +]) + + AC_DEFUN([RAK_ENABLE_DEBUG], [ AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [enable debug information [[default=yes]]]), diff --git a/src/command_download.cc b/src/command_download.cc index 6d7b3458..70ec427a 100644 --- a/src/command_download.cc +++ b/src/command_download.cc @@ -274,6 +274,41 @@ retrieve_d_custom_throw(core::Download* download, const std::string& key) { } } +torrent::Object +retrieve_d_custom_if_z(core::Download* download, const torrent::Object::list_type& args) { + torrent::Object::list_const_iterator itr = args.begin(); + if (itr == args.end()) + throw torrent::bencode_error("d.custom.if_z: Missing key argument."); + const std::string& key = (itr++)->as_string(); + if (key.empty()) + throw torrent::bencode_error("d.custom.if_z: Empty key argument."); + if (itr == args.end()) + throw torrent::bencode_error("d.custom.if_z: Missing default argument."); + + try { + const std::string& val = download->bencode()->get_key("rtorrent").get_key("custom").get_key_string(key); + return val.empty() ? itr->as_string() : val; + } catch (torrent::bencode_error& e) { + return itr->as_string(); + } +} + +torrent::Object +retrieve_d_custom_map(core::Download* download, bool keys_only, const torrent::Object::list_type& args) { + if (args.begin() != args.end()) + throw torrent::bencode_error("d.custom.keys/items takes no arguments."); + + torrent::Object result = keys_only ? torrent::Object::create_list() : torrent::Object::create_map(); + torrent::Object::map_type& entries = download->bencode()->get_key("rtorrent").get_key("custom").as_map(); + + for (torrent::Object::map_type::const_iterator itr = entries.begin(), last = entries.end(); itr != last; itr++) { + if (keys_only) result.as_list().push_back(itr->first); + else result.as_map()[itr->first] = itr->second; + } + + return result; +} + torrent::Object retrieve_d_bitfield(core::Download* download) { const torrent::Bitfield* bitField = download->download()->file_list()->bitfield(); @@ -710,6 +745,9 @@ initialize_command_download() { CMD2_DL_STRING("d.custom", std::bind(&retrieve_d_custom, std::placeholders::_1, std::placeholders::_2)); CMD2_DL_STRING("d.custom_throw", std::bind(&retrieve_d_custom_throw, std::placeholders::_1, std::placeholders::_2)); CMD2_DL_LIST ("d.custom.set", std::bind(&apply_d_custom, std::placeholders::_1, std::placeholders::_2)); + CMD2_DL_LIST ("d.custom.if_z", std::bind(&retrieve_d_custom_if_z, std::placeholders::_1, std::placeholders::_2)); + CMD2_DL_LIST ("d.custom.keys", std::bind(&retrieve_d_custom_map, std::placeholders::_1, true, std::placeholders::_2)); + CMD2_DL_LIST ("d.custom.items", std::bind(&retrieve_d_custom_map, std::placeholders::_1, false, std::placeholders::_2)); CMD2_DL_VAR_STRING_PUBLIC("d.custom1", "rtorrent", "custom1"); CMD2_DL_VAR_STRING_PUBLIC("d.custom2", "rtorrent", "custom2"); diff --git a/src/command_ip.cc b/src/command_ip.cc index fec85081..b13db0a1 100644 --- a/src/command_ip.cc +++ b/src/command_ip.cc @@ -45,42 +45,14 @@ #include "globals.h" #include "command_helpers.h" -void -ipv4_filter_parse(const char* address, int value) { - uint32_t ip_values[4] = { 0, 0, 0, 0 }; - unsigned int block = rpc::ipv4_table::mask_bits; +#include +#include +#include +#include +#include +#include - char ip_dot; - int values_read; - - if ((values_read = sscanf(address, "%u%1[.]%u%1[.]%u%1[.]%u/%u", - ip_values + 0, &ip_dot, - ip_values + 1, &ip_dot, - ip_values + 2, &ip_dot, - ip_values + 3, - &block)) < 2 || - - // Make sure the dot is included. - (values_read < 7 && values_read % 2) || - - ip_values[0] >= 256 || - ip_values[1] >= 256 || - ip_values[2] >= 256 || - ip_values[3] >= 256 || - - block > rpc::ipv4_table::mask_bits) - throw torrent::input_error("Invalid address format."); - - // E.g. '10.10.' will be '10.10.0.0/16'. - if (values_read < 7) - block = 8 * (values_read / 2); - - lt_log_print(torrent::LOG_CONNECTION_DEBUG, "Adding ip filter for %u.%u.%u.%u/%u.", - ip_values[0], ip_values[1], ip_values[2], ip_values[3], block); - - torrent::PeerList::ipv4_filter()->insert((ip_values[0] << 24) + (ip_values[1] << 16) + (ip_values[2] << 8) + ip_values[3], - rpc::ipv4_table::mask_bits - block, value); -} +bool ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address_end); torrent::Object apply_ip_tables_insert_table(const std::string& args) { @@ -98,7 +70,8 @@ apply_ip_tables_size_data(const std::string& args) { if (itr != ip_tables.end()) throw torrent::input_error("IP table does not exist."); - return itr->table.sizeof_data(); + uint32_t size = itr->table.sizeof_data(); + return size; } torrent::Object @@ -110,20 +83,21 @@ apply_ip_tables_get(const torrent::Object::list_type& args) { const std::string& name = (args_itr++)->as_string(); const std::string& address = (args_itr++)->as_string(); - - // Move to a helper function, add support for addresses. - uint32_t ip_values[4]; - - if (sscanf(address.c_str(), "%u.%u.%u.%u", - ip_values + 0, ip_values + 1, ip_values + 2, ip_values + 3) != 4) - throw torrent::input_error("Invalid address format."); + uint32_t address_start; + uint32_t address_end; rpc::ip_table_list::iterator table_itr = ip_tables.find(name); if (table_itr == ip_tables.end()) throw torrent::input_error("Could not find ip table."); - return table_itr->table.at((ip_values[0] << 24) + (ip_values[1] << 16) + (ip_values[2] << 8) + ip_values[3]); + if (!ipv4_range_parse(address.c_str(), &address_start, &address_end)) + throw torrent::input_error("Invalid address format."); + + if(!table_itr->table.defined(address_start, address_end)) + throw torrent::input_error("No value defined for specified IP(s)."); + + return table_itr->table.at(address_start, address_end); } torrent::Object @@ -136,15 +110,6 @@ apply_ip_tables_add_address(const torrent::Object::list_type& args) { const std::string& name = (args_itr++)->as_string(); const std::string& address = (args_itr++)->as_string(); const std::string& value_str = (args_itr++)->as_string(); - - // Move to a helper function, add support for addresses. - uint32_t ip_values[4]; - unsigned int block = rpc::ipv4_table::mask_bits; - - if (sscanf(address.c_str(), "%u.%u.%u.%u/%u", - ip_values + 0, ip_values + 1, ip_values + 2, ip_values + 3, &block) < 4 || - block > rpc::ipv4_table::mask_bits) - throw torrent::input_error("Invalid address format."); int value; @@ -158,8 +123,13 @@ apply_ip_tables_add_address(const torrent::Object::list_type& args) { if (table_itr == ip_tables.end()) throw torrent::input_error("Could not find ip table."); - table_itr->table.insert((ip_values[0] << 24) + (ip_values[1] << 16) + (ip_values[2] << 8) + ip_values[3], - rpc::ipv4_table::mask_bits - block, value); + uint32_t address_start; + uint32_t address_end; + + if (ipv4_range_parse(address.c_str(), &address_start, &address_end)) + table_itr->table.insert(address_start, address_end, value); + else + throw torrent::input_error("Invalid address format."); return torrent::Object(); } @@ -168,6 +138,158 @@ apply_ip_tables_add_address(const torrent::Object::list_type& args) { // IPv4 filter functions: // +/////////////////////////////////////////////////////////// +// IPV4_RANGE_PARSE parses string into an ip range +// +// should be compatible with lines in p2p files +// everything in address before colon is ignored +// +// ip range can be single ip in which case start=end +// ip range can be cidr notation a.b.c.d/e +// ip range can be explicit range like in p2p line a.b.c.d-w.x.y.z +// +// returns false if line does not contain valid ip or ip range +// address_start and address_end will contain start and end ip +// +// addresses parsed are returned in host byte order +// to get network byte order call htonl(address) +/////////////////////////////////////////////////////////// +bool +ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address_end) { + // same length as buffer used to do reads so no worries about overflow + char address_copy[4096]; + bool valid = false; + char address_start_str[20]; + int address_start_index=0; + struct sockaddr_in sa_start; + *address_start=0; + *address_end=0; + + // get rid of everything after '#' comments + // copy everything up to '#' to address_copy and work from there + while(address[address_start_index] != '#' && address[address_start_index] != '\r' && + address[address_start_index] != '\n' && address[address_start_index] != '\0' && + address_start_index < 4096 ) { + + address_copy[address_start_index] = address[address_start_index]; + address_start_index++; + } + + address_copy[address_start_index] = '\0'; + address_start_index=0; + + // skip everything up to and including last ':' character and whitespace + const char* addr = strrchr(address_copy, ':'); + + addr = (addr == NULL) ? address_copy : addr + 1; + + while(addr[0] == ' ' || addr[0] == '\t') + addr++; + + while(((addr[0] >= '0' && addr[0] <= '9') || addr[0] == '.') && address_start_index < 19) { + address_start_str[address_start_index] = addr[0]; + address_start_index++; + addr++; + } + + address_start_str[address_start_index] = '\0'; + + if(strchr(addr, '-') != NULL) { + // explicit range + char address_end_str[20]; + int address_end_index=0; + struct sockaddr_in sa_end; + + while(addr[0] == '-' || addr[0] == ' ' || addr[0] == '\t') + addr++; + + while(((addr[0] >= '0' && addr[0] <= '9') || addr[0] == '.') && address_end_index < 19) { + address_end_str[address_end_index] = addr[0]; + address_end_index++; + addr++; + } + + address_end_str[address_end_index] = '\0'; + + if(inet_pton(AF_INET, address_start_str, &(sa_start.sin_addr)) != 0 && inet_pton(AF_INET, address_end_str, &(sa_end.sin_addr)) != 0) { + *address_start = ntohl(sa_start.sin_addr.s_addr); + *address_end = ntohl(sa_end.sin_addr.s_addr); + + if(*address_start <= *address_end) + valid=true; + } + } else if(strchr(addr, '/') != NULL) { + // cidr range + char mask_bits_str[20]; + int mask_bits_index=0; + uint32_t mask_bits; + + while(addr[0] == '/' || addr[0] == ' ' || addr[0] == '\t') + addr++; + + while( (addr[0] >= '0' && addr[0] <= '9') && mask_bits_index < 19) { + mask_bits_str[mask_bits_index] = addr[0]; + mask_bits_index++; + addr++; + } + + mask_bits_str[mask_bits_index] = '\0'; + + if(inet_pton(AF_INET, address_start_str, &(sa_start.sin_addr)) != 0 && sscanf(mask_bits_str, "%u", &mask_bits) != 0) { + if(mask_bits <=32) { + uint32_t ip=ntohl(sa_start.sin_addr.s_addr); + uint32_t mask=0; + uint32_t end_mask=0; + + mask = (~mask) << (32-mask_bits); + *address_start = ip & mask; + end_mask = (~end_mask) >> mask_bits; + *address_end = (ip & mask) | end_mask; + + valid=true; + } + } + } else { + // single ip + if(inet_pton(AF_INET, address_start_str, &(sa_start.sin_addr)) != 0) { + *address_start = ntohl(sa_start.sin_addr.s_addr); + *address_end = *address_start; + + valid=true; + } + } + + return valid; +} + +/////////////////////////////////////////////////////////// +// IPV4_FILTER_PARSE +// +// should now be compatible with lines in p2p files +// +// addresses in table MUST be in host byte order +// ntohl is called after parsing ip address(es) +/////////////////////////////////////////////////////////// +void +ipv4_filter_parse(const char* address, int value) { + uint32_t address_start; + uint32_t address_end; + + if (ipv4_range_parse(address, &address_start, &address_end) ) { + torrent::PeerList::ipv4_filter()->insert(address_start, address_end, value); + + char start_str[INET_ADDRSTRLEN]; + char end_str[INET_ADDRSTRLEN]; + uint32_t net_start = htonl(address_start); + uint32_t net_end = htonl(address_end); + + inet_ntop(AF_INET, &net_start, start_str, INET_ADDRSTRLEN); + inet_ntop(AF_INET, &net_end, end_str, INET_ADDRSTRLEN); + + lt_log_print(torrent::LOG_CONNECTION_DEBUG, "Adding ip filter for %s-%s.", start_str, end_str); + } +} + torrent::Object apply_ipv4_filter_size_data() { return torrent::PeerList::ipv4_filter()->sizeof_data(); @@ -175,14 +297,16 @@ apply_ipv4_filter_size_data() { torrent::Object apply_ipv4_filter_get(const std::string& args) { - // Move to a helper function, add support for addresses. - uint32_t ip_values[4]; + uint32_t address_start; + uint32_t address_end; - if (sscanf(args.c_str(), "%u.%u.%u.%u", - ip_values + 0, ip_values + 1, ip_values + 2, ip_values + 3) != 4) + if (!ipv4_range_parse(args.c_str(), &address_start, &address_end)) throw torrent::input_error("Invalid address format."); - return torrent::PeerList::ipv4_filter()->at((ip_values[0] << 24) + (ip_values[1] << 16) + (ip_values[2] << 8) + ip_values[3]); + if(!torrent::PeerList::ipv4_filter()->defined(address_start, address_end)) + throw torrent::input_error("No value defined for specified IP(s)."); + + return torrent::PeerList::ipv4_filter()->at(address_start, address_end); } torrent::Object @@ -240,41 +364,34 @@ apply_ipv4_filter_load(const torrent::Object::list_type& args) { return torrent::Object(); } -static void -append_table(torrent::ipv4_table::base_type* extent, torrent::Object::list_type& result) { - torrent::ipv4_table::table_type::iterator first = extent->table.begin(); - torrent::ipv4_table::table_type::iterator last = extent->table.end(); - - while (first != last) { - if (first->first != NULL) { - // Do something more here?... - append_table(first->first, result); - - } else if (first->second != 0) { - uint32_t position = extent->partition_pos(first); - - char buffer[256]; - snprintf(buffer, 256, "%u.%u.%u.%u/%u %s", - (position >> 24) & 0xff, - (position >> 16) & 0xff, - (position >> 8) & 0xff, - (position >> 0) & 0xff, - extent->mask_bits, - torrent::option_as_string(torrent::OPTION_IP_FILTER, first->second)); - - result.push_back((std::string)buffer); - } - - first++; - } -} - torrent::Object apply_ipv4_filter_dump() { torrent::Object raw_result = torrent::Object::create_list(); torrent::Object::list_type& result = raw_result.as_list(); - append_table(torrent::PeerList::ipv4_filter()->data(), result); + torrent::ipv4_table::range_map_type range_map = torrent::PeerList::ipv4_filter()->range_map; + torrent::ipv4_table::range_map_type::iterator iter = range_map.begin(); + + while(iter != range_map.end()) { + char buffer[64]; + uint32_t address_start = iter->first; + uint32_t address_end = (iter->second).first; + int value = (iter->second).second; + + char start_str[INET_ADDRSTRLEN]; + char end_str[INET_ADDRSTRLEN]; + uint32_t net_start = htonl(address_start); + uint32_t net_end = htonl(address_end); + + inet_ntop(AF_INET, &net_start, start_str, INET_ADDRSTRLEN); + inet_ntop(AF_INET, &net_end, end_str, INET_ADDRSTRLEN); + + snprintf(buffer, 64, "%s-%s %s", start_str, end_str, torrent::option_as_string(torrent::OPTION_IP_FILTER, value)); + + result.push_back((std::string)buffer); + + iter++; + } return raw_result; } diff --git a/src/command_network.cc b/src/command_network.cc index 3594c302..092287a5 100644 --- a/src/command_network.cc +++ b/src/command_network.cc @@ -167,8 +167,7 @@ apply_scgi(const std::string& arg, int type) { sa.sa_inet()->clear(); saPtr = &sa; - lt_log_print(torrent::LOG_RPC_EVENTS, - "The SCGI socket has not been bound to any address and likely poses a security risk."); + lt_log_print(torrent::LOG_RPC_EVENTS, "SCGI socket is open to any address and is a security risk"); } else if (std::sscanf(arg.c_str(), "%1023[^:]:%i%c", address, &port, &dummy) == 2 || std::sscanf(arg.c_str(), "[%64[^]]]:%i%c", address, &port, &dummy) == 2) { // [xx::xx]:port format @@ -177,8 +176,7 @@ apply_scgi(const std::string& arg, int type) { saPtr = ai->address(); - lt_log_print(torrent::LOG_RPC_EVENTS, - "The SCGI socket is bound to a specific network device yet may still pose a security risk, consider using 'scgi_local'."); + lt_log_print(torrent::LOG_RPC_EVENTS, "SCGI socket is bound to an address and might be a security risk"); } else { throw torrent::input_error("Could not parse address."); diff --git a/src/command_throttle.cc b/src/command_throttle.cc index bfe95eea..fde828a5 100644 --- a/src/command_throttle.cc +++ b/src/command_throttle.cc @@ -96,12 +96,15 @@ torrent::Object apply_throttle(const torrent::Object::list_type& args, bool up) { torrent::Object::list_const_iterator argItr = args.begin(); + if (argItr == args.end()) + throw torrent::input_error("Missing throttle name."); + const std::string& name = argItr->as_string(); if (name.empty() || name == "NULL") - throw torrent::input_error("Invalid throttle name."); + throw torrent::input_error("Invalid throttle name '" + name + "'."); - if ((++argItr)->as_string().empty()) - return torrent::Object(); + if (++argItr == args.end() || argItr->as_string().empty()) + throw torrent::input_error("Missing throttle rate for '" + name + "'."); int64_t rate; rpc::parse_whole_value_nothrow(argItr->as_string().c_str(), &rate); diff --git a/src/command_ui.cc b/src/command_ui.cc index d65859e0..4938e213 100644 --- a/src/command_ui.cc +++ b/src/command_ui.cc @@ -41,8 +41,10 @@ #include #include +#include #include #include +#include #include "core/manager.h" #include "core/view_manager.h" @@ -149,6 +151,41 @@ apply_cat(rpc::target_type target, const torrent::Object& rawArgs) { return result; } +torrent::Object +apply_value(rpc::target_type target, const torrent::Object::list_type& args) { + if (args.size() < 1) + throw torrent::input_error("'value' takes at least a number argument!"); + if (args.size() > 2) + throw torrent::input_error("'value' takes at most two arguments!"); + + torrent::Object::value_type val = 0; + if (args.front().is_value()) { + val = args.front().as_value(); + } else { + int base = args.size() > 1 ? args.back().is_value() ? + args.back().as_value() : strtol(args.back().as_string().c_str(), NULL, 10) : 10; + char* endptr = 0; + + val = strtoll(args.front().as_string().c_str(), &endptr, base); + while (*endptr == ' ' || *endptr == '\n') ++endptr; + if (*endptr) + throw torrent::input_error("Junk at end of number: " + args.front().as_string()); + } + + return val; +} + +torrent::Object +apply_try(rpc::target_type target, const torrent::Object& args) { + try { + return rpc::call_object(args, target); + } catch (torrent::input_error& e) { + lt_log_print(torrent::LOG_RPC_EVENTS, "try command caught input_error: %s", e.what()); + } + + return torrent::Object(); +} + // Move these boolean operators to a new file. inline bool @@ -568,6 +605,128 @@ apply_elapsed_greater(const torrent::Object::list_type& args) { return (int64_t)(start_time != 0 && rak::timer::current_seconds() - start_time > rpc::convert_to_value(args.back())); } +inline std::vector +as_vector(const torrent::Object::list_type& args) { + if (args.size() == 0) + throw torrent::input_error("Wrong argument count in as_vector."); + + std::vector result; + + for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) { + + if (itr->is_value()) { + result.push_back(itr->as_value()); + } else if (itr->is_string()) { + result.push_back(rpc::convert_to_value(itr->as_string())); + } else if (itr->is_list()) { + std::vector subResult = as_vector(itr->as_list()); + result.insert(result.end(), subResult.begin(), subResult.end()); + } else { + throw torrent::input_error("Wrong type supplied to as_vector."); + } + + } + + return result; +} + +int64_t +apply_math_basic(const char* name, const std::function op, const torrent::Object::list_type& args) { + int64_t val = 0, rhs = 0; + bool divides = !strcmp(name, "math.div") || !strcmp(name, "math.mod"); + + if (args.size() == 0) + throw torrent::input_error(std::string(name) + ": No arguments provided!"); + + for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) { + + if (itr->is_value()) { + rhs = itr->as_value(); + } else if (itr->is_string()) { + rhs = rpc::convert_to_value(itr->as_string()); + } else if (itr->is_list()) { + rhs = apply_math_basic(name, op, itr->as_list()); + } else { + throw torrent::input_error(std::string(name) + ": Wrong argument type"); + } + + if (divides && !rhs && itr != args.begin()) + throw torrent::input_error(std::string(name) + ": Division by zero!"); + + val = itr == args.begin() ? rhs : op(val, rhs); + + } + + return val; +} + +int64_t +apply_arith_basic(const std::function op, const torrent::Object::list_type& args) { + if (args.size() == 0) + throw torrent::input_error("Wrong argument count in apply_arith_basic."); + + int64_t val = 0; + + for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) { + + if (itr->is_value()) { + val = itr == args.begin() ? itr->as_value() : (op(val, itr->as_value()) ? val : itr->as_value()); + } else if (itr->is_string()) { + int64_t cval = rpc::convert_to_value(itr->as_string()); + val = itr == args.begin() ? cval : (op(val, cval) ? val : cval); + } else if (itr->is_list()) { + int64_t fval = apply_arith_basic(op, itr->as_list()); + val = itr == args.begin() ? fval : (op(val, fval) ? val : fval); + } else { + throw torrent::input_error("Wrong type supplied to apply_arith_basic."); + } + + } + + return val; +} + +int64_t +apply_arith_count(const torrent::Object::list_type& args) { + if (args.size() == 0) + throw torrent::input_error("Wrong argument count in apply_arith_count."); + + int64_t val = 0; + + for (torrent::Object::list_const_iterator itr = args.begin(), last = args.end(); itr != last; itr++) { + + switch (itr->type()) { + case torrent::Object::TYPE_VALUE: + case torrent::Object::TYPE_STRING: + val++; + break; + case torrent::Object::TYPE_LIST: + val += apply_arith_count(itr->as_list()); + break; + default: + throw torrent::input_error("Wrong type supplied to apply_arith_count."); + } + + } + + return val; +} + +int64_t +apply_arith_other(const char* op, const torrent::Object::list_type& args) { + if (args.size() == 0) + throw torrent::input_error("Wrong argument count in apply_arith_other."); + + if (strcmp(op, "average") == 0) { + return (int64_t)(apply_math_basic(op, std::plus(), args) / apply_arith_count(args)); + } else if (strcmp(op, "median") == 0) { + std::vector result = as_vector(args); + return (int64_t)rak::median(result.begin(), result.end()); + } else { + throw torrent::input_error("Wrong operation supplied to apply_arith_other."); + } +} + void initialize_command_ui() { CMD2_VAR_STRING("keys.layout", "qwerty"); @@ -608,12 +767,18 @@ initialize_command_ui() { CMD2_VAR_STRING("view.temp_filter.excluded", "default,started,stopped"); CMD2_VAR_BOOL ("view.temp_filter.log", 0); + CMD2_VAR_VALUE ("ui.throttle.global.step.small", 5); + CMD2_VAR_VALUE ("ui.throttle.global.step.medium", 50); + CMD2_VAR_VALUE ("ui.throttle.global.step.large", 500); + // TODO: Add 'option_string' for rtorrent-specific options. CMD2_VAR_STRING("ui.torrent_list.layout", "full"); // Move. CMD2_ANY("print", &apply_print); CMD2_ANY("cat", &apply_cat); + CMD2_ANY_LIST("value", &apply_value); + CMD2_ANY("try", &apply_try); CMD2_ANY("if", std::bind(&apply_if, std::placeholders::_1, std::placeholders::_2, 0)); CMD2_ANY("not", &apply_not); CMD2_ANY("false", &apply_false); @@ -639,6 +804,17 @@ initialize_command_ui() { CMD2_ANY_VALUE("convert.xb", std::bind(&apply_to_xb, std::placeholders::_2)); CMD2_ANY_VALUE("convert.throttle", std::bind(&apply_to_throttle, std::placeholders::_2)); + CMD2_ANY_LIST("math.add", std::bind(&apply_math_basic, "math.add", std::plus(), std::placeholders::_2)); + CMD2_ANY_LIST("math.sub", std::bind(&apply_math_basic, "math.sub", std::minus(), std::placeholders::_2)); + CMD2_ANY_LIST("math.mul", std::bind(&apply_math_basic, "math.mul", std::multiplies(), std::placeholders::_2)); + CMD2_ANY_LIST("math.div", std::bind(&apply_math_basic, "math.div", std::divides(), std::placeholders::_2)); + CMD2_ANY_LIST("math.mod", std::bind(&apply_math_basic, "math.mod", std::modulus(), std::placeholders::_2)); + CMD2_ANY_LIST("math.min", std::bind(&apply_arith_basic, std::less(), std::placeholders::_2)); + CMD2_ANY_LIST("math.max", std::bind(&apply_arith_basic, std::greater(), std::placeholders::_2)); + CMD2_ANY_LIST("math.cnt", std::bind(&apply_arith_count, std::placeholders::_2)); + CMD2_ANY_LIST("math.avg", std::bind(&apply_arith_other, "average", std::placeholders::_2)); + CMD2_ANY_LIST("math.med", std::bind(&apply_arith_other, "median", std::placeholders::_2)); + CMD2_ANY_LIST ("elapsed.less", std::bind(&apply_elapsed_less, std::placeholders::_2)); CMD2_ANY_LIST ("elapsed.greater", std::bind(&apply_elapsed_greater, std::placeholders::_2)); } diff --git a/src/core/manager.cc b/src/core/manager.cc index b00fe6d4..2130b453 100644 --- a/src/core/manager.cc +++ b/src/core/manager.cc @@ -416,7 +416,7 @@ path_expand(std::vector* paths, const std::string& pattern) { itr->update((r.pattern()[0] != '.') ? utils::Directory::update_hide_dot : 0); itr->erase(std::remove_if(itr->begin(), itr->end(), rak::on(rak::mem_ref(&utils::directory_entry::d_name), std::not1(r))), itr->end()); - std::transform(itr->begin(), itr->end(), std::back_inserter(nextCache), rak::bind1st(std::ptr_fun(&path_expand_transform), itr->path() + "/")); + std::transform(itr->begin(), itr->end(), std::back_inserter(nextCache), rak::bind1st(std::ptr_fun(&path_expand_transform), itr->path() + (itr->path() == "/" ? "" : "/"))); } currentCache.clear(); diff --git a/src/display/window_download_list.cc b/src/display/window_download_list.cc index b4a94324..7587956a 100644 --- a/src/display/window_download_list.cc +++ b/src/display/window_download_list.cc @@ -86,6 +86,15 @@ WindowDownloadList::redraw() { if (m_view->empty_visible() || m_canvas->width() < 5 || m_canvas->height() < 2) return; + // show "X of Y" + if (m_canvas->width() > 16 + 8 + m_view->name().length()) { + int item_idx = m_view->focus() - m_view->begin_visible(); + if (item_idx == int(m_view->size())) + m_canvas->print(m_canvas->width() - 16, 0, "[ none of %-5d]", m_view->size()); + else + m_canvas->print(m_canvas->width() - 16, 0, "[%5d of %-5d]", item_idx + 1, m_view->size()); + } + int layout_height; const std::string layout_name = rpc::call_command_string("ui.torrent_list.layout"); diff --git a/src/input/input_event.cc b/src/input/input_event.cc index 69dfef05..72159d62 100644 --- a/src/input/input_event.cc +++ b/src/input/input_event.cc @@ -38,8 +38,7 @@ #include "input_event.h" -//ncurses.h must be included last since sys/mman.h on Solaris munges ERR. -#include +#include "display/attributes.h" namespace input { diff --git a/src/main.cc b/src/main.cc index 1a06b666..38bb1886 100644 --- a/src/main.cc +++ b/src/main.cc @@ -197,6 +197,7 @@ main(int argc, char** argv) { SignalHandler::set_ignore(SIGPIPE); SignalHandler::set_handler(SIGINT, std::bind(&Control::receive_normal_shutdown, control)); + SignalHandler::set_handler(SIGHUP, std::bind(&Control::receive_normal_shutdown, control)); SignalHandler::set_handler(SIGTERM, std::bind(&Control::receive_quick_shutdown, control)); SignalHandler::set_handler(SIGWINCH, std::bind(&display::Manager::force_redraw, control->display())); SignalHandler::set_handler(SIGSEGV, std::bind(&do_panic, SIGSEGV)); @@ -248,6 +249,9 @@ main(int argc, char** argv) { rpc::parse_command_multiple (rpc::make_target(), + "method.insert = event.view.hide,multi|rlookup|static\n" + "method.insert = event.view.show,multi|rlookup|static\n" + "method.insert = event.download.inserted,multi|rlookup|static\n" "method.insert = event.download.inserted_new,multi|rlookup|static\n" "method.insert = event.download.inserted_session,multi|rlookup|static\n" diff --git a/src/rpc/ip_table_list.h b/src/rpc/ip_table_list.h index 8a4e16cd..06a5408d 100644 --- a/src/rpc/ip_table_list.h +++ b/src/rpc/ip_table_list.h @@ -45,7 +45,7 @@ namespace rpc { -typedef torrent::extents ipv4_table; +typedef torrent::extents ipv4_table; struct ip_table_node { std::string name; diff --git a/src/thread_worker.cc b/src/thread_worker.cc index d6fa2f61..939d7026 100644 --- a/src/thread_worker.cc +++ b/src/thread_worker.cc @@ -113,14 +113,15 @@ ThreadWorker::change_xmlrpc_log() { if (scgi() == NULL) return; - if (scgi()->log_fd() != -1) + if (scgi()->log_fd() != -1) { ::close(scgi()->log_fd()); - - if (m_xmlrpcLog.empty()) { + scgi()->set_log_fd(-1); control->core()->push_log("Closed XMLRPC log."); - return; } + if (m_xmlrpcLog.empty()) + return; + scgi()->set_log_fd(open(rak::path_expand(m_xmlrpcLog).c_str(), O_WRONLY | O_APPEND | O_CREAT, 0644)); if (scgi()->log_fd() == -1) { diff --git a/src/ui/download.cc b/src/ui/download.cc index 6058fe77..a82e2f77 100644 --- a/src/ui/download.cc +++ b/src/ui/download.cc @@ -54,6 +54,7 @@ #include "display/window_download_statusbar.h" #include "display/text_element_string.h" +#include "rpc/parse_commands.h" #include "control.h" #include "download.h" @@ -395,20 +396,20 @@ Download::bind_keys() { const char* keys = control->ui()->get_throttle_keys(); - m_bindings[keys[ 0]] = std::bind(&Download::adjust_up_throttle, this, 1); - m_bindings[keys[ 1]] = std::bind(&Download::adjust_up_throttle, this, -1); - m_bindings[keys[ 2]] = std::bind(&Download::adjust_down_throttle, this, 1); - m_bindings[keys[ 3]] = std::bind(&Download::adjust_down_throttle, this, -1); + m_bindings[keys[ 0]] = std::bind(&Download::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.small")); + m_bindings[keys[ 1]] = std::bind(&Download::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.small")); + m_bindings[keys[ 2]] = std::bind(&Download::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.small")); + m_bindings[keys[ 3]] = std::bind(&Download::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.small")); - m_bindings[keys[ 4]] = std::bind(&Download::adjust_up_throttle, this, 5); - m_bindings[keys[ 5]] = std::bind(&Download::adjust_up_throttle, this, -5); - m_bindings[keys[ 6]] = std::bind(&Download::adjust_down_throttle, this, 5); - m_bindings[keys[ 7]] = std::bind(&Download::adjust_down_throttle, this, -5); + m_bindings[keys[ 4]] = std::bind(&Download::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.medium")); + m_bindings[keys[ 5]] = std::bind(&Download::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.medium")); + m_bindings[keys[ 6]] = std::bind(&Download::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.medium")); + m_bindings[keys[ 7]] = std::bind(&Download::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.medium")); - m_bindings[keys[ 8]] = std::bind(&Download::adjust_up_throttle, this, 50); - m_bindings[keys[ 9]] = std::bind(&Download::adjust_up_throttle, this, -50); - m_bindings[keys[10]] = std::bind(&Download::adjust_down_throttle, this, 50); - m_bindings[keys[11]] = std::bind(&Download::adjust_down_throttle, this, -50); + m_bindings[keys[ 8]] = std::bind(&Download::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.large")); + m_bindings[keys[ 9]] = std::bind(&Download::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.large")); + m_bindings[keys[10]] = std::bind(&Download::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.large")); + m_bindings[keys[11]] = std::bind(&Download::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.large")); } } diff --git a/src/ui/download.h b/src/ui/download.h index c896c528..d4ba0a7f 100644 --- a/src/ui/download.h +++ b/src/ui/download.h @@ -89,7 +89,8 @@ public: void adjust_up_throttle(int throttle); void adjust_down_throttle(int throttle); - display::Window* window() { return NULL; } + display::Window* window() { return NULL; } + core::Download* download() { return m_download; }; private: Download(const Download&); diff --git a/src/ui/download_list.cc b/src/ui/download_list.cc index 7f388e5e..2f2241dd 100644 --- a/src/ui/download_list.cc +++ b/src/ui/download_list.cc @@ -126,16 +126,13 @@ DownloadList::set_current_view(const std::string& name) { return dynamic_cast(m_uiArray[DISPLAY_DOWNLOAD_LIST])->receive_change_view(name); } -// This should also do focus_next() or something. void DownloadList::unfocus_download(core::Download* d) { - if (current_view()->focus() >= current_view()->end_visible() || *current_view()->focus() != d) - return; - - if (m_state == DISPLAY_DOWNLOAD) + if (m_state == DISPLAY_DOWNLOAD && d == static_cast(m_uiArray[DISPLAY_DOWNLOAD])->download()) activate_display(DISPLAY_DOWNLOAD_LIST); - current_view()->next_focus(); + if (*current_view()->focus() == d && current_view()->focus() < current_view()->end_visible()) + current_view()->next_focus(); } void diff --git a/src/ui/element_download_list.cc b/src/ui/element_download_list.cc index 3f34bb90..90a769ab 100644 --- a/src/ui/element_download_list.cc +++ b/src/ui/element_download_list.cc @@ -220,7 +220,14 @@ ElementDownloadList::receive_change_view(const std::string& name) { return; } + std::string old_name = view() ? view()->name() : ""; + if (!old_name.empty()) + rpc::commands.call_catch("event.view.hide", rpc::make_target(), name, + "View hide event action failed: "); set_view(*itr); + if (!old_name.empty()) + rpc::commands.call_catch("event.view.show", rpc::make_target(), old_name, + "View show event action failed: "); } void diff --git a/src/ui/root.cc b/src/ui/root.cc index e02fc938..29e45d3f 100644 --- a/src/ui/root.cc +++ b/src/ui/root.cc @@ -140,20 +140,20 @@ Root::setup_keys() { const char* keys = get_throttle_keys(); - m_bindings[keys[ 0]] = std::bind(&Root::adjust_up_throttle, this, 1); - m_bindings[keys[ 1]] = std::bind(&Root::adjust_up_throttle, this, -1); - m_bindings[keys[ 2]] = std::bind(&Root::adjust_down_throttle, this, 1); - m_bindings[keys[ 3]] = std::bind(&Root::adjust_down_throttle, this, -1); + m_bindings[keys[ 0]] = std::bind(&Root::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.small")); + m_bindings[keys[ 1]] = std::bind(&Root::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.small")); + m_bindings[keys[ 2]] = std::bind(&Root::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.small")); + m_bindings[keys[ 3]] = std::bind(&Root::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.small")); - m_bindings[keys[ 4]] = std::bind(&Root::adjust_up_throttle, this, 5); - m_bindings[keys[ 5]] = std::bind(&Root::adjust_up_throttle, this, -5); - m_bindings[keys[ 6]] = std::bind(&Root::adjust_down_throttle, this, 5); - m_bindings[keys[ 7]] = std::bind(&Root::adjust_down_throttle, this, -5); + m_bindings[keys[ 4]] = std::bind(&Root::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.medium")); + m_bindings[keys[ 5]] = std::bind(&Root::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.medium")); + m_bindings[keys[ 6]] = std::bind(&Root::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.medium")); + m_bindings[keys[ 7]] = std::bind(&Root::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.medium")); - m_bindings[keys[ 8]] = std::bind(&Root::adjust_up_throttle, this, 50); - m_bindings[keys[ 9]] = std::bind(&Root::adjust_up_throttle, this, -50); - m_bindings[keys[10]] = std::bind(&Root::adjust_down_throttle, this, 50); - m_bindings[keys[11]] = std::bind(&Root::adjust_down_throttle, this, -50); + m_bindings[keys[ 8]] = std::bind(&Root::adjust_up_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.large")); + m_bindings[keys[ 9]] = std::bind(&Root::adjust_up_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.large")); + m_bindings[keys[10]] = std::bind(&Root::adjust_down_throttle, this, (int) rpc::call_command_value("ui.throttle.global.step.large")); + m_bindings[keys[11]] = std::bind(&Root::adjust_down_throttle, this, (int) -rpc::call_command_value("ui.throttle.global.step.large")); m_bindings['\x0C'] = std::bind(&display::Manager::force_redraw, m_control->display()); // ^L m_bindings['\x11'] = std::bind(&Control::receive_normal_shutdown, m_control); // ^Q