Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package rtorrent for openSUSE:Factory checked in at 2026-04-09 17:20:32 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rtorrent (Old) and /work/SRC/openSUSE:Factory/.rtorrent.new.21863 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rtorrent" Thu Apr 9 17:20:32 2026 rev:28 rq:1345576 version:0.16.9 Changes: -------- --- /work/SRC/openSUSE:Factory/rtorrent/rtorrent.changes 2026-03-16 14:21:23.411405589 +0100 +++ /work/SRC/openSUSE:Factory/.rtorrent.new.21863/rtorrent.changes 2026-04-09 17:20:45.933664272 +0200 @@ -1,0 +2,8 @@ +Thu Apr 9 13:57:46 UTC 2026 - Jan Engelhardt <[email protected]> + +- Update to release 0.16.9 + * Improved DNS cache handling and various bug fixes + * Trusted/Untrusted XMLRPC Connection Security Model + * SCGI Systemd Socket Activation Support + +------------------------------------------------------------------- Old: ---- rtorrent-0.16.8.tar.gz New: ---- rtorrent-0.16.9.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rtorrent.spec ++++++ --- /var/tmp/diff_new_pack.nmvy9M/_old 2026-04-09 17:20:47.481728386 +0200 +++ /var/tmp/diff_new_pack.nmvy9M/_new 2026-04-09 17:20:47.481728386 +0200 @@ -17,7 +17,7 @@ Name: rtorrent -Version: 0.16.8 +Version: 0.16.9 Release: 0 Summary: Console-based BitTorrent client License: SUSE-GPL-2.0+-with-openssl-exception ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.nmvy9M/_old 2026-04-09 17:20:47.513729711 +0200 +++ /var/tmp/diff_new_pack.nmvy9M/_new 2026-04-09 17:20:47.517729877 +0200 @@ -1,5 +1,5 @@ -mtime: 1773653033 -commit: 327ebbbfdc243b23a33c29682bee8a4bb249dfd98cf37832edc02d065bce5495 +mtime: 1775743098 +commit: 1a5485ec9c6a9a88e829354c9404787bd95698e5e76215c5e1c7aac77b333ebb url: https://src.opensuse.org/jengelh/rtorrent revision: master ++++++ build.specials.obscpio ++++++ ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-04-09 15:58:29.000000000 +0200 @@ -0,0 +1 @@ +.osc ++++++ rtorrent-0.16.8.tar.gz -> rtorrent-0.16.9.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/Makefile.in new/rtorrent-0.16.9/Makefile.in --- old/rtorrent-0.16.8/Makefile.in 2026-03-15 18:07:04.000000000 +0100 +++ new/rtorrent-0.16.9/Makefile.in 2026-04-06 16:10:12.000000000 +0200 @@ -341,6 +341,8 @@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ +SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ +SYSTEMD_LIBS = @SYSTEMD_LIBS@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/config.h.in new/rtorrent-0.16.9/config.h.in --- old/rtorrent-0.16.8/config.h.in 2026-03-15 18:07:03.000000000 +0100 +++ new/rtorrent-0.16.9/config.h.in 2026-04-06 16:10:11.000000000 +0200 @@ -87,6 +87,9 @@ /* Define to 1 if you have the <string.h> header file. */ #undef HAVE_STRING_H +/* Support for systemd socket activation. */ +#undef HAVE_SYSTEMD + /* Define to 1 if you have the <sys/stat.h> header file. */ #undef HAVE_SYS_STAT_H diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/configure new/rtorrent-0.16.9/configure --- old/rtorrent-0.16.8/configure 2026-03-15 18:07:03.000000000 +0100 +++ new/rtorrent-0.16.9/configure 2026-04-06 16:10:11.000000000 +0200 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.72 for rtorrent 0.16.8. +# Generated by GNU Autoconf 2.72 for rtorrent 0.16.9. # # Report bugs to <[email protected]>. # @@ -614,8 +614,8 @@ # Identity of this package. PACKAGE_NAME='rtorrent' PACKAGE_TARNAME='rtorrent' -PACKAGE_VERSION='0.16.8' -PACKAGE_STRING='rtorrent 0.16.8' +PACKAGE_VERSION='0.16.9' +PACKAGE_STRING='rtorrent 0.16.9' PACKAGE_BUGREPORT='[email protected]' PACKAGE_URL='' @@ -656,6 +656,8 @@ am__EXEEXT_TRUE LTLIBOBJS LIBOBJS +SYSTEMD_LIBS +SYSTEMD_CFLAGS LUA_INCLUDE LUA_LIB pkgluaexecdir @@ -834,6 +836,7 @@ with_xmlrpc_c with_lua with_xmlrpc_tinyxml2 +with_systemd enable_year2038 ' ac_precious_vars='build_alias @@ -861,7 +864,9 @@ DEPENDENCIES_LIBS LUA LUA_LIB -LUA_INCLUDE' +LUA_INCLUDE +SYSTEMD_CFLAGS +SYSTEMD_LIBS' # Initialize some variables set by options. @@ -1410,7 +1415,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -'configure' configures rtorrent 0.16.8 to adapt to many kinds of systems. +'configure' configures rtorrent 0.16.9 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1481,7 +1486,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of rtorrent 0.16.8:";; + short | recursive ) echo "Configuration of rtorrent 0.16.9:";; esac cat <<\_ACEOF @@ -1526,6 +1531,8 @@ --with-xmlrpc-c=PATH enable XMLRPC-C support --with-lua enable LUA support --with-xmlrpc-tinyxml2 enable XMLRPC support via tinyxml2 + --with-systemd enable systemd socket activation support + [[default=no]] Some influential environment variables: CC C compiler command @@ -1560,6 +1567,10 @@ LUA The Lua interpreter, e.g. /usr/bin/lua5.1 LUA_LIB The Lua library, e.g. -llua5.1 LUA_INCLUDE The Lua includes, e.g. -I/usr/include/lua5.1 + SYSTEMD_CFLAGS + C compiler flags for SYSTEMD, overriding pkg-config + SYSTEMD_LIBS + linker flags for SYSTEMD, overriding pkg-config Use these variables to override the choices made by 'configure' or to help it to find libraries and programs with nonstandard names/locations. @@ -1628,7 +1639,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -rtorrent configure 0.16.8 +rtorrent configure 0.16.9 generated by GNU Autoconf 2.72 Copyright (C) 2023 Free Software Foundation, Inc. @@ -2257,7 +2268,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by rtorrent $as_me 0.16.8, which was +It was created by rtorrent $as_me 0.16.9, which was generated by GNU Autoconf 2.72. Invocation command line was $ $0$ac_configure_args_raw @@ -3950,7 +3961,7 @@ # Define the identity of the package. PACKAGE='rtorrent' - VERSION='0.16.8' + VERSION='0.16.9' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h @@ -21553,19 +21564,19 @@ fi pkg_failed=no -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent >= 0.16.8" >&5 -printf %s "checking for libtorrent >= 0.16.8... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libtorrent >= 0.16.9" >&5 +printf %s "checking for libtorrent >= 0.16.9... " >&6; } if test -n "$DEPENDENCIES_CFLAGS"; then pkg_cv_DEPENDENCIES_CFLAGS="$DEPENDENCIES_CFLAGS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent >= 0.16.8\""; } >&5 - ($PKG_CONFIG --exists --print-errors "libtorrent >= 0.16.8") 2>&5 + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent >= 0.16.9\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libtorrent >= 0.16.9") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_DEPENDENCIES_CFLAGS=`$PKG_CONFIG --cflags "libtorrent >= 0.16.8" 2>/dev/null` + pkg_cv_DEPENDENCIES_CFLAGS=`$PKG_CONFIG --cflags "libtorrent >= 0.16.9" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -21577,12 +21588,12 @@ pkg_cv_DEPENDENCIES_LIBS="$DEPENDENCIES_LIBS" elif test -n "$PKG_CONFIG"; then if test -n "$PKG_CONFIG" && \ - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent >= 0.16.8\""; } >&5 - ($PKG_CONFIG --exists --print-errors "libtorrent >= 0.16.8") 2>&5 + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libtorrent >= 0.16.9\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libtorrent >= 0.16.9") 2>&5 ac_status=$? printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then - pkg_cv_DEPENDENCIES_LIBS=`$PKG_CONFIG --libs "libtorrent >= 0.16.8" 2>/dev/null` + pkg_cv_DEPENDENCIES_LIBS=`$PKG_CONFIG --libs "libtorrent >= 0.16.9" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes @@ -21603,14 +21614,14 @@ _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - DEPENDENCIES_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent >= 0.16.8" 2>&1` + DEPENDENCIES_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libtorrent >= 0.16.9" 2>&1` else - DEPENDENCIES_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent >= 0.16.8" 2>&1` + DEPENDENCIES_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libtorrent >= 0.16.9" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$DEPENDENCIES_PKG_ERRORS" >&5 - as_fn_error $? "Package requirements (libtorrent >= 0.16.8) were not met: + as_fn_error $? "Package requirements (libtorrent >= 0.16.9) were not met: $DEPENDENCIES_PKG_ERRORS @@ -22610,6 +22621,95 @@ + +# Check whether --with-systemd was given. +if test ${with_systemd+y} +then : + withval=$with_systemd; + if test "$withval" = "yes"; then + +pkg_failed=no +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libsystemd" >&5 +printf %s "checking for libsystemd... " >&6; } + +if test -n "$SYSTEMD_CFLAGS"; then + pkg_cv_SYSTEMD_CFLAGS="$SYSTEMD_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libsystemd") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_SYSTEMD_CFLAGS=`$PKG_CONFIG --cflags "libsystemd" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$SYSTEMD_LIBS"; then + pkg_cv_SYSTEMD_LIBS="$SYSTEMD_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libsystemd") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_SYSTEMD_LIBS=`$PKG_CONFIG --libs "libsystemd" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd" 2>&1` + else + SYSTEMD_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$SYSTEMD_PKG_ERRORS" >&5 + + as_fn_error $? "libsystemd not found. Install libsystemd-dev (or the equivalent for your distribution)." "$LINENO" 5 +elif test $pkg_failed = untried; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } + as_fn_error $? "libsystemd not found. Install libsystemd-dev (or the equivalent for your distribution)." "$LINENO" 5 +else + SYSTEMD_CFLAGS=$pkg_cv_SYSTEMD_CFLAGS + SYSTEMD_LIBS=$pkg_cv_SYSTEMD_LIBS + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } + + CXXFLAGS="$CXXFLAGS $SYSTEMD_CFLAGS" + LIBS="$LIBS $SYSTEMD_LIBS" + +printf "%s\n" "#define HAVE_SYSTEMD 1" >>confdefs.h + + +fi + fi + +fi + + + if test ${with_xmlrpc_c+y} && test ${with_xmlrpc_tinyxml2+y}; then as_fn_error $? "--with-xmlrpc-c and --with-xmlrpc-tinyxml2 cannot be used together. Please choose only one" "$LINENO" 5 fi @@ -23202,7 +23302,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by rtorrent $as_me 0.16.8, which was +This file was extended by rtorrent $as_me 0.16.9, which was generated by GNU Autoconf 2.72. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -23270,7 +23370,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -rtorrent config.status 0.16.8 +rtorrent config.status 0.16.9 configured by $0, generated by GNU Autoconf 2.72, with options \\"\$ac_cs_config\\" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/configure.ac new/rtorrent-0.16.9/configure.ac --- old/rtorrent-0.16.8/configure.ac 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/configure.ac 2026-04-06 16:00:46.000000000 +0200 @@ -1,6 +1,6 @@ m4_pattern_allow([PKG_CHECK_EXISTS]) -AC_INIT([rtorrent],[0.16.8],[[email protected]]) +AC_INIT([rtorrent],[0.16.9],[[email protected]]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIRS([scripts]) @@ -48,7 +48,7 @@ fi PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"]) -PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.8]) +PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.9]) AC_LANG_PUSH(C++) TORRENT_WITH_XMLRPC_C @@ -56,6 +56,7 @@ TORRENT_WITH_LUA TORRENT_WITH_TINYXML2 +TORRENT_WITH_SYSTEMD if test ${with_xmlrpc_c+y} && test ${with_xmlrpc_tinyxml2+y}; then AC_MSG_ERROR([--with-xmlrpc-c and --with-xmlrpc-tinyxml2 cannot be used together. Please choose only one]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/doc/Makefile.in new/rtorrent-0.16.9/doc/Makefile.in --- old/rtorrent-0.16.8/doc/Makefile.in 2026-03-15 18:07:04.000000000 +0100 +++ new/rtorrent-0.16.9/doc/Makefile.in 2026-04-06 16:10:12.000000000 +0200 @@ -227,6 +227,8 @@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ +SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ +SYSTEMD_LIBS = @SYSTEMD_LIBS@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/scripts/checks.m4 new/rtorrent-0.16.9/scripts/checks.m4 --- old/rtorrent-0.16.8/scripts/checks.m4 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/scripts/checks.m4 2026-04-06 16:00:46.000000000 +0200 @@ -358,3 +358,20 @@ ] ) ]) + + +AC_DEFUN([TORRENT_WITH_SYSTEMD], [ + AC_ARG_WITH(systemd, + AS_HELP_STRING([--with-systemd],[enable systemd socket activation support [[default=no]]]), + [ + if test "$withval" = "yes"; then + PKG_CHECK_MODULES([SYSTEMD], [libsystemd], + [ + CXXFLAGS="$CXXFLAGS $SYSTEMD_CFLAGS" + LIBS="$LIBS $SYSTEMD_LIBS" + AC_DEFINE(HAVE_SYSTEMD, 1, [Support for systemd socket activation.]) + ], + [AC_MSG_ERROR([libsystemd not found. Install libsystemd-dev (or the equivalent for your distribution).])]) + fi + ]) +]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/Makefile.in new/rtorrent-0.16.9/src/Makefile.in --- old/rtorrent-0.16.8/src/Makefile.in 2026-03-15 18:07:04.000000000 +0100 +++ new/rtorrent-0.16.9/src/Makefile.in 2026-04-06 16:10:12.000000000 +0200 @@ -430,6 +430,8 @@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ +SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ +SYSTEMD_LIBS = @SYSTEMD_LIBS@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/command_download.cc new/rtorrent-0.16.9/src/command_download.cc --- old/rtorrent-0.16.8/src/command_download.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/command_download.cc 2026-04-06 16:00:46.000000000 +0200 @@ -884,4 +884,91 @@ CMD2_DL_LIST ("t.multicall", std::bind(&t_multicall, std::placeholders::_1, std::placeholders::_2)); CMD2_ANY_LIST ("p.call_target", std::bind(&p_call_target, std::placeholders::_2)); + + rpc::rpc.mark_safe("add_peer"); + rpc::rpc.mark_safe("d.hash"); + rpc::rpc.mark_safe("d.local_id"); + rpc::rpc.mark_safe("d.local_id_html"); + rpc::rpc.mark_safe("d.bitfield"); + rpc::rpc.mark_safe("d.base_path"); + rpc::rpc.mark_safe("d.base_filename"); + rpc::rpc.mark_safe("d.name"); + rpc::rpc.mark_safe("d.directory"); + rpc::rpc.mark_safe("d.directory_base"); + rpc::rpc.mark_safe("d.creation_date"); + rpc::rpc.mark_safe("d.load_date"); + rpc::rpc.mark_safe("d.up.rate"); + rpc::rpc.mark_safe("d.up.total"); + rpc::rpc.mark_safe("d.down.rate"); + rpc::rpc.mark_safe("d.down.total"); + rpc::rpc.mark_safe("d.skip.rate"); + rpc::rpc.mark_safe("d.skip.total"); + rpc::rpc.mark_safe("d.is_open"); + rpc::rpc.mark_safe("d.is_active"); + rpc::rpc.mark_safe("d.is_hash_checked"); + rpc::rpc.mark_safe("d.is_hash_checking"); + rpc::rpc.mark_safe("d.is_multi_file"); + rpc::rpc.mark_safe("d.is_private"); + rpc::rpc.mark_safe("d.is_pex_active"); + rpc::rpc.mark_safe("d.is_partially_done"); + rpc::rpc.mark_safe("d.is_not_partially_done"); + rpc::rpc.mark_safe("d.is_meta"); + rpc::rpc.mark_safe("d.peer_exchange"); + rpc::rpc.mark_safe("d.resume"); + rpc::rpc.mark_safe("d.pause"); + rpc::rpc.mark_safe("d.open"); + rpc::rpc.mark_safe("d.close"); + rpc::rpc.mark_safe("d.close.directly"); + rpc::rpc.mark_safe("d.erase"); + rpc::rpc.mark_safe("d.check_hash"); + rpc::rpc.mark_safe("d.save_resume"); + rpc::rpc.mark_safe("d.save_full_session"); + rpc::rpc.mark_safe("d.update_priorities"); + rpc::rpc.mark_safe("d.custom"); + rpc::rpc.mark_safe("d.custom1"); + rpc::rpc.mark_safe("d.custom2"); + rpc::rpc.mark_safe("d.custom3"); + rpc::rpc.mark_safe("d.custom4"); + rpc::rpc.mark_safe("d.custom5"); + rpc::rpc.mark_safe("d.size_bytes"); + rpc::rpc.mark_safe("d.size_chunks"); + rpc::rpc.mark_safe("d.size_pex"); + rpc::rpc.mark_safe("d.completed_bytes"); + rpc::rpc.mark_safe("d.bytes_done"); + rpc::rpc.mark_safe("d.peers_accounted"); + rpc::rpc.mark_safe("d.chunks_hashed"); + rpc::rpc.mark_safe("d.tracker_size"); + rpc::rpc.mark_safe("d.completed_chunks"); + rpc::rpc.mark_safe("d.left_bytes"); + rpc::rpc.mark_safe("d.chunk_size"); + rpc::rpc.mark_safe("d.priority"); + rpc::rpc.mark_safe("d.priority_str"); + rpc::rpc.mark_safe("d.state"); + rpc::rpc.mark_safe("d.state_changed"); + rpc::rpc.mark_safe("d.state_counter"); + rpc::rpc.mark_safe("d.connection_current"); + rpc::rpc.mark_safe("d.connection_leech"); + rpc::rpc.mark_safe("d.connection_seed"); + rpc::rpc.mark_safe("d.throttle_name"); + rpc::rpc.mark_safe("d.uploads_max"); + rpc::rpc.mark_safe("d.downloads_max"); + rpc::rpc.mark_safe("d.peers_min"); + rpc::rpc.mark_safe("d.peers_max"); + rpc::rpc.mark_safe("d.peers_connected"); + rpc::rpc.mark_safe("d.peers_not_connected"); + rpc::rpc.mark_safe("d.peers_complete"); + rpc::rpc.mark_safe("d.tracker_numwant"); + rpc::rpc.mark_safe("d.tracker_focus"); + rpc::rpc.mark_safe("d.message"); + rpc::rpc.mark_safe("d.hashing"); + rpc::rpc.mark_safe("d.hashing_failed"); + rpc::rpc.mark_safe("d.free_diskspace"); + rpc::rpc.mark_safe("d.views"); + rpc::rpc.mark_safe("d.views.remove"); + rpc::rpc.mark_safe("d.views.push_back_unique"); + rpc::rpc.mark_safe("d.ratio"); + rpc::rpc.mark_safe("f.multicall"); + rpc::rpc.mark_safe("p.multicall"); + rpc::rpc.mark_safe("p.call_target"); + rpc::rpc.mark_safe("t.multicall"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/command_dynamic.cc new/rtorrent-0.16.9/src/command_dynamic.cc --- old/rtorrent-0.16.8/src/command_dynamic.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/command_dynamic.cc 2026-04-06 16:00:46.000000000 +0200 @@ -455,4 +455,27 @@ CMD2_ANY ("strings.tracker_event", std::bind(&torrent::option_list_strings, torrent::OPTION_TRACKER_EVENT)); CMD2_ANY ("strings.tracker_mode", std::bind(&torrent::option_list_strings, torrent::OPTION_TRACKER_MODE)); // clang-format on + +#ifdef HAVE_XMLRPC_TINYXML2 + rpc::rpc.mark_safe("system.listMethods"); +#endif + + rpc::rpc.mark_safe("method.use_deprecated"); + rpc::rpc.mark_safe("method.const"); + rpc::rpc.mark_safe("method.has_key"); + rpc::rpc.mark_safe("method.list_keys"); + rpc::rpc.mark_safe("method.get"); + rpc::rpc.mark_safe("method.rlookup"); + rpc::rpc.mark_safe("catch"); + + rpc::rpc.mark_safe("strings.choke_heuristics"); + rpc::rpc.mark_safe("strings.choke_heuristics.upload"); + rpc::rpc.mark_safe("strings.choke_heuristics.download"); + rpc::rpc.mark_safe("strings.connection_type"); + rpc::rpc.mark_safe("strings.encryption"); + rpc::rpc.mark_safe("strings.ip_filter"); + rpc::rpc.mark_safe("strings.ip_tos"); + rpc::rpc.mark_safe("strings.log_group"); + rpc::rpc.mark_safe("strings.tracker_event"); + rpc::rpc.mark_safe("strings.tracker_mode"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/command_events.cc new/rtorrent-0.16.9/src/command_events.cc --- old/rtorrent-0.16.8/src/command_events.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/command_events.cc 2026-04-06 16:00:46.000000000 +0200 @@ -344,4 +344,15 @@ CMD2_ANY_LIST ("d.multicall.filtered", std::bind(&d_multicall_filtered, std::placeholders::_2)); CMD2_ANY_LIST ("directory.watch.added", std::bind(&directory_watch_added, std::placeholders::_2)); + + rpc::rpc.mark_safe("start_tied"); + rpc::rpc.mark_safe("stop_untied"); + rpc::rpc.mark_safe("close_untied"); + rpc::rpc.mark_safe("remove_untied"); + + rpc::rpc.mark_safe("close_low_diskspace"); + rpc::rpc.mark_safe("close_low_diskspace.normal"); + rpc::rpc.mark_safe("download_list"); + rpc::rpc.mark_safe("d.multicall2"); + rpc::rpc.mark_safe("d.multicall.filtered"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/command_file.cc new/rtorrent-0.16.9/src/command_file.cc --- old/rtorrent-0.16.8/src/command_file.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/command_file.cc 2026-04-06 16:00:46.000000000 +0200 @@ -103,4 +103,32 @@ CMD2_FILEITR("fi.filename_last", std::bind(&apply_fi_filename_last, std::placeholders::_1)); CMD2_FILEITR("fi.is_file", std::bind(&torrent::FileListIterator::is_file, std::placeholders::_1)); + + rpc::rpc.mark_safe("f.path"); + rpc::rpc.mark_safe("f.path_components"); + rpc::rpc.mark_safe("f.path_depth"); + rpc::rpc.mark_safe("f.frozen_path"); + rpc::rpc.mark_safe("f.offset"); + rpc::rpc.mark_safe("f.size_bytes"); + rpc::rpc.mark_safe("f.size_chunks"); + rpc::rpc.mark_safe("f.completed_chunks"); + rpc::rpc.mark_safe("f.range_first"); + rpc::rpc.mark_safe("f.range_second"); + rpc::rpc.mark_safe("f.priority"); + rpc::rpc.mark_safe("f.priority.set"); + rpc::rpc.mark_safe("f.is_created"); + rpc::rpc.mark_safe("f.is_open"); + rpc::rpc.mark_safe("f.is_create_queued"); + rpc::rpc.mark_safe("f.is_resize_queued"); + rpc::rpc.mark_safe("f.prioritize_first"); + rpc::rpc.mark_safe("f.prioritize_first.enable"); + rpc::rpc.mark_safe("f.prioritize_first.disable"); + rpc::rpc.mark_safe("f.prioritize_last"); + rpc::rpc.mark_safe("f.prioritize_last.enable"); + rpc::rpc.mark_safe("f.prioritize_last.disable"); + rpc::rpc.mark_safe("f.last_touched"); + rpc::rpc.mark_safe("f.match_depth_prev"); + rpc::rpc.mark_safe("f.match_depth_next"); + rpc::rpc.mark_safe("fi.filename_last"); + rpc::rpc.mark_safe("fi.is_file"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/command_groups.cc new/rtorrent-0.16.9/src/command_groups.cc --- old/rtorrent-0.16.8/src/command_groups.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/command_groups.cc 2026-04-06 16:00:46.000000000 +0200 @@ -385,4 +385,24 @@ CMD2_ANY ("choke_group.down.heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS, std::bind(&torrent::choke_queue::heuristics, CHOKE_GROUP(&torrent::choke_group::down_queue)))); CMD2_ANY_LIST ("choke_group.down.heuristics.set", std::bind(&apply_cg_heuristics_set, std::placeholders::_2, false)); + + rpc::rpc.mark_safe("choke_group.list"); + rpc::rpc.mark_safe("choke_group.size"); + rpc::rpc.mark_safe("choke_group.index_of"); + rpc::rpc.mark_safe("choke_group.general.size"); + rpc::rpc.mark_safe("choke_group.tracker.mode"); + rpc::rpc.mark_safe("choke_group.up.rate"); + rpc::rpc.mark_safe("choke_group.down.rate"); + rpc::rpc.mark_safe("choke_group.up.max"); + rpc::rpc.mark_safe("choke_group.up.max.unlimited"); + rpc::rpc.mark_safe("choke_group.up.total"); + rpc::rpc.mark_safe("choke_group.up.queued"); + rpc::rpc.mark_safe("choke_group.up.unchoked"); + rpc::rpc.mark_safe("choke_group.up.heuristics"); + rpc::rpc.mark_safe("choke_group.down.max"); + rpc::rpc.mark_safe("choke_group.down.max.unlimited"); + rpc::rpc.mark_safe("choke_group.down.total"); + rpc::rpc.mark_safe("choke_group.down.queued"); + rpc::rpc.mark_safe("choke_group.down.unchoked"); + rpc::rpc.mark_safe("choke_group.down.heuristics"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/command_local.cc new/rtorrent-0.16.9/src/command_local.cc --- old/rtorrent-0.16.8/src/command_local.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/command_local.cc 2026-04-06 16:00:46.000000000 +0200 @@ -120,6 +120,15 @@ rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.max", "value", (int64_t)300)); rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.upload", "value", (int64_t)20 << 20)); + rpc::rpc.mark_safe("group." + name + ".view"); + rpc::rpc.mark_safe("group." + name + ".view.set"); + rpc::rpc.mark_safe("group." + name + ".ratio.min"); + rpc::rpc.mark_safe("group." + name + ".ratio.min.set"); + rpc::rpc.mark_safe("group." + name + ".ratio.max"); + rpc::rpc.mark_safe("group." + name + ".ratio.max.set"); + rpc::rpc.mark_safe("group." + name + ".ratio.upload"); + rpc::rpc.mark_safe("group." + name + ".ratio.upload.set"); + if (rpc::call_command_value("method.use_intermediate") == 3) { // Cleaned up in 0.16.1: @@ -173,9 +182,13 @@ if (output == nullptr) throw torrent::input_error("Could not append to file '" + args.front().as_string() + "': " + std::strerror(errno)); - file_print_list(++args.begin(), args.end(), output, file_print_delim_space); - - fprintf(output, "\n"); + try { + file_print_list(++args.begin(), args.end(), output, file_print_delim_space); + fprintf(output, "\n"); + } catch (...) { + fclose(output); + throw; + } fclose(output); return torrent::Object(); } @@ -319,4 +332,25 @@ CMD2_ANY_P("argument.3", std::bind(&rpc::command_base::argument_ref, 3)); CMD2_ANY_LIST ("group.insert", std::bind(&group_insert, std::placeholders::_2)); + + rpc::rpc.mark_safe("system.api_version"); + rpc::rpc.mark_safe("system.client_version"); + rpc::rpc.mark_safe("system.library_version"); + rpc::rpc.mark_safe("system.file.max_size"); + rpc::rpc.mark_safe("system.file.split_size"); + rpc::rpc.mark_safe("system.file.split_suffix"); + + rpc::rpc.mark_safe("directory.default"); + rpc::rpc.mark_safe("session.path"); + rpc::rpc.mark_safe("session.use_lock"); + rpc::rpc.mark_safe("session.on_completion"); + + rpc::rpc.mark_safe("pieces.sync.always_safe"); + rpc::rpc.mark_safe("pieces.sync.timeout"); + rpc::rpc.mark_safe("pieces.sync.timeout_safe"); + rpc::rpc.mark_safe("pieces.preload.type"); + rpc::rpc.mark_safe("pieces.preload.min_size"); + rpc::rpc.mark_safe("pieces.preload.min_rate"); + rpc::rpc.mark_safe("pieces.memory.max"); + rpc::rpc.mark_safe("pieces.hash.on_completion"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/command_network.cc new/rtorrent-0.16.9/src/command_network.cc --- old/rtorrent-0.16.8/src/command_network.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/command_network.cc 2026-04-06 16:00:46.000000000 +0200 @@ -3,6 +3,10 @@ #include <functional> #include <cstdio> #include <unistd.h> +#ifdef HAVE_SYSTEMD +#include <sys/socket.h> +#include <systemd/sd-daemon.h> +#endif #include <rak/address_info.h> #include <torrent/torrent.h> #include <torrent/rate.h> @@ -144,6 +148,60 @@ } torrent::Object +apply_scgi_systemd() { +#ifdef HAVE_SYSTEMD + if (scgi_thread::scgi() != nullptr) + throw torrent::input_error("SCGI already enabled."); + + int n = sd_listen_fds(0); + if (n < 1) + throw torrent::input_error("No systemd socket(s) provided (sd_listen_fds returned " + + std::to_string(n) + ")."); + + // Iterate over all provided fds. Use the first listening stream socket; + // close the rest. The systemd docs say unused fds should be closed. + int selected_fd = -1; + for (int i = 0; i < n; i++) { + int fd = SD_LISTEN_FDS_START + i; + + if (selected_fd != -1) { + ::close(fd); + continue; + } + + auto err = sd_is_socket(fd, AF_UNSPEC, SOCK_STREAM, 1); + + if (err < 0) { + // Safe to ignore errors here - we just skip it and move on. + ::close(fd); + continue; + } + + if (err == 0) { + // Not the socket we're looking for. + ::close(fd); + continue; + } + + selected_fd = fd; + } + + if (selected_fd == -1) + throw torrent::input_error("No listening stream socket found among systemd-provided fds."); + + initialize_rpc_handlers(); + + rpc::SCgi* scgi = new rpc::SCgi; + scgi->open_fd(selected_fd); + + scgi_thread::set_scgi(scgi); + return torrent::Object(); +#else + throw torrent::input_error("Systemd SCGI endpoint is not supported."); +#endif +} + +torrent::Object apply_xmlrpc_dialect(const std::string& arg) { int value; @@ -243,6 +301,7 @@ CMD2_ANY_STRING ("network.scgi.open_port", std::bind(&apply_scgi, std::placeholders::_2, 1)); CMD2_ANY_STRING ("network.scgi.open_local", std::bind(&apply_scgi, std::placeholders::_2, 2)); CMD2_VAR_BOOL ("network.scgi.dont_route", false); + CMD2_ANY ("network.scgi.open_systemd", [](auto, auto) { return apply_scgi_systemd(); }); CMD2_ANY_STRING ("network.xmlrpc.dialect.set", [](const auto&, const auto& arg) { return apply_xmlrpc_dialect(arg); }) CMD2_ANY ("network.xmlrpc.size_limit", [](const auto&, const auto&) { return rpc::rpc.size_limit(); }); @@ -261,4 +320,36 @@ CMD2_ANY_VALUE_V ("network.block.outgoing.set", [nw_config](auto, auto& value) { return nw_config->set_block_outgoing(value); }); CMD2_ANY ("network.prefer.ipv6", [nw_config](auto, auto) { return nw_config->is_prefer_ipv6(); }); CMD2_ANY_VALUE_V ("network.prefer.ipv6.set", [nw_config](auto, auto& value) { return nw_config->set_prefer_ipv6(value); }); + + rpc::rpc.mark_safe("network.port_open"); + rpc::rpc.mark_safe("network.port_random"); + rpc::rpc.mark_safe("network.port_range"); + rpc::rpc.mark_safe("network.listen.port"); + rpc::rpc.mark_safe("network.listen.backlog"); + + rpc::rpc.mark_safe("network.http.current_open"); + rpc::rpc.mark_safe("network.http.max_cache_connections"); + rpc::rpc.mark_safe("network.http.max_host_connections"); + rpc::rpc.mark_safe("network.http.max_total_connections"); + + rpc::rpc.mark_safe("network.open_files"); + rpc::rpc.mark_safe("network.max_open_files"); + rpc::rpc.mark_safe("network.max_open_sockets"); + rpc::rpc.mark_safe("network.total_handshakes"); + + rpc::rpc.mark_safe("network.send_buffer.size"); + rpc::rpc.mark_safe("network.receive_buffer.size"); + rpc::rpc.mark_safe("network.bind_address"); + rpc::rpc.mark_safe("network.local_address"); + rpc::rpc.mark_safe("network.xmlrpc.size_limit"); + rpc::rpc.mark_safe("network.open_sockets"); + rpc::rpc.mark_safe("network.http.cacert"); + rpc::rpc.mark_safe("network.http.capath"); + rpc::rpc.mark_safe("network.http.proxy_address"); + rpc::rpc.mark_safe("network.proxy_address"); + rpc::rpc.mark_safe("network.scgi.dont_route"); + rpc::rpc.mark_safe("protocol.pex"); + + rpc::rpc.mark_safe("network.rpc.use_xmlrpc"); + rpc::rpc.mark_safe("network.rpc.use_jsonrpc"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/command_peer.cc new/rtorrent-0.16.9/src/command_peer.cc --- old/rtorrent-0.16.8/src/command_peer.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/command_peer.cc 2026-04-06 16:00:46.000000000 +0200 @@ -60,6 +60,8 @@ torrent::Object retrieve_p_completed_percent(torrent::Peer* peer) { + if (peer->bitfield()->size_bits() == 0) + return int64_t(0); return (100 * peer->bitfield()->size_set()) / peer->bitfield()->size_bits(); } @@ -98,4 +100,30 @@ CMD2_PEER_V("p.disconnect", std::bind(&torrent::Peer::disconnect, std::placeholders::_1, 0)); CMD2_PEER_V("p.disconnect_delayed", std::bind(&torrent::Peer::disconnect, std::placeholders::_1, torrent::ConnectionList::disconnect_delayed)); + + rpc::rpc.mark_safe("p.address"); + rpc::rpc.mark_safe("p.port"); + rpc::rpc.mark_safe("p.client_version"); + rpc::rpc.mark_safe("p.options_str"); + rpc::rpc.mark_safe("p.id"); + rpc::rpc.mark_safe("p.id_html"); + rpc::rpc.mark_safe("p.up_rate"); + rpc::rpc.mark_safe("p.up_total"); + rpc::rpc.mark_safe("p.down_rate"); + rpc::rpc.mark_safe("p.down_total"); + rpc::rpc.mark_safe("p.peer_rate"); + rpc::rpc.mark_safe("p.peer_total"); + rpc::rpc.mark_safe("p.is_encrypted"); + rpc::rpc.mark_safe("p.is_incoming"); + rpc::rpc.mark_safe("p.is_obfuscated"); + rpc::rpc.mark_safe("p.is_snubbed"); + rpc::rpc.mark_safe("p.is_unwanted"); + rpc::rpc.mark_safe("p.is_preferred"); + rpc::rpc.mark_safe("p.snubbed"); + rpc::rpc.mark_safe("p.snubbed.set"); + rpc::rpc.mark_safe("p.banned"); + rpc::rpc.mark_safe("p.banned.set"); + rpc::rpc.mark_safe("p.completed_percent"); + rpc::rpc.mark_safe("p.disconnect"); + rpc::rpc.mark_safe("p.disconnect_delayed"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/command_throttle.cc new/rtorrent-0.16.9/src/command_throttle.cc --- old/rtorrent-0.16.8/src/command_throttle.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/command_throttle.cc 2026-04-06 16:00:46.000000000 +0200 @@ -199,4 +199,60 @@ CMD2_ANY_STRING ("throttle.up.rate", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_up | throttle_info_rate)); CMD2_ANY_STRING ("throttle.down.max", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_down | throttle_info_max)); CMD2_ANY_STRING ("throttle.down.rate", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_down | throttle_info_rate)); + + rpc::rpc.mark_safe("throttle.unchoked_uploads"); + rpc::rpc.mark_safe("throttle.max_unchoked_uploads"); + rpc::rpc.mark_safe("throttle.unchoked_downloads"); + rpc::rpc.mark_safe("throttle.max_unchoked_downloads"); + + rpc::rpc.mark_safe("throttle.min_peers.normal"); + rpc::rpc.mark_safe("throttle.min_peers.normal.set"); + rpc::rpc.mark_safe("throttle.max_peers.normal"); + rpc::rpc.mark_safe("throttle.max_peers.normal.set"); + rpc::rpc.mark_safe("throttle.min_peers.seed"); + rpc::rpc.mark_safe("throttle.min_peers.seed.set"); + rpc::rpc.mark_safe("throttle.max_peers.seed"); + rpc::rpc.mark_safe("throttle.max_peers.seed.set"); + + rpc::rpc.mark_safe("throttle.min_uploads"); + rpc::rpc.mark_safe("throttle.min_uploads.set"); + rpc::rpc.mark_safe("throttle.max_uploads"); + rpc::rpc.mark_safe("throttle.max_uploads.set"); + rpc::rpc.mark_safe("throttle.min_downloads"); + rpc::rpc.mark_safe("throttle.min_downloads.set"); + rpc::rpc.mark_safe("throttle.max_downloads"); + rpc::rpc.mark_safe("throttle.max_downloads.set"); + + rpc::rpc.mark_safe("throttle.max_uploads.div"); + rpc::rpc.mark_safe("throttle.max_uploads.div.set"); + rpc::rpc.mark_safe("throttle.max_uploads.div._val"); + rpc::rpc.mark_safe("throttle.max_uploads.div._val.set"); + rpc::rpc.mark_safe("throttle.max_uploads.global"); + rpc::rpc.mark_safe("throttle.max_uploads.global.set"); + rpc::rpc.mark_safe("throttle.max_uploads.global._val"); + rpc::rpc.mark_safe("throttle.max_uploads.global._val.set"); + rpc::rpc.mark_safe("throttle.max_downloads.div"); + rpc::rpc.mark_safe("throttle.max_downloads.div.set"); + rpc::rpc.mark_safe("throttle.max_downloads.div._val"); + rpc::rpc.mark_safe("throttle.max_downloads.div._val.set"); + rpc::rpc.mark_safe("throttle.max_downloads.global"); + rpc::rpc.mark_safe("throttle.max_downloads.global.set"); + rpc::rpc.mark_safe("throttle.max_downloads.global._val"); + rpc::rpc.mark_safe("throttle.max_downloads.global._val.set"); + + rpc::rpc.mark_safe("throttle.global_up.rate"); + rpc::rpc.mark_safe("throttle.global_up.total"); + rpc::rpc.mark_safe("throttle.global_up.max_rate"); + rpc::rpc.mark_safe("throttle.global_up.max_rate.set"); + rpc::rpc.mark_safe("throttle.global_up.max_rate.set_kb"); + rpc::rpc.mark_safe("throttle.global_down.rate"); + rpc::rpc.mark_safe("throttle.global_down.total"); + rpc::rpc.mark_safe("throttle.global_down.max_rate"); + rpc::rpc.mark_safe("throttle.global_down.max_rate.set"); + rpc::rpc.mark_safe("throttle.global_down.max_rate.set_kb"); + + rpc::rpc.mark_safe("throttle.up.max"); + rpc::rpc.mark_safe("throttle.up.rate"); + rpc::rpc.mark_safe("throttle.down.max"); + rpc::rpc.mark_safe("throttle.down.rate"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/command_tracker.cc new/rtorrent-0.16.9/src/command_tracker.cc --- old/rtorrent-0.16.8/src/command_tracker.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/command_tracker.cc 2026-04-06 16:00:46.000000000 +0200 @@ -151,4 +151,35 @@ CMD2_ANY_STRING ("dht.add_node", std::bind(&apply_dht_add_node, std::placeholders::_2)); CMD2_ANY ("dht.statistics", std::bind(&core::DhtManager::dht_statistics, dht_manager)); + + rpc::rpc.mark_safe("t.url"); + rpc::rpc.mark_safe("t.group"); + rpc::rpc.mark_safe("t.id"); + rpc::rpc.mark_safe("t.type"); + rpc::rpc.mark_safe("t.is_usable"); + rpc::rpc.mark_safe("t.is_busy"); + rpc::rpc.mark_safe("t.is_enabled"); + rpc::rpc.mark_safe("t.is_enabled.set"); + rpc::rpc.mark_safe("t.is_extra_tracker"); + rpc::rpc.mark_safe("t.is_open"); + rpc::rpc.mark_safe("t.normal_interval"); + rpc::rpc.mark_safe("t.scrape_time_last"); + rpc::rpc.mark_safe("t.scrape_counter"); + rpc::rpc.mark_safe("t.success_time_last"); + rpc::rpc.mark_safe("t.success_counter"); + rpc::rpc.mark_safe("t.failed_time_last"); + rpc::rpc.mark_safe("t.failed_counter"); + rpc::rpc.mark_safe("t.activity_time_last"); + rpc::rpc.mark_safe("t.activity_time_next"); + rpc::rpc.mark_safe("t.scrape_complete"); + rpc::rpc.mark_safe("t.scrape_incomplete"); + rpc::rpc.mark_safe("t.scrape_downloaded"); + + rpc::rpc.mark_safe("dht.mode.set"); + rpc::rpc.mark_safe("dht.port"); + rpc::rpc.mark_safe("dht.override_port"); + rpc::rpc.mark_safe("dht.add_node"); + rpc::rpc.mark_safe("dht.statistics"); + rpc::rpc.mark_safe("trackers.numwant"); + rpc::rpc.mark_safe("trackers.use_udp"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/command_ui.cc new/rtorrent-0.16.9/src/command_ui.cc --- old/rtorrent-0.16.8/src/command_ui.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/command_ui.cc 2026-04-06 16:00:46.000000000 +0200 @@ -888,4 +888,28 @@ return control->object_storage()->get_str(display::color_vars[color_id]); }); } + + rpc::rpc.mark_safe("view.set_visible"); + rpc::rpc.mark_safe("view.set_not_visible"); + + rpc::rpc.mark_safe("cat"); + rpc::rpc.mark_safe("if"); + rpc::rpc.mark_safe("branch"); + rpc::rpc.mark_safe("and"); + rpc::rpc.mark_safe("or"); + rpc::rpc.mark_safe("not"); + rpc::rpc.mark_safe("value"); + rpc::rpc.mark_safe("compare"); + rpc::rpc.mark_safe("elapsed.less"); + rpc::rpc.mark_safe("elapsed.greater"); + + rpc::rpc.mark_safe("convert.gm_time"); + rpc::rpc.mark_safe("convert.gm_date"); + rpc::rpc.mark_safe("convert.time"); + rpc::rpc.mark_safe("convert.date"); + rpc::rpc.mark_safe("convert.elapsed_time"); + rpc::rpc.mark_safe("convert.kb"); + rpc::rpc.mark_safe("convert.mb"); + rpc::rpc.mark_safe("convert.xb"); + rpc::rpc.mark_safe("convert.throttle"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/core/download.cc new/rtorrent-0.16.9/src/core/download.cc --- old/rtorrent-0.16.8/src/core/download.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/core/download.cc 2026-04-06 16:00:46.000000000 +0200 @@ -137,7 +137,7 @@ rpc::call_command("d.state.set", (int64_t)0, rpc::make_target(this)); control->core()->download_list()->close_directly(this); - throw torrent::input_error("Cannot change the directory of an open download atter the files have been moved."); + throw torrent::input_error("Cannot change the directory of an open download after the files have been moved."); } control->core()->download_list()->close_directly(this); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/display/frame.cc new/rtorrent-0.16.9/src/display/frame.cc --- old/rtorrent-0.16.8/src/display/frame.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/display/frame.cc 2026-04-06 16:00:46.000000000 +0200 @@ -361,7 +361,7 @@ (*itr)->balance(x, y, m_width, std::min((*itr)->m_height, height)); y += (*itr)->m_height; - height -= (*itr)->m_height; + height -= std::min(height, (*itr)->m_height); } } @@ -436,7 +436,7 @@ (*itr)->balance(x, y, std::min((*itr)->m_width, width), m_height); x += (*itr)->m_width; - width -= (*itr)->m_width; + width -= std::min(width, (*itr)->m_width); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/display/utils.cc new/rtorrent-0.16.9/src/display/utils.cc --- old/rtorrent-0.16.8/src/display/utils.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/display/utils.cc 2026-04-06 16:00:46.000000000 +0200 @@ -150,7 +150,7 @@ if (d->is_hash_checking()) { first = print_buffer(first, last, "Checking hash [%2i%%]", - (d->download()->chunks_hashed() * 100) / d->download()->file_list()->size_chunks()); + d->download()->file_list()->size_chunks() != 0 ? (d->download()->chunks_hashed() * 100) / d->download()->file_list()->size_chunks() : 0); } else if (d->tracker_controller().has_active_trackers_not_scrape()) { auto tracker = d->tracker_controller().find_if([](const auto& t) { @@ -208,7 +208,7 @@ if (d->is_done()) first = print_buffer(first, last, " 100%% "); else if (d->is_open()) - first = print_buffer(first, last, " %2u%% ",(d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks()); + first = print_buffer(first, last, " %2u%% ", d->download()->file_list()->size_chunks() != 0 ? (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks() : 0); else first = print_buffer(first, last, " "); @@ -260,7 +260,7 @@ //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, "[%2u%%]", d->download()->file_list()->size_chunks() != 0 ? (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks() : 0); } char* diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/display/window_http_queue.cc new/rtorrent-0.16.9/src/display/window_http_queue.cc --- old/rtorrent-0.16.8/src/display/window_http_queue.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/display/window_http_queue.cc 2026-04-06 16:00:46.000000000 +0200 @@ -19,7 +19,7 @@ set_active(false); m_conn_insert = m_queue->signal_insert().insert(m_queue->signal_insert().end(), [this](auto h) { receive_insert(h); }); - m_conn_erase = m_queue->signal_erase().insert(m_queue->signal_insert().end(), [this](auto h) { receive_erase(h); }); + m_conn_erase = m_queue->signal_erase().insert(m_queue->signal_erase().end(), [this](auto h) { receive_erase(h); }); m_task_deactivate.slot() = [this] { if (!m_container.empty()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/display/window_input.cc new/rtorrent-0.16.9/src/display/window_input.cc --- old/rtorrent-0.16.8/src/display/window_input.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/display/window_input.cc 2026-04-06 16:00:46.000000000 +0200 @@ -12,7 +12,7 @@ m_canvas->erase(); m_canvas->print(0, 0, "%s> %s", m_title.c_str(), m_input != NULL ? m_input->c_str() : "<NULL>"); - if (m_focus) + if (m_focus && m_input != NULL) m_canvas->set_attr(m_input->get_pos() + 2 + m_title.size(), 0, 1, A_REVERSE, COLOR_PAIR(0)); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/main.cc new/rtorrent-0.16.9/src/main.cc --- old/rtorrent-0.16.8/src/main.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/main.cc 2026-04-06 16:00:46.000000000 +0200 @@ -419,6 +419,7 @@ // Deprecate: CMD2_VAR_STRING("dht.throttle.name", "deprecated"); + rpc::rpc.mark_safe("dht.throttle.name"); CMD2_REDIRECT("network.http.max_open", "network.http.max_total_connections"); CMD2_REDIRECT("network.http.max_open.set", "network.http.max_total_connections.set"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/option_parser.cc new/rtorrent-0.16.9/src/option_parser.cc --- old/rtorrent-0.16.8/src/option_parser.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/option_parser.cc 2026-04-06 16:00:46.000000000 +0200 @@ -1,37 +1,3 @@ -// rTorrent - BitTorrent client -// Copyright (C) 2005-2011, Jari Sundell -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -// In addition, as a special exception, the copyright holders give -// permission to link the code of portions of this program with the -// OpenSSL library under certain conditions as described in each -// individual source file, and distribute linked combinations -// including the two. -// -// You must obey the GNU General Public License in all respects for -// all of the code used other than OpenSSL. If you modify file(s) -// with this exception, you may extend this exception to your version -// of the file(s), but you are not obligated to do so. If you do not -// wish to do so, delete this exception statement from your version. -// If you delete this exception statement from all source files in the -// program, then also delete it here. -// -// Contact: Jari Sundell <[email protected]> - - #include "config.h" #include <algorithm> @@ -137,8 +103,11 @@ OptionParser::call_int_pair(slot_int_pair slot, const std::string& arg) { int a, b; - if (std::sscanf(arg.c_str(), "%u-%u", &a, &b) != 2) + if (std::sscanf(arg.c_str(), "%d-%d", &a, &b) != 2) throw std::runtime_error("Invalid argument, \"" + arg + "\" should be \"a-b\""); + if (a < 0 || b < 0) + throw std::runtime_error("Invalid argument, \"" + arg + "\" should be positive numbers"); + slot(a, b); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/rpc/command_map.cc new/rtorrent-0.16.9/src/rpc/command_map.cc --- old/rtorrent-0.16.8/src/rpc/command_map.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/rpc/command_map.cc 2026-04-06 16:00:46.000000000 +0200 @@ -98,11 +98,17 @@ if (itr == base_type::end()) throw torrent::input_error("Command \"" + std::string(key) + "\" does not exist."); + if (!rpc.is_trusted() && !(itr->second.m_flags & flag_untrusted_safe)) + throw untrusted_error("Command \"" + std::string(key) + "\" is not allowed for untrusted connections."); + return itr->second.m_anySlot(&itr->second.m_variable, target, arg); } const CommandMap::mapped_type CommandMap::call_command(iterator itr, const mapped_type& arg, const target_type& target) { + if (!rpc.is_trusted() && !(itr->second.m_flags & flag_untrusted_safe)) + throw untrusted_error("Command \"" + itr->first + "\" is not allowed for untrusted connections."); + return itr->second.m_anySlot(&itr->second.m_variable, target, arg); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/rpc/command_map.h new/rtorrent-0.16.9/src/rpc/command_map.h --- old/rtorrent-0.16.8/src/rpc/command_map.h 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/rpc/command_map.h 2026-04-06 16:00:46.000000000 +0200 @@ -56,6 +56,8 @@ static const int flag_file_target = 0x100; static const int flag_tracker_target = 0x200; + static const int flag_untrusted_safe = 0x400; + CommandMap() = default; bool has(const std::string& key) const { return base_type::find(key) != base_type::end(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/rpc/exec_file.cc new/rtorrent-0.16.9/src/rpc/exec_file.cc --- old/rtorrent-0.16.8/src/rpc/exec_file.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/rpc/exec_file.cc 2026-04-06 16:00:46.000000000 +0200 @@ -41,8 +41,13 @@ pid_t childPid = fork(); - if (childPid == -1) + if (childPid == -1) { + if (flags & flag_capture) { + ::close(pipeFd[0]); + ::close(pipeFd[1]); + } throw torrent::input_error("ExecFile::execute(...) Fork failed."); + } if (childPid == 0) { if (flags & flag_background) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/rpc/jsonrpc.cc new/rtorrent-0.16.9/src/rpc/jsonrpc.cc --- old/rtorrent-0.16.8/src/rpc/jsonrpc.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/rpc/jsonrpc.cc 2026-04-06 16:00:46.000000000 +0200 @@ -133,9 +133,12 @@ params_object_list.erase(params_object_list.begin()); - const auto& result = rpc::commands.call_command(itr, params_object, target); - - return object_to_json(result); + try { + const auto& result = rpc::commands.call_command(itr, params_object, target); + return object_to_json(result); + } catch (untrusted_error& e) { + throw rpc_error(JSONRPC_METHOD_NOT_FOUND_ERROR, e.what()); + } } json diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/rpc/parse.cc new/rtorrent-0.16.9/src/rpc/parse.cc --- old/rtorrent-0.16.8/src/rpc/parse.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/rpc/parse.cc 2026-04-06 16:00:46.000000000 +0200 @@ -120,11 +120,17 @@ case 'b': case 'B': ++last; break; case 'k': - case 'K': *value = *value << 10; ++last; break; + case 'K': + if (*value > (int64_t)0x1FFFFFFFFFFFFF) return src; // overflow guard + *value = *value << 10; ++last; break; case 'm': - case 'M': *value = *value << 20; ++last; break; + case 'M': + if (*value > (int64_t)0x7FFFFFFFFFF) return src; // overflow guard + *value = *value << 20; ++last; break; case 'g': - case 'G': *value = *value << 30; ++last; break; + case 'G': + if (*value > (int64_t)0x1FFFFFFFF) return src; // overflow guard + *value = *value << 30; ++last; break; // case ' ': // case '\0': *value = *value * unit; break; // default: throw torrent::input_error("Could not parse value."); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/rpc/rpc_manager.cc new/rtorrent-0.16.9/src/rpc/rpc_manager.cc --- old/rtorrent-0.16.8/src/rpc/rpc_manager.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/rpc/rpc_manager.cc 2026-04-06 16:00:46.000000000 +0200 @@ -13,6 +13,19 @@ RpcManager rpc; ExecFile execFile; +// Trusted/untrusted XMLRPC connection model. +// +// The trust state is set per-request by the SCGI layer based on the +// UNTRUSTED_CONNECTION header. Commands without flag_untrusted_safe +// are blocked for untrusted connections. The check is in +// CommandMap::call_command(), which catches all command execution +// including nested calls through argument expansion. + +bool +RpcManager::is_trusted() const { + return m_trusted; +} + void RpcManager::object_to_target(const torrent::Object& obj, int call_flags, rpc::target_type* target, std::function<void()>* deleter) { if (!obj.is_string()) @@ -124,6 +137,21 @@ } } +bool +RpcManager::process_untrusted(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback) { + bool previous = m_trusted; + m_trusted = false; + + try { + bool result = process(type, in_buffer, length, callback); + m_trusted = previous; + return result; + } catch (...) { + m_trusted = previous; + throw; + } +} + void RpcManager::initialize_handlers() { if (m_handlers_initialized) @@ -175,4 +203,14 @@ m_jsonrpc.insert_command(name, parm, doc); } +void +RpcManager::mark_safe(const std::string& key) { + auto itr = commands.find(key); + + if (itr == commands.end()) + return; + + itr->second.m_flags |= CommandMap::flag_untrusted_safe; +} + } // namespace rpc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/rpc/rpc_manager.h new/rtorrent-0.16.9/src/rpc/rpc_manager.h --- old/rtorrent-0.16.8/src/rpc/rpc_manager.h 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/rpc/rpc_manager.h 2026-04-06 16:00:46.000000000 +0200 @@ -4,6 +4,7 @@ #include <cstdint> #include <functional> #include <torrent/common.h> +#include <torrent/exceptions.h> #include "rpc/command.h" #include "rpc/command_map.h" @@ -34,6 +35,12 @@ std::string m_msg; }; +class untrusted_error : public torrent::input_error { +public: + using torrent::input_error::input_error; + virtual ~untrusted_error() throw() = default; +}; + class RpcManager { public: using slot_download = std::function<core::Download*(const char*)>; @@ -63,17 +70,26 @@ void set_type_enabled(RPCType type, bool enabled); bool process(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback); + bool process_untrusted(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback); void insert_command(const char* name, const char* parm, const char* doc); + void mark_safe(const std::string& key); slot_download& slot_find_download() { return m_slot_find_download; } slot_file& slot_find_file() { return m_slot_find_file; } slot_tracker& slot_find_tracker() { return m_slot_find_tracker; } slot_peer& slot_find_peer() { return m_slot_find_peer; } + // Trusted/untrusted XMLRPC connection model. + // When an SCGI request includes the UNTRUSTED_CONNECTION header, + // commands without flag_untrusted_safe are blocked. + bool is_trusted() const; + static void object_to_target(const torrent::Object& obj, int callFlags, rpc::target_type* target, std::function<void()>* deleter); private: + bool m_trusted{true}; + XmlRpc m_xmlrpc; JsonRpc m_jsonrpc; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/rpc/scgi.cc new/rtorrent-0.16.9/src/rpc/scgi.cc --- old/rtorrent-0.16.8/src/rpc/scgi.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/rpc/scgi.cc 2026-04-06 16:00:46.000000000 +0200 @@ -2,6 +2,7 @@ #include <algorithm> #include <cassert> +#include <fcntl.h> #include <unistd.h> #include <sys/un.h> #include <torrent/connection_manager.h> @@ -81,6 +82,19 @@ } void +SCgi::open_fd(int fd) { + torrent::runtime::socket_manager()->open_event_or_throw(this, [&]() { + if (!torrent::fd_set_nonblock(fd)) + throw torrent::resource_error("Could not set non-blocking on systemd fd: " + + std::string(std::strerror(errno))); + set_file_descriptor(fd); + // fd is already bound and listening; no bind()/listen() needed. + }); + + torrent::connection_manager()->inc_socket_count(); +} + +void SCgi::open(sockaddr* sa, unsigned int length) { try { if (::bind(file_descriptor(), sa, length) == -1) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/rpc/scgi.h new/rtorrent-0.16.9/src/rpc/scgi.h --- old/rtorrent-0.16.8/src/rpc/scgi.h 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/rpc/scgi.h 2026-04-06 16:00:46.000000000 +0200 @@ -21,6 +21,7 @@ void open_port(sockaddr* sa, unsigned int length, bool dont_route); void open_named(const std::string& filename); + void open_fd(int fd); void activate(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/rpc/scgi_task.cc new/rtorrent-0.16.9/src/rpc/scgi_task.cc --- old/rtorrent-0.16.8/src/rpc/scgi_task.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/rpc/scgi_task.cc 2026-04-06 16:00:46.000000000 +0200 @@ -111,6 +111,9 @@ size_t content_length = 0; const char* header_end = current + header_size; + // Assume trusted until we find the UNTRUSTED_CONNECTION header. + m_trusted = true; + // Parse out the null-terminated header keys and values, with // checks to ensure it doesn't scan beyond the limits of the // header @@ -141,6 +144,8 @@ goto event_read_failed; } else if (strcmp(key, "CONTENT_TYPE") == 0) { content_type = value; + } else if (strcmp(key, "UNTRUSTED_CONNECTION") == 0 && strcmp(value, "1") == 0) { + m_trusted = false; } } @@ -200,7 +205,7 @@ void SCgiTask::event_write() { - int bytes = ::send(m_fileDesc, m_position, m_buffer_size, MSG_NOSIGNAL); + int bytes = ::send(m_fileDesc, m_position, m_buffer_size, 0); if (bytes == -1) { if (!(errno == EAGAIN || errno == EINTR)) @@ -270,6 +275,7 @@ // TODO: Rewrite RpcManager.process to pass the result buffer instead of having to copy it. auto scgi_thread = torrent::utils::Thread::self(); + bool trusted = m_trusted; auto result_callback = [this, scgi_thread](const char* b, uint32_t l) { receive_write(b, l); @@ -285,30 +291,30 @@ auto lock = std::lock_guard<std::mutex>(m_result_mutex); + RpcManager::RPCType rpc_type; + switch (content_type()) { case rpc::SCgiTask::ContentType::JSON: - torrent::main_thread::thread()->callback_interrupt_polling(this, [buffer, length, result_callback]() { - rpc.process(RpcManager::RPCType::JSON, buffer, length, - [result_callback](const char* b, uint32_t l) { - result_callback(b, l); - return true; - }); - }); + rpc_type = RpcManager::RPCType::JSON; break; - case rpc::SCgiTask::ContentType::XML: - torrent::main_thread::thread()->callback_interrupt_polling(this, [buffer, length, result_callback]() { - rpc.process(RpcManager::RPCType::XML, buffer, length, - [result_callback](const char* b, uint32_t l) { - result_callback(b, l); - return true; - }); - }); + rpc_type = RpcManager::RPCType::XML; break; - default: throw torrent::internal_error("SCgiTask::receive_call(...) received bad input."); } + + torrent::main_thread::thread()->callback_interrupt_polling(this, [buffer, length, result_callback, trusted, rpc_type]() { + auto callback = [result_callback](const char* b, uint32_t l) { + result_callback(b, l); + return true; + }; + + if (trusted) + rpc.process(rpc_type, buffer, length, callback); + else + rpc.process_untrusted(rpc_type, buffer, length, callback); + }); } void diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/rpc/scgi_task.h new/rtorrent-0.16.9/src/rpc/scgi_task.h --- old/rtorrent-0.16.8/src/rpc/scgi_task.h 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/rpc/scgi_task.h 2026-04-06 16:00:46.000000000 +0200 @@ -53,6 +53,7 @@ unsigned int m_buffer_size{0}; ContentType m_content_type{ XML }; + bool m_trusted{true}; }; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/rpc/xmlrpc_c.cc new/rtorrent-0.16.9/src/rpc/xmlrpc_c.cc --- old/rtorrent-0.16.8/src/rpc/xmlrpc_c.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/rpc/xmlrpc_c.cc 2026-04-06 16:00:46.000000000 +0200 @@ -385,6 +385,10 @@ return object_to_xmlrpc(env, rpc::commands.call_command(itr, object, target)); + } catch (untrusted_error& e) { + xmlrpc_env_set_fault(env, XMLRPC_REQUEST_REFUSED_ERROR, e.what()); + return NULL; + } catch (xmlrpc_error_c& e) { xmlrpc_env_set_fault(env, e.type(), e.what()); return NULL; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/rpc/xmlrpc_tinyxml2.cc new/rtorrent-0.16.9/src/rpc/xmlrpc_tinyxml2.cc --- old/rtorrent-0.16.8/src/rpc/xmlrpc_tinyxml2.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/rpc/xmlrpc_tinyxml2.cc 2026-04-06 16:00:46.000000000 +0200 @@ -31,7 +31,7 @@ // const int XMLRPC_NETWORK_ERROR = -504; // const int XMLRPC_TIMEOUT_ERROR = -505; const int XMLRPC_NO_SUCH_METHOD_ERROR = -506; -// const int XMLRPC_REQUEST_REFUSED_ERROR = -507; +const int XMLRPC_REQUEST_REFUSED_ERROR = -507; // const int XMLRPC_INTROSPECTION_DISABLED_ERROR = -508; const int XMLRPC_LIMIT_EXCEEDED_ERROR = -509; // const int XMLRPC_INVALID_UTF8_ERROR = -510; @@ -238,7 +238,11 @@ throw rpc_error(XMLRPC_TYPE_ERROR, "invalid parameters: too few"); } - return rpc::commands.call_command(cmd_itr, params_raw, target); + try { + return rpc::commands.call_command(cmd_itr, params_raw, target); + } catch (untrusted_error& e) { + throw rpc_error(XMLRPC_REQUEST_REFUSED_ERROR, e.what()); + } } void diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/session/session_manager.cc new/rtorrent-0.16.9/src/session/session_manager.cc --- old/rtorrent-0.16.8/src/session/session_manager.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/session/session_manager.cc 2026-04-06 16:00:46.000000000 +0200 @@ -112,8 +112,10 @@ if (!m_active) throw torrent::internal_error("SessionManager::save_download() called while not active."); - if (replace_save_request_unsafe(save_request)) - throw torrent::internal_error("SessionManager::save_full_download() replacing existing save request, not supported?"); + if (replace_save_request_unsafe(save_request)) { + LT_LOG("updated pending save request with full save data : download:%p", download); + return; + } m_save_requests.push_back(std::move(save_request)); m_save_request_counter = m_save_requests.size(); @@ -426,11 +428,22 @@ if (itr->path != save_request.path) throw torrent::internal_error("SessionManager::replace_save_request_unsafe() path mismatch on replace: " + itr->path + " != " + save_request.path); - if (save_request.torrent_stream != nullptr) - throw torrent::internal_error("SessionManager::replace_save_request_unsafe() cannot replace full save requests."); - - itr->rtorrent_stream = std::move(save_request.rtorrent_stream); - itr->libtorrent_stream = std::move(save_request.libtorrent_stream); + // If the existing request is a full save (created during torrent initialization), + // keep its torrent_stream but update the resume streams with newer data. + // Otherwise, replace all streams. + if (itr->torrent_stream != nullptr) { + // Keep existing full-save torrent stream, only update resume streams + if (save_request.rtorrent_stream != nullptr) + itr->rtorrent_stream = std::move(save_request.rtorrent_stream); + + if (save_request.libtorrent_stream != nullptr) + itr->libtorrent_stream = std::move(save_request.libtorrent_stream); + } else { + // No full save pending, replace everything + itr->torrent_stream = std::move(save_request.torrent_stream); + itr->rtorrent_stream = std::move(save_request.rtorrent_stream); + itr->libtorrent_stream = std::move(save_request.libtorrent_stream); + } return true; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/ui/element_download_list.cc new/rtorrent-0.16.9/src/ui/element_download_list.cc --- old/rtorrent-0.16.8/src/ui/element_download_list.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/ui/element_download_list.cc 2026-04-06 16:00:46.000000000 +0200 @@ -167,6 +167,8 @@ void ElementDownloadList::receive_end() { + if (m_view->size_visible() == 0) + return; m_view->set_focus(m_view->end_visible() - 1); m_view->set_last_changed(); } @@ -221,10 +223,10 @@ 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: "); + rpc::commands.call_catch("event.view.hide", rpc::make_target(), old_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: "); + if (!name.empty()) + rpc::commands.call_catch("event.view.show", rpc::make_target(), name, "View show event action failed: "); } void diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/src/ui/root.cc new/rtorrent-0.16.9/src/ui/root.cc --- old/rtorrent-0.16.8/src/ui/root.cc 2026-03-15 17:58:09.000000000 +0100 +++ new/rtorrent-0.16.9/src/ui/root.cc 2026-04-06 16:00:46.000000000 +0200 @@ -496,7 +496,7 @@ } else if (style == "emacs") { m_keymap = emacs_keymap; } else { - throw torrent::input_error("Root::set_keymap_style() -> ui.keymap.style is configured with unknown keymap style: " + m_keymap_style); + throw torrent::input_error("Root::set_keymap_style() -> ui.keymap.style is configured with unknown keymap style: " + style); } m_keymap_style = style; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/rtorrent-0.16.8/test/Makefile.in new/rtorrent-0.16.9/test/Makefile.in --- old/rtorrent-0.16.8/test/Makefile.in 2026-03-15 18:07:04.000000000 +0100 +++ new/rtorrent-0.16.9/test/Makefile.in 2026-04-06 16:10:12.000000000 +0200 @@ -561,6 +561,8 @@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ +SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ +SYSTEMD_LIBS = @SYSTEMD_LIBS@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@
