Compare commits

...

49 Commits

Author SHA1 Message Date
Xirvik Support 49625ab5e5 rpc: close SCGI task on EPIPE to stop event_write() busy-loop
When an SCGI client closes the connection before rtorrent finishes sending
the response, send() in SCgiTask::event_write() returns -1 with errno EPIPE.
EPIPE was grouped with EAGAIN/EINTR as a non-fatal retry-later condition, so
the task was not closed and its descriptor stayed registered for EPOLLOUT. A
broken socket is reported writable immediately, so epoll_wait() returns it on
every iteration and the SCGI thread spins at 100% CPU on one core
indefinitely. The dead connection fd is also leaked (stays ESTAB).

EPIPE is terminal here, not retryable: the peer is gone and the response can
never be delivered. Close the task on EPIPE, matching event_read(), which
already closes on any recv() error other than EAGAIN/EINTR.

Reproduction: open the SCGI socket, send a complete RPC request, then
shutdown(SHUT_RDWR)/close before reading the reply. Stock: the rtorrent-scgi
thread goes to 100% CPU and the connection leaks. With this change: CPU stays
at 0% and the descriptor is closed.
2026-06-04 10:41:19 +02:00
Jari Sundell cc15e9308a Fixed xmlrpc-c build errors and added better github workflow for unit-tests. 2026-06-04 17:16:47 +09:00
Jari Sundell a5a96236df Moved base64 transform and validation functions. 2026-06-04 06:37:01 +09:00
Jari Sundell f1cfe8ad72 Added hex to Object and string_utf8. 2026-06-04 04:22:16 +09:00
rakshasa d0fc48cf7b Tagged release 0.16.13. 2026-06-03 10:18:25 +02:00
trim21 5725eb3773 fix: move --without-ncurses logic to scripts/checks.m4 per review
- Add TORRENT_WITHOUT_NCURSES macro in scripts/checks.m4
- Clear CURSES_LIBS/CFLAGS in the macro instead of if/else in configure.ac
- Restore simple LIBS/CFLAGS lines in configure.ac
- Add missing set_escdelay stub
2026-06-01 20:33:39 +02:00
trim21 ca706c21c0 feat: add --without-ncurses option for daemon-only builds
Add a dummy curses stub header that provides all ncurses types, macros,
and no-op function stubs. When configured with --without-ncurses, the
build uses this stub instead of linking to the real ncurses library.

This allows rtorrent to be built for daemon-only usage (e.g. with
ruTorrent or Flood) without requiring ncurses to be installed.

Closes #1613
2026-06-01 20:33:39 +02:00
Jari Sundell 14edd68653 Removed SignalBitfield and replaced it with callbacks. 2026-06-01 22:40:22 +09:00
Jari Sundell 50a609ade9 Fixed cacheline size check. 2026-06-01 20:11:02 +09:00
Jari Sundell 2cf8f2351b Fixed github workflow. 2026-05-31 00:37:22 +09:00
Jari Sundell add305f90c Added support base64 support for non-UTF8 strings. 2026-05-30 00:25:16 +09:00
Jari Sundell 880510073a Updated LUA autoconf script. 2026-05-27 21:37:09 +09:00
rakshasa 0989b2218d Various cleanup and fixes to SocketManager categories. 2026-05-27 12:38:52 +02:00
Trim21 3d7e6cb077 Added per-category allocation limits to SocketManager by @trim21 2026-05-27 18:16:02 +09:00
Jari Sundell 64881317c6 Fix RPC/SCGI security and crash bugs by @sirus20x6 2026-05-26 17:27:10 +09:00
rakshasa a5ecd89a54 Removed unnessesary configure check from PR. 2026-05-25 18:21:31 +02:00
tonimelisma 84229a85be Add directory.watch.ready for safe torrent watch folders 2026-05-25 18:21:31 +02:00
Jari Sundell e03ab27cf0 Added intermediate requesting flag for trackers to prevent premature deletion. 2026-05-26 00:43:15 +09:00
Jari Sundell e41b066555 Converted callbacks to new interface. 2026-05-25 20:13:45 +09:00
Jari Sundell 8318133c79 Added a new callback mechanism and use it for trackers. 2026-05-23 23:43:45 +09:00
rakshasa 088bf40bdf Removed obsolete use of trackers.use_udp. 2026-05-18 16:35:20 +02:00
rakshasa 2f7a984992 Tagged release 0.16.12. 2026-05-18 15:25:56 +02:00
Jari Sundell 840a57915e Deprecate trackers.use_udp as it is no longer needed. 2026-05-17 00:59:03 +09:00
Jari Sundell 5e211c3a28 Allow for quick shutdown after sending the first UDP announce packet. 2026-05-15 20:58:17 +09:00
Jari Sundell 797a194abc Cleaned up torrent header files. 2026-05-14 19:24:38 +09:00
Trim21 501853cfbc Delete repro-scgi-content-type.py 2026-05-11 11:42:36 +02:00
trim21 b0ef95592b fix SCGI parse_headers: defer content-type body peek until body received
detect_content_type() peeked at m_buffer[m_body] to infer JSON vs XML
when no CONTENT_TYPE header was provided.  When the TCP header segment
arrives without any body bytes, m_body equals m_position and the peek
reads the null terminator padding byte — not the actual '{' or '[' —
causing JSON requests to be incorrectly classified as XML and fail.

Fix:
 - Remove the body peek from detect_content_type(); defer it to after
   the full body is confirmed present in event_read().
 - Add a m_content_type_set flag to distinguish header-provided type
   from auto-detected type.
2026-05-11 11:42:36 +02:00
Miroslav Marchev 26a7e9f545 Update to version 11.0.0 2026-05-10 09:25:02 +02:00
Miroslav Marchev 0ed10984b3 Update to version 11.0.0 2026-05-10 09:25:02 +02:00
Miroslav Marchev 5f09bb74de Updated to version 3.12.0 2026-05-10 09:25:02 +02:00
Jari Sundell c368b2de1e Fix SCGI event_read: return on partial header read instead of closing connection @trim21 2026-05-10 16:01:45 +09:00
Jari Sundell 71ac256cb5 Moved socket counter to runtime::SocketManager. 2026-05-10 02:11:35 +09:00
rakshasa b05b65bd65 Enable SCGI compression by default and set min size to 1000. 2026-05-09 12:24:24 +02:00
Jari Sundell f05b48e228 Moved NetworkConfig to runtime. 2026-05-05 16:17:24 +09:00
Xirvik 1279bd9f7a Move d.save_full_session to run after user-registered inserted_new handlers
Previously d.save_full_session was inside the 1_prepare handler for
event.download.inserted_new. Since 0.16.6 moved session saving to a
separate thread, save_full_download() snapshots the bencode synchronously
when called and queues the prebuilt streams for async write.

If user-registered handlers (e.g. seedingtime plugin's addtime setter
running d.set_custom=addtime) sort lexicographically AFTER 1_prepare, they
modify the bencode AFTER the snapshot has already been taken. The first
on-disk .rtorrent then lacks those custom fields. The next periodic
resume save catches up, but if rtorrent restarts before that, the data
is lost — manifesting as blank Finished/SeedingTime columns in ruTorrent.

Fix: split the inserted_new key into two — 1_prepare keeps view
visibility setup, ~_save_full runs d.save_full_session last (~ prefix
is ASCII 0x7E, sorts after all alphanumerics, matching the existing
~_delete_tied precedent on event.download.erased). The snapshot then
includes any custom fields written by user handlers.
2026-05-05 08:40:13 +02:00
Xirvik 981184574d Reset SCgiTask m_trusted on connection reuse
SCgiTask objects are pre-allocated in a pool (scgi.cc) and reused across
SCGI connections. SCgiTask::open() did not reset m_trusted, so when a
task that had handled an untrusted connection (m_trusted=false) was
reused for a new connection, m_trusted stayed false unless the new
connection explicitly sent UNTRUSTED_CONNECTION=1.

The header parser only set m_trusted=false on value 1 and was a no-op
on value 0 (the comment said "default is trusted, so do nothing") —
which is wrong for a reused task that is no longer in default state.

This caused intermittent rejection of trusted commands (e.g. ruTorrent
calling execute.capture for UID detection) with "Command X is not allowed
for untrusted connections", producing cascading plugin failures and
"ruTorrent cannot determine the UID of rTorrent user" in the web UI.

Fix:
- SCgiTask::open() resets m_trusted=true to default.
- parse_headers explicitly sets m_trusted=true on UNTRUSTED_CONNECTION=0,
  so the value sent on the wire is authoritative regardless of pool
  reuse semantics.

Verified on gb4 with rtorrent 0.16.11 + this fix: 30/30 trusted calls
succeed, 30/30 untrusted correctly blocked, 30/30 trusted-after-untrusted
batch all succeed (previously 70%+ would fail in the same scenario).
2026-05-04 10:06:48 +02:00
rakshasa 2700b3141f Tagged release 0.16.11. 2026-05-01 11:29:09 +02:00
fffe 4fec56a243 assert valid read_length 2026-04-24 12:22:34 +02:00
fffe dcf24711ef refactor 2026-04-24 12:22:34 +02:00
fffe 4e3ad1ab62 fix off-by-one in SCgiTask::event_read 2026-04-24 12:22:34 +02:00
rakshasa b5a606649a Tagged release 0.16.10. 2026-04-23 10:31:51 +02:00
Jari Sundell eb96876eb2 Moved Thread header to torrent/system directory. 2026-04-22 23:02:05 +09:00
Jari Sundell a964b0a350 Added special syntax to config files to enable log categories early. 2026-04-20 01:07:38 +09:00
Jari Sundell 1adcf230d2 Fix core subsystem logic and safety bugs @Sirus20x6 2026-04-18 01:54:46 +09:00
rakshasa 4235507292 Fixed path expand when doing a single '~' when expanding directory entries. 2026-04-17 12:13:27 +02:00
Jari Sundell b7a21b1e9d Added support for gzip accepted_encoding in scgi headers. 2026-04-12 22:19:49 +09:00
Jari Sundell bd53959b40 Fixed session save getting stuck. 2026-04-11 16:50:58 +09:00
Jari Sundell 5a4ba8bc32 Removed deprecated rak header files. 2026-04-10 22:04:02 +09:00
Jari Sundell e64ec358fa Removed deprecated rak headers. 2026-04-10 03:49:38 +09:00
121 changed files with 3152 additions and 2674 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
permissions:
pull-requests: write
# OPTIONAL: auto-closing conversations requires the `contents` permission
contents: write
contents: read
steps:
- name: Sleep for 30 seconds
run: sleep 30s
+2 -1
View File
@@ -16,7 +16,8 @@ jobs:
sudo apt-get install -y \
bear \
clang-tidy \
libcurl4-openssl-dev
libcurl4-openssl-dev \
zlib1g-dev
- name: Fetch libtorrent
run: |
git clone https://github.com/rakshasa/libtorrent
+64 -27
View File
@@ -4,9 +4,8 @@ on: pull_request
jobs:
unit-tests:
ubuntu-base:
runs-on: ubuntu-22.04
steps:
- name: Update Packages
run: |
@@ -15,6 +14,7 @@ jobs:
run: |
sudo apt-get install -y \
libcppunit-dev \
zlib1g-dev \
libcurl4-openssl-dev
- name: Fetch libtorrent
run: |
@@ -22,43 +22,80 @@ jobs:
- name: Build libtorrent
run: |
cd libtorrent
libtoolize
aclocal -I scripts
autoconf -i
autoheader
automake --add-missing
autoreconf -fiv
./configure
make
sudo make install
cd ..
rm -rf libtorrent
- uses: actions/checkout@v4
sudo make install DESTDIR=$GITHUB_WORKSPACE/libtorrent-dist
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Fetch base branch
name: installed-libtorrent
path: libtorrent-dist/
unit-tests-default:
runs-on: ubuntu-22.04
needs: ubuntu-base
steps:
- name: Install Dependencies
run: |
git remote add upstream "https://github.com/${{ github.event.pull_request.base.repo.full_name }}"
git fetch --no-tags --no-recurse-submodules upstream "${{ github.event.pull_request.base.ref }}"
sudo apt-get install -y \
libcppunit-dev \
zlib1g-dev \
libcurl4-openssl-dev
- name: Download Libtorrent Artifacts
uses: actions/download-artifact@v4
with:
name: installed-libtorrent
path: ./libtorrent-dist
- name: Move Artifacts to System Path
run: |
# Elevate permissions with sudo to safely place the files
sudo cp -r ./libtorrent-dist/usr/local/* /usr/local/
rm -rf ./libtorrent-dist
- uses: actions/checkout@v4
- name: Configure Project
run: |
libtoolize
aclocal -I scripts
autoconf -i
autoheader
automake --add-missing
autoreconf -fiv
./configure
- name: Build Project
run: |
make
- name: Run Unit Tests
run: |
ls /usr/local/lib/
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib"
make check
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib" make check
- name: Archive test/test-suite.log
if: success() || failure()
uses: actions/upload-artifact@v4
with:
name: test-suite.log
path: test/test-suite.log
unit-tests-variants:
runs-on: ubuntu-22.04
needs: unit-tests-default
strategy:
matrix:
config_flag: ["--with-xmlrpc-c", "--with-xmlrpc-tinyxml2"]
steps:
- name: Install Dependencies
run: |
sudo apt-get install -y \
libcppunit-dev \
zlib1g-dev \
libcurl4-openssl-dev \
libxmlrpc-c++8-dev
- name: Download Libtorrent Artifacts
uses: actions/download-artifact@v4
with:
name: installed-libtorrent
path: ./libtorrent-dist
- name: Move Artifacts to System Path
run: |
sudo cp -r ./libtorrent-dist/usr/local/* /usr/local/
rm -rf ./libtorrent-dist
- uses: actions/checkout@v4
- name: Configure Project
run: |
autoreconf -fiv
./configure ${{ matrix.config_flag }}
make
ls /usr/local/lib/
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib" make check
+2
View File
@@ -18,6 +18,7 @@
.libs
Makefile
aclocal.m4
actmp.*
ar-lib
autom4te.cache
compile
@@ -69,4 +70,5 @@ TAGS
###########################
src/rtorrent
test/rtorrent_Test*
test/test-suite.log.tmp
test-driver
-5
View File
@@ -7,12 +7,7 @@ nobase_dist_pkgdata_DATA = \
lua/rtorrent.lua
EXTRA_DIST= \
rak/address_info.h \
rak/algorithm.h \
rak/partial_queue.h \
rak/regex.h \
rak/string_manip.h \
rak/unordered_vector.h \
scripts/checks.m4 \
scripts/common.m4 \
scripts/attributes.m4
+22 -12
View File
@@ -1,6 +1,6 @@
m4_pattern_allow([PKG_CHECK_EXISTS])
AC_INIT([rtorrent],[0.16.9],[sundell.software@gmail.com])
AC_INIT([rtorrent],[0.16.13],[sundell.software@gmail.com])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIRS([scripts])
@@ -11,12 +11,12 @@ LT_INIT
AC_PROG_CXX
AC_DEFINE([API_VERSION], [19], [api version])
AC_DEFINE([API_VERSION], [21], [api version])
# Filter out unwanted flags added by autoconf on some systems, e.g. MacOS.
TORRENT_REMOVE_UNWANTED(CXX, $CXX, -std=c++11 -std=gnu++11)
AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory])
AX_CXX_COMPILE_STDCXX(20, noext, mandatory)
AC_SYS_LARGEFILE
@@ -41,19 +41,28 @@ AC_ARG_ENABLE(execinfo,
])
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])
TORRENT_WITHOUT_NCURSES
if test "x$with_ncurses" != xno; then
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])
fi
fi
PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.9])
PKG_CHECK_MODULES([ZLIB], [zlib])
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.13])
AC_LANG_PUSH(C++)
TORRENT_WITH_XMLRPC_C
AC_LANG_POP(C++)
dnl We don't have LuaJIT support, and the script breaks if not disabled.
AM_CONDITIONAL([LUAJIT], [false])
TORRENT_WITH_LUA
TORRENT_WITH_TINYXML2
TORRENT_WITH_SYSTEMD
@@ -62,15 +71,16 @@ 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])
fi
AC_DEFINE(USER_AGENT, [std::string(PACKAGE "/" VERSION "/") + torrent::version()], Http user agent)
AC_DEFINE(USER_AGENT, [std::string(PACKAGE "/" VERSION)], Http user agent)
dnl Only update global build variables immediately before generating the output,
dnl to avoid affecting the global build environment for other autoconf checks.
LIBS="$PTHREAD_LIBS $CURSES_LIB $CURSES_LIBS $DEPENDENCIES_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
LIBS="$PTHREAD_LIBS $CURSES_LIB $CURSES_LIBS $ZLIB_LIBS $DEPENDENCIES_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS"
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS"
TORRENT_CHECK_POPCOUNT()
TORRENT_CHECK_CACHELINE
TORRENT_CHECK_POPCOUNT
AC_CONFIG_FILES([
Makefile
+5 -1
View File
@@ -46,8 +46,12 @@
#session.path.set = ./session
# Watch a directory for new torrents, and stop those that have been
# deleted.
# deleted. Use directory.watch.ready for network shares or other watch
# directories where files may be copied or written directly into place.
# Do not mix directory.watch.ready and directory.watch.added on the same
# watch directory.
#
#directory.watch.ready = ./watch, load.start
#schedule2 = watch_directory,5,5,load.start=./watch/*.torrent
# Close torrents when disk-space is low.
-60
View File
@@ -1,60 +0,0 @@
// Wrapper for addrinfo with focus on zero-copy conversion to and from
// the c-type and wrapper.
//
// Do use the wrapper on a pre-existing struct addrinfo, cast the
// pointer rather than the base type.
#ifndef RAK_ADDRESS_INFO_H
#define RAK_ADDRESS_INFO_H
#include <netdb.h>
#include <cstring>
namespace rak {
class address_info {
public:
void clear() { std::memset(this, 0, sizeof(address_info)); }
int flags() const { return m_addrinfo.ai_flags; }
void set_flags(int f) { m_addrinfo.ai_flags = f; }
int family() const { return m_addrinfo.ai_family; }
void set_family(int f) { m_addrinfo.ai_family = f; }
int socket_type() const { return m_addrinfo.ai_socktype; }
void set_socket_type(int t) { m_addrinfo.ai_socktype = t; }
int protocol() const { return m_addrinfo.ai_protocol; }
void set_protocol(int p) { m_addrinfo.ai_protocol = p; }
size_t length() const { return m_addrinfo.ai_addrlen; }
addrinfo* c_addrinfo() { return &m_addrinfo; }
const addrinfo* c_addrinfo() const { return &m_addrinfo; }
address_info* next() { return reinterpret_cast<address_info*>(m_addrinfo.ai_next); }
static int get_address_info(const char* node, int domain, int type, address_info** ai);
static void free_address_info(address_info* ai) { ::freeaddrinfo(ai->c_addrinfo()); }
static const char* strerror(int err) { return gai_strerror(err); }
private:
addrinfo m_addrinfo;
};
inline int
address_info::get_address_info(const char* node, int pfamily, int stype, address_info** ai) {
address_info hints;
hints.clear();
hints.set_family(pfamily);
hints.set_socket_type(stype);
return ::getaddrinfo(node, NULL, hints.c_addrinfo(), reinterpret_cast<addrinfo**>(ai));
}
}
#endif
-195
View File
@@ -1,195 +0,0 @@
// rak - Rakshasa's toolbox
// Copyright (C) 2005-2007, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#ifndef RAK_ALGORITHM_H
#define RAK_ALGORITHM_H
#include <algorithm>
#include <limits>
namespace rak {
template <typename _InputIter, typename _Function>
_Function
for_each_pre(_InputIter __first, _InputIter __last, _Function __f) {
_InputIter __tmp;
while (__first != __last) {
__tmp = __first++;
__f(*__tmp);
}
return __f;
}
// Return a range with a distance of no more than __distance and
// between __first and __last, centered on __middle1.
template <typename _InputIter, typename _Distance>
std::pair<_InputIter, _InputIter>
advance_bidirectional(_InputIter __first, _InputIter __middle1, _InputIter __last, _Distance __distance) {
_InputIter __middle2 = __middle1;
do {
if (!__distance)
break;
if (__middle2 != __last) {
++__middle2;
--__distance;
} else if (__middle1 == __first) {
break;
}
if (!__distance)
break;
if (__middle1 != __first) {
--__middle1;
--__distance;
} else if (__middle2 == __last) {
break;
}
} while (true);
return std::make_pair(__middle1, __middle2);
}
template <typename _InputIter, typename _Distance>
_InputIter
advance_forward(_InputIter __first, _InputIter __last, _Distance __distance) {
while (__first != __last && __distance != 0) {
__first++;
__distance--;
}
return __first;
}
template <typename _InputIter, typename _Distance>
_InputIter
advance_backward(_InputIter __first, _InputIter __last, _Distance __distance) {
while (__first != __last && __distance != 0) {
__first--;
__distance--;
}
return __first;
}
template <typename _Value>
struct compare_base {
bool operator () (const _Value& complete, const _Value& base) const {
return !complete.compare(0, base.size(), base);
}
};
// Count the number of elements from the start of the containers to
// the first inequal element.
template <typename _InputIter1, typename _InputIter2>
typename std::iterator_traits<_InputIter1>::difference_type
count_base(_InputIter1 __first1, _InputIter1 __last1,
_InputIter2 __first2, _InputIter2 __last2) {
typename std::iterator_traits<_InputIter1>::difference_type __n = 0;
for ( ;__first1 != __last1 && __first2 != __last2; ++__first1, ++__first2, ++__n)
if (*__first1 != *__first2)
return __n;
return __n;
}
template <typename _Return, typename _InputIter, typename _Ftor>
_Return
make_base(_InputIter __first, _InputIter __last, _Ftor __ftor) {
if (__first == __last)
return "";
_Return __base = __ftor(*__first++);
for ( ;__first != __last; ++__first) {
typename std::iterator_traits<_InputIter>::difference_type __pos = count_base(__base.begin(), __base.end(),
__ftor(*__first).begin(), __ftor(*__first).end());
if (__pos < (typename std::iterator_traits<_InputIter>::difference_type)__base.size())
__base.resize(__pos);
}
return __base;
}
template<typename T>
inline int popcount_wrapper(T t) {
#if USE_BUILTIN_POPCOUNT
return __builtin_popcountll(t);
#else
#error __builtin_popcount not found.
unsigned int count = 0;
while (t) {
count += t & 0x1;
t >> 1;
}
return count;
#endif
}
// Get the median of an unordered set of numbers of arbitrary
// type by modifing the underlying dataset
template <typename T = double, typename _InputIter>
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
-197
View File
@@ -1,197 +0,0 @@
// rak - Rakshasa's toolbox
// Copyright (C) 2005-2007, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#ifndef RAK_PARTIAL_QUEUE_H
#define RAK_PARTIAL_QUEUE_H
#include <array>
#include <cstring>
#include <stdexcept>
#include <cinttypes>
namespace rak {
// First step, don't allow overflowing to the next layer. Only disable
// the above layers for now.
// We also include 0 in a single layer as some chunk may be available
// only through seeders.
class partial_queue {
public:
typedef uint8_t key_type;
typedef uint32_t mapped_type;
typedef uint16_t size_type;
typedef std::pair<size_type, size_type> size_pair_type;
static const size_type num_layers = 8;
partial_queue() : m_data(NULL), m_maxLayerSize(0) {}
~partial_queue() { disable(); }
bool is_full() const { return m_ceiling == 0; }
bool is_layer_full(size_type l) const { return m_layers[l].second >= m_maxLayerSize; }
bool is_enabled() const { return m_data != NULL; }
// Add check to see if we can add more. Also make it possible to
// check how full we are in the lower parts so the caller knows when
// he can stop searching.
//
// Though propably not needed, as we must continue til the first
// layer is full.
size_type max_size() const { return m_maxLayerSize * num_layers; }
size_type max_layer_size() const { return m_maxLayerSize; }
// Must be less that or equal to (max size_type) / num_layers.
void enable(size_type ls);
void disable();
void clear();
// Safe to call while pop'ing and it will not reuse pop'ed indices
// so it is guaranteed to reach max_size at some point. This will
// ensure that the user needs to refill with new data at regular
// intervals.
bool insert(key_type key, mapped_type value);
// Only call this when pop'ing as it moves the index.
bool prepare_pop();
mapped_type pop();
private:
partial_queue(const partial_queue&);
void operator = (const partial_queue&);
static size_type ceiling(size_type layer) { return (2 << layer) - 1; }
void find_non_empty();
mapped_type* m_data;
size_type m_maxLayerSize;
size_type m_index;
size_type m_ceiling;
std::array<size_pair_type, num_layers> m_layers;
};
inline void
partial_queue::enable(size_type ls) {
if (ls == 0)
throw std::logic_error("partial_queue::enable(...) ls == 0.");
delete [] m_data;
m_data = new mapped_type[ls * num_layers];
m_maxLayerSize = ls;
}
inline void
partial_queue::disable() {
delete [] m_data;
m_data = NULL;
m_maxLayerSize = 0;
}
inline void
partial_queue::clear() {
if (m_data == NULL)
return;
m_index = 0;
m_ceiling = ceiling(num_layers - 1);
m_layers = {};
}
inline bool
partial_queue::insert(key_type key, mapped_type value) {
if (key >= m_ceiling)
return false;
size_type idx = 0;
// Hmm... since we already check the 'm_ceiling' above, we only need
// to find the target layer. Could this be calculated directly?
while (key >= ceiling(idx))
++idx;
m_index = std::min(m_index, idx);
// Currently don't allow overflow.
if (is_layer_full(idx))
throw std::logic_error("partial_queue::insert(...) layer already full.");
//return false;
m_data[m_maxLayerSize * idx + m_layers[idx].second] = value;
m_layers[idx].second++;
if (is_layer_full(idx))
// Set the ceiling to 0 when layer 0 is full so no more values can
// be inserted.
m_ceiling = idx > 0 ? ceiling(idx - 1) : 0;
return true;
}
// is_empty() will iterate to the first layer with un-popped elements
// and return true, else return false when it reaches a overflowed or
// the last layer.
inline bool
partial_queue::prepare_pop() {
while (m_layers[m_index].first == m_layers[m_index].second) {
if (is_layer_full(m_index) || m_index + 1 == num_layers)
return false;
m_index++;
}
return true;
}
inline partial_queue::mapped_type
partial_queue::pop() {
if (m_index >= num_layers || m_layers[m_index].first >= m_layers[m_index].second)
throw std::logic_error("partial_queue::pop() bad state.");
return m_data[m_index * m_maxLayerSize + m_layers[m_index].first++];
}
}
#endif
-425
View File
@@ -1,425 +0,0 @@
// rak - Rakshasa's toolbox
// Copyright (C) 2005-2007, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#ifndef RAK_STRING_MANIP_H
#define RAK_STRING_MANIP_H
#include <algorithm>
#include <cctype>
#include <climits>
#include <cstdlib>
#include <functional>
#include <iterator>
#include <locale>
#include <random>
namespace rak {
// Use these trim functions until n1872 is widely supported.
template <typename Sequence>
Sequence trim_begin(const Sequence& seq) {
if (seq.empty() || !std::isspace(*seq.begin()))
return seq;
typename Sequence::size_type pos = 0;
while (pos != seq.length() && std::isspace(seq[pos]))
pos++;
return seq.substr(pos, seq.length() - pos);
}
template <typename Sequence>
Sequence trim_end(const Sequence& seq) {
if (seq.empty() || !std::isspace(*(--seq.end())))
return seq;
typename Sequence::size_type pos = seq.size();
while (pos != 0 && std::isspace(seq[pos - 1]))
pos--;
return seq.substr(0, pos);
}
template <typename Sequence>
Sequence trim(const Sequence& seq) {
return trim_begin(trim_end(seq));
}
template <typename Sequence>
Sequence trim_begin_classic(const Sequence& seq) {
if (seq.empty() || !std::isspace(*seq.begin(), std::locale::classic()))
return seq;
typename Sequence::size_type pos = 0;
while (pos != seq.length() && std::isspace(seq[pos], std::locale::classic()))
pos++;
return seq.substr(pos, seq.length() - pos);
}
template <typename Sequence>
Sequence trim_end_classic(const Sequence& seq) {
if (seq.empty() || !std::isspace(*(--seq.end()), std::locale::classic()))
return seq;
typename Sequence::size_type pos = seq.size();
while (pos != 0 && std::isspace(seq[pos - 1], std::locale::classic()))
pos--;
return seq.substr(0, pos);
}
template <typename Sequence>
Sequence trim_classic(const Sequence& seq) {
return trim_begin_classic(trim_end_classic(seq));
}
// Consider rewritting such that m_seq is replaced by first/last.
template <typename Sequence>
class split_iterator_t {
public:
typedef typename Sequence::const_iterator const_iterator;
typedef typename Sequence::value_type value_type;
split_iterator_t() {}
split_iterator_t(const Sequence& seq, value_type delim) :
m_seq(&seq),
m_delim(delim),
m_pos(seq.begin()),
m_next(std::find(seq.begin(), seq.end(), delim)) {
}
Sequence operator * () { return Sequence(m_pos, m_next); }
split_iterator_t& operator ++ () {
m_pos = m_next;
if (m_pos == m_seq->end())
return *this;
m_pos++;
m_next = std::find(m_pos, m_seq->end(), m_delim);
return *this;
}
bool operator == (const split_iterator_t&) const { return m_pos == m_seq->end(); }
bool operator != (const split_iterator_t&) const { return m_pos != m_seq->end(); }
private:
const Sequence* m_seq;
value_type m_delim;
const_iterator m_pos;
const_iterator m_next;
};
template <typename Sequence>
inline split_iterator_t<Sequence>
split_iterator(const Sequence& seq, typename Sequence::value_type delim) {
return split_iterator_t<Sequence>(seq, delim);
}
template <typename Sequence>
inline split_iterator_t<Sequence>
split_iterator(const Sequence&) {
return split_iterator_t<Sequence>();
}
// Could optimize this abit.
inline char
hexchar_to_value(char c) {
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'A' && c <= 'F')
return 10 + c - 'A';
else
return 10 + c - 'a';
}
template <int pos, typename Value>
inline char
value_to_hexchar(Value v) {
v >>= pos * 4;
v &= 0xf;
if (v < 0xA)
return '0' + v;
else
return 'A' + v - 0xA;
}
template <typename InputIterator, typename OutputIterator>
OutputIterator
copy_escape_html(InputIterator first, InputIterator last, OutputIterator dest) {
while (first != last) {
if (std::isalpha(*first, std::locale::classic()) ||
std::isdigit(*first, std::locale::classic()) ||
*first == '-') {
*(dest++) = *first;
} else {
*(dest++) = '%';
*(dest++) = value_to_hexchar<1>(*first);
*(dest++) = value_to_hexchar<0>(*first);
}
++first;
}
return dest;
}
template <typename InputIterator, typename OutputIterator>
OutputIterator
copy_escape_html(InputIterator first1, InputIterator last1, OutputIterator first2, OutputIterator last2) {
while (first1 != last1) {
if (std::isalpha(*first1, std::locale::classic()) ||
std::isdigit(*first1, std::locale::classic()) ||
*first1 == '-') {
if (first2 == last2) break; else *(first2++) = *first1;
} else {
if (first2 == last2) break; else *(first2++) = '%';
if (first2 == last2) break; else *(first2++) = value_to_hexchar<1>(*first1);
if (first2 == last2) break; else *(first2++) = value_to_hexchar<0>(*first1);
}
++first1;
}
return first2;
}
template <typename Iterator>
inline std::string
copy_escape_html(Iterator first, Iterator last) {
std::string dest;
copy_escape_html(first, last, std::back_inserter(dest));
return dest;
}
template <typename Sequence>
inline Sequence
copy_escape_html(const Sequence& src) {
Sequence dest;
copy_escape_html(src.begin(), src.end(), std::back_inserter(dest));
return dest;
}
template <typename Sequence>
inline std::string
copy_escape_html_str(const Sequence& src) {
std::string dest;
copy_escape_html(src.begin(), src.end(), std::back_inserter(dest));
return dest;
}
// Consider support for larger than char type.
template <typename InputIterator, typename OutputIterator>
OutputIterator
transform_hex(InputIterator first, InputIterator last, OutputIterator dest) {
while (first != last) {
*(dest++) = value_to_hexchar<1>(*first);
*(dest++) = value_to_hexchar<0>(*first);
++first;
}
return dest;
}
template <typename InputIterator, typename OutputIterator>
OutputIterator
transform_hex(InputIterator first1, InputIterator last1, OutputIterator first2, OutputIterator last2) {
while (first1 != last1) {
if (first2 == last2) break; else *(first2++) = value_to_hexchar<1>(*first1);
if (first2 == last2) break; else *(first2++) = value_to_hexchar<0>(*first1);
++first1;
}
return first2;
}
template <typename Sequence>
inline Sequence
transform_hex(const Sequence& src) {
Sequence dest;
transform_hex(src.begin(), src.end(), std::back_inserter(dest));
return dest;
}
template <typename Iterator>
inline std::string
transform_hex(Iterator first, Iterator last) {
std::string dest;
transform_hex(first, last, std::back_inserter(dest));
return dest;
}
template <typename Sequence>
inline std::string
transform_hex_str(const Sequence& seq) {
std::string dest;
transform_hex(seq.begin(), seq.end(), std::back_inserter(dest));
return dest;
}
template <typename Sequence>
Sequence
generate_random(size_t length) {
std::random_device rd;
std::mt19937 mt(rd());
using bytes_randomizer = std::independent_bits_engine<std::mt19937, CHAR_BIT, uint8_t>;
bytes_randomizer bytes(mt);
Sequence s;
s.reserve(length);
std::generate_n(std::back_inserter(s), length, std::ref(bytes));
return s;
}
template <typename Iterator>
inline bool
is_all_alpha(Iterator first, Iterator last) {
while (first != last)
if (!std::isalpha(*first++, std::locale::classic()))
return false;
return true;
}
template <typename Sequence>
inline bool
is_all_alpha(const Sequence& src) {
return is_all_alpha(src.begin(), src.end());
}
template <typename Iterator>
inline bool
is_all_alnum(Iterator first, Iterator last) {
while (first != last)
if (!std::isalnum(*first++, std::locale::classic()))
return false;
return true;
}
template <typename Sequence>
inline bool
is_all_alnum(const Sequence& src) {
return is_all_alnum(src.begin(), src.end());
}
template <typename Iterator>
inline bool
is_all_name(Iterator first, Iterator last) {
while (first != last) {
if (!std::isalnum(*first, std::locale::classic()) && *first != '_')
return false;
first++;
}
return true;
}
template <typename Sequence>
inline bool
is_all_name(const Sequence& src) {
return is_all_name(src.begin(), src.end());
}
template <typename Iterator>
std::string
sanitize(Iterator first, Iterator last) {
std::string dest;
for (; first != last; ++first) {
if (std::isprint(*first) && *first != '\r' && *first != '\n' && *first != '\t')
dest += *first;
else
dest += " ";
}
return dest;
}
template <typename Sequence>
std::string
sanitize(const Sequence& src) {
return trim(sanitize(src.begin(), src.end()));
}
template <typename Iterator>
std::string striptags(Iterator first, Iterator last) {
bool copychar = true;
std::string dest;
for (; first != last; ++first) {
if (std::isprint(*first) && *first == '<') {
copychar = false;
} else if (std::isprint(*first) && *first == '>') {
copychar = true;
continue;
}
if (copychar)
dest += *first;
}
return dest;
}
template <typename Sequence>
std::string striptags(const Sequence& src) {
return striptags(src.begin(), src.end());
}
}
#endif
-107
View File
@@ -1,107 +0,0 @@
// rak - Rakshasa's toolbox
// Copyright (C) 2005-2007, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#ifndef RAK_UNORDERED_VECTOR_H
#define RAK_UNORDERED_VECTOR_H
#include <vector>
namespace rak {
template <typename _Tp>
class unordered_vector : private std::vector<_Tp> {
public:
typedef std::vector<_Tp> Base;
typedef typename Base::value_type value_type;
typedef typename Base::pointer pointer;
typedef typename Base::const_pointer const_pointer;
typedef typename Base::reference reference;
typedef typename Base::const_reference const_reference;
typedef typename Base::size_type size_type;
typedef typename Base::difference_type difference_type;
typedef typename Base::allocator_type allocator_type;
typedef typename Base::iterator iterator;
typedef typename Base::reverse_iterator reverse_iterator;
typedef typename Base::const_iterator const_iterator;
typedef typename Base::const_reverse_iterator const_reverse_iterator;
using Base::clear;
using Base::empty;
using Base::size;
using Base::reserve;
using Base::front;
using Base::back;
using Base::begin;
using Base::end;
using Base::rbegin;
using Base::rend;
using Base::push_back;
using Base::pop_back;
// Use the range erase function, the single element erase gets
// overloaded.
using Base::erase;
iterator insert(iterator position, const value_type& x);
iterator erase(iterator position);
private:
};
template <typename _Tp>
typename unordered_vector<_Tp>::iterator
unordered_vector<_Tp>::insert(iterator position, const value_type& x) {
Base::push_back(x);
return --end();
}
template <typename _Tp>
typename unordered_vector<_Tp>::iterator
unordered_vector<_Tp>::erase(iterator position) {
// We don't need to check if position == end - 1 since we then copy
// to the position we pop later.
*position = Base::back();
Base::pop_back();
return position;
}
}
#endif
+124 -16
View File
@@ -10,8 +10,8 @@
#
# Check for baseline language coverage in the compiler for the specified
# version of the C++ standard. If necessary, add switches to CXX and
# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
# or '14' (for the C++14 standard).
# CXXCPP to enable support. VERSION may be '11', '14', '17', '20', or
# '23' for the respective C++ standard version.
#
# The second argument, if specified, indicates whether you insist on an
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
@@ -36,13 +36,15 @@
# Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
# Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.com>
# Copyright (c) 2020 Jason Merrill <jason@redhat.com>
# Copyright (c) 2021, 2024 Jörn Heusipp <osmanx@problemloesungsmaschine.de>
# Copyright (c) 2015, 2022, 2023, 2024 Olly Betts
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 12
#serial 25
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
dnl (serial version number 13).
@@ -51,6 +53,8 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
[$1], [14], [ax_cxx_compile_alternatives="14 1y"],
[$1], [17], [ax_cxx_compile_alternatives="17 1z"],
[$1], [20], [ax_cxx_compile_alternatives="20"],
[$1], [23], [ax_cxx_compile_alternatives="23"],
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
m4_if([$2], [], [],
[$2], [ext], [],
@@ -102,9 +106,18 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
dnl HP's aCC needs +std=c++11 according to:
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
dnl Cray's crayCC needs "-h std=c++11"
dnl MSVC needs -std:c++NN for C++17 and later (default is C++14)
for alternative in ${ax_cxx_compile_alternatives}; do
for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}" MSVC; do
if test x"$switch" = xMSVC; then
dnl AS_TR_SH maps both `:` and `=` to `_` so -std:c++17 would collide
dnl with -std=c++17. We suffix the cache variable name with _MSVC to
dnl avoid this.
switch=-std:c++${alternative}
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_${switch}_MSVC])
else
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
fi
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
$cachevar,
[ac_save_CXX="$CXX"
@@ -148,23 +161,44 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
dnl Test body for checking C++11 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
[_AX_CXX_COMPILE_STDCXX_testbody_new_in_11]
)
dnl Test body for checking C++14 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
[_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14]
)
dnl Test body for checking C++17 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
[_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17]
)
dnl Test body for checking C++20 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_20],
[_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
_AX_CXX_COMPILE_STDCXX_testbody_new_in_20]
)
dnl Test body for checking C++23 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_23],
[_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
_AX_CXX_COMPILE_STDCXX_testbody_new_in_20
_AX_CXX_COMPILE_STDCXX_testbody_new_in_23]
)
dnl Tests for new features in C++11
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
@@ -176,7 +210,21 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
#error "This is not a C++ compiler"
#elif __cplusplus < 201103L
// MSVC always sets __cplusplus to 199711L in older versions; newer versions
// only set it correctly if /Zc:__cplusplus is specified as well as a
// /std:c++NN switch:
//
// https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/
//
// The value __cplusplus ought to have is available in _MSVC_LANG since
// Visual Studio 2015 Update 3:
//
// https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros
//
// This was also the first MSVC version to support C++14 so we can't use the
// value of either __cplusplus or _MSVC_LANG to quickly rule out MSVC having
// C++11 or C++14 support, but we can check _MSVC_LANG for C++17 and later.
#elif __cplusplus < 201103L && !defined _MSC_VER
#error "This is not a C++11 compiler"
@@ -467,7 +515,7 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
#error "This is not a C++ compiler"
#elif __cplusplus < 201402L
#elif __cplusplus < 201402L && !defined _MSC_VER
#error "This is not a C++14 compiler"
@@ -591,7 +639,7 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
#error "This is not a C++ compiler"
#elif __cplusplus < 201703L
#elif (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 201703L
#error "This is not a C++17 compiler"
@@ -957,6 +1005,66 @@ namespace cxx17
} // namespace cxx17
#endif // __cplusplus < 201703L
#endif // (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 201703L
]])
dnl Tests for new features in C++20
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_20], [[
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 202002L
#error "This is not a C++20 compiler"
#else
#include <version>
namespace cxx20
{
// As C++20 supports feature test macros in the standard, there is no
// immediate need to actually test for feature availability on the
// Autoconf side.
} // namespace cxx20
#endif // (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 202002L
]])
dnl Tests for new features in C++23
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_23], [[
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 202302L
#error "This is not a C++23 compiler"
#else
#include <version>
namespace cxx23
{
// As C++23 supports feature test macros in the standard, there is no
// immediate need to actually test for feature availability on the
// Autoconf side.
} // namespace cxx23
#endif // (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 202302L
]])
+83 -34
View File
@@ -19,7 +19,7 @@
# header is checked to match the Lua interpreter version exactly. When
# searching for Lua libraries, the version number is used as a suffix.
# This is done with the goal of supporting multiple Lua installs (5.1,
# 5.2, and 5.3 side-by-side).
# 5.2, 5.3, and 5.4 side-by-side).
#
# A note on compatibility with previous versions: This file has been
# mostly rewritten for serial 18. Most developers should be able to use
@@ -49,6 +49,14 @@
# interpreter. If LUA is blank, the user's path is searched for an
# suitable interpreter.
#
# Optionally a LUAJIT option may be set ahead of time to look for and
# validate a LuaJIT install instead of PUC Lua. Usage might look like:
#
# AC_ARG_WITH(luajit, [AS_HELP_STRING([--with-luajit],
# [Prefer LuaJIT over PUC Lua, even if the latter is newer. Default: no])
# ])
# AM_CONDITIONAL([LUAJIT], [test "x$with_luajit" != 'xno'])
#
# If MINIMUM-VERSION is supplied, then only Lua interpreters with a
# version number greater or equal to MINIMUM-VERSION will be accepted. If
# TOO-BIG-VERSION is also supplied, then only Lua interpreters with a
@@ -82,7 +90,7 @@
# appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES.
#
# If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is
# performed, otherwise ACTION-IF-NOT-FOUND is preformed. If ACTION-IF-NOT-
# performed, otherwise ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-
# FOUND is blank, then it will default to printing an error. To prevent
# the default behavior, give ':' as an action.
#
@@ -152,6 +160,7 @@
#
# LICENSE
#
# Copyright (c) 2023 Caleb Maclennan <caleb@alerque.com>
# Copyright (c) 2015 Reuben Thomas <rrt@sc3d.org>
# Copyright (c) 2014 Tim Perkins <tprk77@gmail.com>
#
@@ -181,7 +190,7 @@
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 42
#serial 47
dnl =========================================================================
dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION],
@@ -197,13 +206,14 @@ AC_DEFUN([AX_PROG_LUA],
AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1])
dnl Find a Lua interpreter.
m4_define_default([_AX_LUA_INTERPRETER_LIST],
[lua lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua50])
AM_COND_IF([LUAJIT],
[_ax_lua_interpreter_list='luajit luajit-2.1.0-beta3 luajit-2.0.5 luajit-2.0.4 luajit-2.0.3'],
[_ax_lua_interpreter_list='lua lua5.4 lua54 lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua5.0 lua50'])
m4_if([$1], [],
[ dnl No version check is needed. Find any Lua interpreter.
AS_IF([test "x$LUA" = 'x'],
[AC_PATH_PROGS([LUA], [_AX_LUA_INTERPRETER_LIST], [:])])
[AC_PATH_PROGS([LUA], [$_ax_lua_interpreter_list], [:])])
ax_display_LUA='lua'
AS_IF([test "x$LUA" != 'x:'],
@@ -242,7 +252,7 @@ AC_DEFUN([AX_PROG_LUA],
[_ax_check_text="for a Lua interpreter with version >= $1, < $2"])
AC_CACHE_CHECK([$_ax_check_text],
[ax_cv_pathless_LUA],
[ for ax_cv_pathless_LUA in _AX_LUA_INTERPRETER_LIST none; do
[ for ax_cv_pathless_LUA in $_ax_lua_interpreter_list none; do
test "x$ax_cv_pathless_LUA" = 'xnone' && break
_AX_LUA_CHK_IS_INTRP([$ax_cv_pathless_LUA], [], [continue])
_AX_LUA_CHK_VER([$ax_cv_pathless_LUA], [$1], [$2], [break])
@@ -268,13 +278,25 @@ AC_DEFUN([AX_PROG_LUA],
ax_cv_lua_version=[`$LUA -e '
-- return a version number in X.Y format
local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)")
print(ver)'`]
print(ver or "")'`]
])
AS_IF([test "x$ax_cv_lua_version" = 'x'],
[AC_MSG_ERROR([invalid Lua version number])])
AC_SUBST([LUA_VERSION], [$ax_cv_lua_version])
AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | $SED 's|\.||'`])
AM_COND_IF([LUAJIT], [
AC_CACHE_CHECK([for $ax_display_LUA jit version], [ax_cv_luajit_version],
[ ax_cv_luajit_version=[`$LUA -e '
local _, _, ver = string.find(jit and jit.version, "(%d+%..+)")
print(ver or "")'`]
])
AS_IF([test "x$ax_cv_luajit_version" = 'x'],
[AC_MSG_ERROR([invalid Lua jit version number])])
AC_SUBST([LUAJIT_VERSION], [$ax_cv_luajit_version])
AC_SUBST([LUAJIT_SHORT_VERSION], [$(echo "$LUAJIT_VERSION" | $SED 's|\.|§|;s|\..*||;s|§|.|')])
])
dnl The following check is not supported:
dnl At times (like when building shared libraries) you may want to know
dnl which OS platform Lua thinks this is.
@@ -464,33 +486,50 @@ AC_DEFUN([AX_LUA_HEADERS],
AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION])
])
AM_COND_IF([LUAJIT],[
dnl Check for LUAJIT_VERSION.
AC_MSG_CHECKING([if LUAJIT_VERSION is defined])
AS_IF([test "x$LUAJIT_VERSION" != 'x'],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([cannot check Lua jit headers without knowing LUAJIT_VERSION])
])
])
dnl Make LUA_INCLUDE a precious variable.
AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1])
dnl Some default directories to search.
LUA_SHORT_VERSION=`echo "$LUA_VERSION" | $SED 's|\.||'`
m4_define_default([_AX_LUA_INCLUDE_LIST],
[ /usr/include/lua$LUA_VERSION \
/usr/include/lua-$LUA_VERSION \
/usr/include/lua/$LUA_VERSION \
/usr/include/lua$LUA_SHORT_VERSION \
/usr/local/include/lua$LUA_VERSION \
/usr/local/include/lua-$LUA_VERSION \
/usr/local/include/lua/$LUA_VERSION \
/usr/local/include/lua$LUA_SHORT_VERSION \
])
dnl Some default directories to search.
AM_COND_IF([LUAJIT],
[_ax_lua_include_list="
/usr/include/luajit-$LUAJIT_VERSION
/usr/include/luajit-$LUAJIT_SHORT_VERSION
/usr/local/include/luajit-$LUAJIT_VERSION
/usr/local/include/luajit-$LUAJIT_SHORT_VERSION"],
[_ax_lua_include_list="
/usr/include/lua$LUA_VERSION
/usr/include/lua-$LUA_VERSION
/usr/include/lua/$LUA_VERSION
/usr/include/lua$LUA_SHORT_VERSION
/usr/local/include/lua$LUA_VERSION
/usr/local/include/lua-$LUA_VERSION
/usr/local/include/lua/$LUA_VERSION
/usr/local/include/lua$LUA_SHORT_VERSION"])
dnl Try to find the headers.
_ax_lua_saved_cppflags=$CPPFLAGS
CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
AM_COND_IF([LUAJIT], [AC_CHECK_HEADERS([luajit.h])])
CPPFLAGS=$_ax_lua_saved_cppflags
dnl Try some other directories if LUA_INCLUDE was not set.
AS_IF([test "x$LUA_INCLUDE" = 'x' &&
test "x$ac_cv_header_lua_h" != 'xyes'],
test "x$ac_cv_header_lua_h" != 'xyes' ||
test "x$with_luajit" != 'xno' &&
test "x$ac_cv_header_luajit_h" != 'xyes'],
[ dnl Try some common include paths.
for _ax_include_path in _AX_LUA_INCLUDE_LIST; do
for _ax_include_path in $_ax_lua_include_list; do
test ! -d "$_ax_include_path" && continue
AC_MSG_CHECKING([for Lua headers in])
@@ -500,10 +539,12 @@ AC_DEFUN([AX_LUA_HEADERS],
AS_UNSET([ac_cv_header_lualib_h])
AS_UNSET([ac_cv_header_lauxlib_h])
AS_UNSET([ac_cv_header_luaconf_h])
AS_UNSET([ac_cv_header_luajit_h])
_ax_lua_saved_cppflags=$CPPFLAGS
CPPFLAGS="$CPPFLAGS -I$_ax_include_path"
AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
AM_COND_IF([LUAJIT], [AC_CHECK_HEADERS([luajit.h])])
CPPFLAGS=$_ax_lua_saved_cppflags
AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'],
@@ -621,18 +662,26 @@ AC_DEFUN([AX_LUA_LIBS],
dnl Try to find the Lua libs.
_ax_lua_saved_libs=$LIBS
LIBS="$LIBS $LUA_LIB"
AC_SEARCH_LIBS([lua_load],
[ lua$LUA_VERSION \
lua$LUA_SHORT_VERSION \
lua-$LUA_VERSION \
lua-$LUA_SHORT_VERSION \
:liblua-$LUA_VERSION.so.0 \
:liblua-$LUA_SHORT_VERSION.so.0 \
lua \
],
[_ax_found_lua_libs='yes'],
[_ax_found_lua_libs='no'],
[$_ax_lua_extra_libs])
AM_COND_IF([LUAJIT],
[AC_SEARCH_LIBS([lua_load],
[ luajit$LUA_VERSION \
luajit$LUA_SHORT_VERSION \
luajit-$LUA_VERSION \
luajit-$LUA_SHORT_VERSION \
luajit],
[_ax_found_lua_libs='yes'],
[_ax_found_lua_libs='no'],
[$_ax_lua_extra_libs])],
[AC_SEARCH_LIBS([lua_load],
[ lua$LUA_VERSION \
lua$LUA_SHORT_VERSION \
lua-$LUA_VERSION \
lua-$LUA_SHORT_VERSION \
lua \
],
[_ax_found_lua_libs='yes'],
[_ax_found_lua_libs='no'],
[$_ax_lua_extra_libs])])
LIBS=$_ax_lua_saved_libs
AS_IF([test "x$ac_cv_search_lua_load" != 'xno' &&
+158 -10
View File
@@ -54,6 +54,8 @@ AC_DEFUN([TORRENT_CHECK_EPOLL], [
}
])],
[
use_epoll=yes
AC_DEFINE(USE_EPOLL, 1, Use epoll.)
AC_MSG_RESULT(yes)
], [
@@ -86,6 +88,8 @@ AC_DEFUN([TORRENT_CHECK_KQUEUE], [
}
])],
[
use_kqueue=yes
AC_DEFINE(USE_KQUEUE, 1, Use kqueue.)
AC_MSG_RESULT(yes)
], [
@@ -125,7 +129,7 @@ AC_DEFUN([TORRENT_CHECK_FALLOCATE], [
#include <fcntl.h>
]], [[ fallocate(0, FALLOC_FL_KEEP_SIZE, 0, 0); return 0;
]])],[
AC_DEFINE(HAVE_FALLOCATE, 1, Linux's fallocate supported.)
AC_DEFINE(USE_FALLOCATE, 1, Linux's fallocate supported.)
AC_MSG_RESULT(yes)
],[
AC_MSG_RESULT(no)
@@ -157,6 +161,116 @@ AC_DEFUN([TORRENT_WITH_POSIX_FALLOCATE], [
])
])
AC_DEFUN([TORRENT_CHECK_STATVFS], [
AC_CHECK_HEADERS(sys/vfs.h sys/statvfs.h sys/statfs.h)
AC_MSG_CHECKING(for statvfs)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#if HAVE_SYS_VFS_H
#include <sys/vfs.h>
#endif
#if HAVE_SYS_STATVFS_H
#include <sys/statvfs.h>
#endif
#if HAVE_SYS_STATFS_H
#include <sys/statfs.h>
#endif
]], [[
struct statvfs s; fsblkcnt_t c;
statvfs("", &s);
fstatvfs(0, &s);
]])],[
AC_DEFINE(FS_STAT_FD, [fstatvfs(fd, &m_stat) == 0], Function to determine filesystem stats from fd)
AC_DEFINE(FS_STAT_FN, [statvfs(fn, &m_stat) == 0], Function to determine filesystem stats from filename)
AC_DEFINE(FS_STAT_STRUCT, [struct statvfs], Type of second argument to statfs function)
AC_DEFINE(FS_STAT_SIZE_TYPE, [unsigned long], Type of block size member in stat struct)
AC_DEFINE(FS_STAT_COUNT_TYPE, [fsblkcnt_t], Type of block count member in stat struct)
AC_DEFINE(FS_STAT_BLOCK_SIZE, [(m_stat.f_frsize)], Determine the block size)
AC_MSG_RESULT(ok)
have_stat_vfs=yes
],[
AC_MSG_RESULT(no)
have_stat_vfs=no
])
])
AC_DEFUN([TORRENT_CHECK_STATFS], [
AC_CHECK_HEADERS(sys/statfs.h)
AC_MSG_CHECKING(for statfs)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#if HAVE_SYS_STATFS_H
#include <sys/statfs.h>
#endif
]], [[
struct statfs s;
statfs("", &s);
fstatfs(0, &s);
]])],[
AC_DEFINE(FS_STAT_FD, [fstatfs(fd, &m_stat) == 0], Function to determine filesystem stats from fd)
AC_DEFINE(FS_STAT_FN, [statfs(fn, &m_stat) == 0], Function to determine filesystem stats from filename)
AC_DEFINE(FS_STAT_STRUCT, [struct statfs], Type of second argument to statfs function)
AC_DEFINE(FS_STAT_SIZE_TYPE, [long], Type of block size member in stat struct)
AC_DEFINE(FS_STAT_COUNT_TYPE, [long], Type of block count member in stat struct)
AC_DEFINE(FS_STAT_BLOCK_SIZE, [(m_stat.f_bsize)], Determine the block size)
AC_MSG_RESULT(ok)
have_stat_vfs=yes
],[
AC_MSG_RESULT(no)
have_stat_vfs=no
])
])
AC_DEFUN([TORRENT_DISABLED_STATFS], [
AC_DEFINE(FS_STAT_FD, [(errno = ENOSYS) == 0], Function to determine filesystem stats from fd)
AC_DEFINE(FS_STAT_FN, [(errno = ENOSYS) == 0], Function to determine filesystem stats from filename)
AC_DEFINE(FS_STAT_STRUCT, [struct {blocksize_type f_bsize; blockcount_type f_bavail;}], Type of second argument to statfs function)
AC_DEFINE(FS_STAT_SIZE_TYPE, [int], Type of block size member in stat struct)
AC_DEFINE(FS_STAT_COUNT_TYPE, [int], Type of block count member in stat struct)
AC_DEFINE(FS_STAT_BLOCK_SIZE, [(4096)], Determine the block size)
AC_MSG_RESULT(No filesystem stats available)
])
AC_DEFUN([TORRENT_WITHOUT_STATVFS], [
AC_ARG_WITH(statvfs,
AS_HELP_STRING([--without-statvfs],[don't try to use statvfs to find free diskspace]),
[
if test "$withval" = "yes"; then
TORRENT_CHECK_STATVFS
else
have_stat_vfs=no
fi
],
[
TORRENT_CHECK_STATVFS
])
])
AC_DEFUN([TORRENT_WITHOUT_STATFS], [
AC_ARG_WITH(statfs,
AS_HELP_STRING([--without-statfs],[don't try to use statfs to find free diskspace]),
[
if test "$have_stat_vfs" = "no"; then
if test "$withval" = "yes"; then
TORRENT_CHECK_STATFS
else
TORRENT_DISABLED_STATFS
fi
fi
],
[
if test "$have_stat_vfs" = "no"; then
TORRENT_CHECK_STATFS
if test "$have_stat_vfs" = "no"; then
TORRENT_DISABLED_STATFS
fi
fi
])
])
AC_DEFUN([TORRENT_WITH_ADDRESS_SPACE], [
AC_ARG_WITH(address-space,
AS_HELP_STRING([--with-address-space=MB],[change the default address space size [[default=1024mb]]]),
@@ -189,7 +303,7 @@ AC_DEFUN([TORRENT_WITH_FASTCGI], [
elif test "$withval" = "yes"; then
CXXFLAGS="$CXXFLAGS"
LIBS="$LIBS -lfcgi"
LIBS="$LIBS -lfcgi"
AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <fcgiapp.h>
]], [[ FCGX_Init(); ]])],[
@@ -281,29 +395,45 @@ AC_DEFUN([TORRENT_WITH_LUA], [
AC_MSG_RESULT(no)
else
AX_PROG_LUA
AX_LUA_LIBS
AX_LUA_HEADERS
AC_DEFINE(HAVE_LUA, 1, Use LUA.)
AC_DEFINE(LUA_DATADIR, [PACKAGE_DATADIR "/lua"], [LUA data directory])
LIBS="$LIBS $LUA_LIB"
CXXFLAGS="$CXXFLAGS $LUA_INCLUDE"
# 1. Override AX_LUA_LIBS default crash behavior
AX_LUA_LIBS([have_lua_libs=yes], [have_lua_libs=no])
# 2. Override AX_LUA_HEADERS default crash behavior
AX_LUA_HEADERS([have_lua_headers=yes], [have_lua_headers=no])
# 3. Only inject if both checks completely pass
if test "x$have_lua_libs" = "xyes" && test "x$have_lua_headers" = "xyes"; then
AC_DEFINE(HAVE_LUA, 1, Use LUA.)
AC_DEFINE(LUA_DATADIR, [PACKAGE_DATADIR "/lua"], [LUA data directory])
LIBS="$LIBS $LUA_LIB"
CXXFLAGS="$CXXFLAGS $LUA_INCLUDE"
else
# Throw fatal error ONLY if user strictly ran --with-lua=yes
if test "$withval" = "yes"; then
AC_MSG_ERROR([Lua support explicitly requested, but compatible Lua 5.3 libs/headers were not found.])
else
AC_MSG_WARN([Lua 5.3 libs or headers missing. Proceeding without Lua support.])
fi
fi
fi
],[
AC_MSG_RESULT(ignored)
])
])
AC_DEFUN([TORRENT_WITH_INOTIFY], [
AC_LANG_PUSH(C++)
AC_CHECK_HEADERS([sys/inotify.h mcheck.h])
AC_CHECK_HEADERS([sys/inotify.h])
AC_MSG_CHECKING([whether sys/inotify.h actually works])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <sys/inotify.h>
int main(int,const char**) { return (-1 == inotify_init()); }])
],[
AC_DEFINE(HAVE_INOTIFY, 1, [sys/inotify.h exists and works correctly])
AC_DEFINE(USE_INOTIFY, 1, [sys/inotify.h exists and works correctly])
AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(failed)]
)
@@ -317,6 +447,7 @@ AC_DEFUN([TORRENT_CHECK_PTHREAD_SETNAME_NP], [
AC_MSG_CHECKING(for pthread_setname_np type)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#define _GNU_SOURCE
#include <pthread.h>
#include <sys/types.h>
]], [[
@@ -375,3 +506,20 @@ AC_DEFUN([TORRENT_WITH_SYSTEMD], [
fi
])
])
AC_DEFUN([TORRENT_WITHOUT_NCURSES], [
AC_ARG_WITH([ncurses],
[AS_HELP_STRING([--without-ncurses], [build without ncurses (daemon-only mode)])],
[with_ncurses=$withval],
[with_ncurses=yes])
if test "x$with_ncurses" = xno; then
AC_DEFINE([HAVE_NO_NCURSES], [1], [Define to 1 if building without ncurses])
CURSES_LIBS=""
CURSES_CFLAGS=""
CURSES_LIB=""
fi
AM_CONDITIONAL([NO_NCURSES], [test "x$with_ncurses" = xno])
])
+64 -15
View File
@@ -159,23 +159,72 @@ AC_DEFUN([TORRENT_CHECK_POPCOUNT], [
])
AC_DEFUN([TORRENT_CHECK_CACHELINE], [
AC_MSG_CHECKING(for cacheline)
AC_REQUIRE([AC_CANONICAL_HOST])
AC_MSG_CHECKING([for target cacheline size])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <stdlib.h>
#include <linux/cache.h>
void* vptr __cacheline_aligned;
void f() { posix_memalign(&vptr, SMP_CACHE_BYTES, 42); }
])],
[
AC_MSG_RESULT(found builtin)
dnl Need to fix this so that it uses the stuff defined by the system.
case "$host_os" in
linux*)
# REGION: Linux Kernel Extraction Loop
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
#include <stdlib.h>
#include <linux/cache.h>
void* vptr;
void f() {
int res = posix_memalign(&vptr, SMP_CACHE_BYTES, 42);
(void)res;
}
]])],[
# We need an explicit variable fallback condition inside AC_COMPUTE_INT
AC_COMPUTE_INT([torrent_cv_cacheline_size], [SMP_CACHE_BYTES], [#include <linux/cache.h>], [torrent_cv_cacheline_size=0])
AC_DEFINE(LT_SMP_CACHE_BYTES, 128, Largest L1 cache size we know of should work on all archs.)
], [
AC_MSG_RESULT(using default 128 bytes)
AC_DEFINE(LT_SMP_CACHE_BYTES, 128, Largest L1 cache size we know of should work on all archs.)
])
if test "$torrent_cv_cacheline_size" -gt 0; then
AC_MSG_RESULT([linux builtin ($torrent_cv_cacheline_size bytes)])
AC_DEFINE_UNQUOTED([LT_SMP_CACHE_BYTES], [$torrent_cv_cacheline_size], [System-defined Linux L1 SMP cacheline size.])
else
# Handle scenarios where macro maps to a complex runtime expression or fails
AC_MSG_RESULT([failed to parse SMP_CACHE_BYTES value])
AC_MSG_FAILURE([Linux kernel headers found, but cacheline constant could not be computed at compile-time.])
fi
],[
# Explicitly validate the CPU type even on Linux if the header check fails
case "$host_cpu" in
x86_64*|amd64*|i386*|i486*|i586*|i686*)
AC_MSG_RESULT([linux fallback x86 64 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Fallback 64-byte alignment for Linux x86 hardware.])
;;
arm*|aarch64*|powerpc*|ppc*|s390x*)
AC_MSG_RESULT([linux fallback enterprise 128 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 128, [Fallback 128-byte alignment for Linux enterprise hardware.])
;;
*)
AC_MSG_RESULT([unrecognized CPU arch on Linux header fallback])
AC_MSG_FAILURE([Unrecognized CPU architecture ($host_cpu) on Linux fallback path. Aborting build.])
;;
esac
])
;;
*)
# REGION: Cross-Platform Strict Hardware Mapping (macOS, FreeBSD, OpenBSD, NetBSD)
case "$host_cpu" in
x86_64*|amd64*|i386*|i486*|i586*|i686*)
# Explicit x86 desktop hardware baseline block
AC_MSG_RESULT([$host_os ($host_cpu) standard x86 64 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Standard 64-byte alignment for stable x86 hardware layout.])
;;
arm*|aarch64*|powerpc*|ppc*|s390x*)
# Explicit modern enterprise and Apple Silicon hardware baseline block
AC_MSG_RESULT([$host_os ($host_cpu) stable enterprise 128 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 128, [Optimized 128-byte alignment for newer high-performance chipsets.])
;;
*)
# STRICT ENFORCEMENT: Fail the build immediately if the CPU isn't explicitly known
AC_MSG_RESULT([unrecognized architecture])
AC_MSG_FAILURE([The target CPU architecture ($host_cpu) is unrecognized. Aborting configuration to prevent fatal runtime false-sharing or memory misalignment errors.])
;;
esac
;;
esac
])
AC_DEFUN([TORRENT_CHECK_ALIGNED], [
+10
View File
@@ -169,9 +169,13 @@ libsub_root_a_SOURCES = \
utils/file_status_cache.cc \
utils/file_status_cache.h \
utils/functional.h \
utils/gzip.cc \
utils/gzip.h \
utils/list_focus.h \
utils/lockfile.cc \
utils/lockfile.h \
utils/watch_ready_queue.cc \
utils/watch_ready_queue.h \
\
command_download.cc \
command_dynamic.cc \
@@ -195,8 +199,14 @@ libsub_root_a_SOURCES = \
globals.h \
option_parser.cc \
option_parser.h \
setup.cc \
setup.h \
signal_handler.cc \
signal_handler.h
if NO_NCURSES
libsub_root_a_SOURCES += display/curses_stub.cc display/curses_stub.h
endif
AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir) -DPACKAGE_DATADIR=\"$(pkgdatadir)\"
+95 -56
View File
@@ -5,7 +5,6 @@
#include <functional>
#include <netdb.h>
#include <unistd.h>
#include <rak/string_manip.h>
#include <rak/regex.h>
#include <torrent/rate.h>
#include <torrent/throttle.h>
@@ -19,9 +18,11 @@
#include <torrent/net/types.h>
#include <torrent/peer/connection_list.h>
#include <torrent/peer/peer_list.h>
#include <torrent/system/callbacks.h>
#include <torrent/utils/file_stat.h>
#include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
#include <torrent/utils/string_manip.h>
#include "core/download.h"
#include "core/manager.h"
@@ -32,29 +33,34 @@
#include "control.h"
#include "command_helpers.h"
std::string
torrent::string_utf8
retrieve_d_base_path(core::Download* download) {
if (download->file_list()->is_multi_file())
return download->file_list()->frozen_root_dir();
else
return download->file_list()->empty() ? std::string() : download->file_list()->at(0)->frozen_path();
if (download->file_list()->empty())
return {};
return download->file_list()->at(0)->frozen_path();
}
std::string
torrent::string_utf8
retrieve_d_base_filename(core::Download* download) {
const std::string* base;
torrent::string_utf8 base_path;
if (download->file_list()->is_multi_file())
base = &download->file_list()->frozen_root_dir();
base_path = download->file_list()->frozen_root_dir();
else if (!download->file_list()->empty())
base_path = download->file_list()->at(0)->frozen_path();
else
base = &download->file_list()->at(0)->frozen_path();
return {};
std::string::size_type split = base->rfind('/');
auto split = base_path.str().rfind('/');
if (split == std::string::npos)
return *base;
else
return base->substr(split + 1);
return base_path;
return torrent::string_utf8::from_string(base_path.str().substr(split + 1));
}
torrent::Object
@@ -84,7 +90,7 @@ apply_d_change_link(core::Download* download, const torrent::Object::list_type&
// } else if (type == "directory_path") {
// target = rpc::call_command_string("d.directory", rpc::make_target(download));
// link = rak::path_expand(prefix + rpc::call_command_string("d.base_path", rpc::make_target(download)) + postfix);
// link = path_expand(prefix + rpc::call_command_string("d.base_path", rpc::make_target(download)) + postfix);
} else if (type == "tied") {
link = expand_path(rpc::call_command_string("d.tied_to_file", rpc::make_target(download)));
@@ -142,9 +148,9 @@ apply_d_directory(core::Download* download, const std::string& name) {
if (!download->file_list()->is_multi_file())
download->set_root_directory(name);
else if (name.empty() || *name.rbegin() == '/')
download->set_root_directory(name + download->info()->name());
download->set_root_directory(name + download->info()->name().str());
else
download->set_root_directory(name + "/" + download->info()->name());
download->set_root_directory(name + "/" + download->info()->name().str());
}
torrent::Object
@@ -269,16 +275,6 @@ retrieve_d_custom_map(core::Download* download, bool keys_only, const torrent::O
return result;
}
torrent::Object
retrieve_d_bitfield(core::Download* download) {
const torrent::Bitfield* bitField = download->download()->file_list()->bitfield();
if (bitField->empty())
return torrent::Object("");
return torrent::Object(rak::transform_hex(bitField->begin(), bitField->end()));
}
void
apply_d_add_peer(core::Download* download, const std::string& arg) {
int port, ret;
@@ -303,8 +299,10 @@ apply_d_add_peer(core::Download* download, const std::string& arg) {
assert(std::this_thread::get_id() == torrent::main_thread::thread_id());
auto callback_id = torrent::system::make_callback_id();
// Currently discarding SOCK_STREAM.
torrent::this_thread::resolver()->resolve_preferred(NULL, host, AF_UNSPEC, AF_INET, [download, port](torrent::c_sa_shared_ptr sa, int err) {
torrent::this_thread::resolver()->resolve_preferred(callback_id, host, AF_UNSPEC, AF_INET, [download, port](torrent::c_sa_shared_ptr sa, int err) {
if (sa == nullptr) {
lt_log_print(torrent::LOG_TORRENT_WARN, "could not resolve hostname for added peer: %s", gai_strerror(err));
return;
@@ -326,7 +324,8 @@ d_chunks_seen(core::Download* download) {
std::string result;
result.resize(size * 2);
rak::transform_hex((const char*)seen, (const char*)seen + size, result.begin());
torrent::utils::transform_to_hex(seen, seen + size, result);
return result;
}
@@ -444,9 +443,11 @@ p_call_target(const torrent::Object::list_type& args) {
torrent::HashString hash;
if (peer_id.size() != 40 ||
torrent::hash_string_from_hex_c_str(peer_id.c_str(), hash) == peer_id.c_str())
throw torrent::input_error("Not a hash string.");
if (peer_id.size() != 40)
throw torrent::input_error("invalid argument: peer id target is not 40 bytes long");
if (torrent::utils::transform_from_hex(peer_id.c_str(), peer_id.c_str() + 40, hash) != hash.end())
throw torrent::input_error("invalid argument: peer id target is not a hex string");
torrent::ConnectionList::iterator peerItr = download->connection_list()->find(hash.c_str());
@@ -467,12 +468,14 @@ download_tracker_insert(core::Download* download, const torrent::Object::list_ty
if (args.size() != 2)
throw torrent::input_error("Wrong argument count.");
int64_t group;
int64_t group = 0;
if (args.front().is_string())
rpc::parse_whole_value_nothrow(args.front().as_string().c_str(), &group);
else
if (args.front().is_string()) {
if (!rpc::parse_whole_value_nothrow(args.front().as_string().c_str(), &group))
throw torrent::input_error("Invalid tracker group number.");
} else {
group = args.front().as_value();
}
if (group < 0 || group > 32)
throw torrent::input_error("Tracker group number invalid.");
@@ -641,30 +644,48 @@ void cg_d_group_set(core::Download* download, const torrent::Objec
void
initialize_command_download() {
CMD2_DL("d.hash", std::bind(&rak::transform_hex_str<torrent::HashString>, CMD2_ON_INFO(hash)));
CMD2_DL("d.local_id", std::bind(&rak::transform_hex_str<torrent::HashString>, CMD2_ON_INFO(local_id)));
CMD2_DL("d.local_id_html", std::bind(&rak::copy_escape_html_str<torrent::HashString>, CMD2_ON_INFO(local_id)));
CMD2_DL("d.bitfield", std::bind(&retrieve_d_bitfield, std::placeholders::_1));
CMD2_DL("d.base_path", std::bind(&retrieve_d_base_path, std::placeholders::_1));
CMD2_DL("d.base_filename", std::bind(&retrieve_d_base_filename, std::placeholders::_1));
CMD2_DL("d.hash", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(download->info()->hash()); });
CMD2_DL("d.local_id", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(download->info()->local_id()); });
CMD2_DL("d.local_id_html", [](auto* download, auto) { return torrent::utils::copy_escape_html_str(download->info()->local_id()); });
CMD2_DL("d.bitfield", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(*download->download()->file_list()->bitfield()); });
CMD2_DL("d.base_path", [](auto* download, auto) { return retrieve_d_base_path(download).str(); });
CMD2_DL("d.base_path.hex", [](auto* download, auto) { return retrieve_d_base_path(download).object_hex(); });
CMD2_DL("d.base_path.base64", [](auto* download, auto) { return retrieve_d_base_path(download).object_base64(); });
CMD2_DL("d.base_path.base64_as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_base64_as_binary(); });
CMD2_DL("d.base_path.as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_as_binary(); });
CMD2_DL("d.base_path.or_base64", [](auto* download, auto) { return retrieve_d_base_path(download).object_utf8_or_base64(); });
CMD2_DL("d.base_path.or_as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_utf8_or_as_binary(); });
CMD2_DL("d.base_filename", [](auto* download, auto) { return retrieve_d_base_filename(download).str(); });
CMD2_DL("d.base_filename.hex", [](auto* download, auto) { return retrieve_d_base_filename(download).object_hex(); });
CMD2_DL("d.base_filename.base64", [](auto* download, auto) { return retrieve_d_base_filename(download).object_base64(); });
CMD2_DL("d.base_filename.base64_as_binary", [](auto* download, auto) { return retrieve_d_base_filename(download).object_base64_as_binary(); });
CMD2_DL("d.base_filename.as_binary", [](auto* download, auto) { return retrieve_d_base_filename(download).object_as_binary(); });
CMD2_DL("d.base_filename.or_base64", [](auto* download, auto) { return retrieve_d_base_filename(download).object_utf8_or_base64(); });
CMD2_DL("d.base_filename.or_as_binary", [](auto* download, auto) { return retrieve_d_base_filename(download).object_utf8_or_as_binary(); });
CMD2_DL("d.name", CMD2_ON_INFO(name));
CMD2_DL("d.creation_date", CMD2_ON_INFO(creation_date));
CMD2_DL("d.load_date", CMD2_ON_INFO(load_date));
CMD2_DL("d.name", [](auto* download, auto) { return download->info()->name().str(); });
CMD2_DL("d.name.hex", [](auto* download, auto) { return download->info()->name().object_hex(); });
CMD2_DL("d.name.base64", [](auto* download, auto) { return download->info()->name().object_base64(); });
CMD2_DL("d.name.base64_as_binary", [](auto* download, auto) { return download->info()->name().object_base64_as_binary(); });
CMD2_DL("d.name.as_binary", [](auto* download, auto) { return download->info()->name().object_as_binary(); });
CMD2_DL("d.name.or_base64", [](auto* download, auto) { return download->info()->name().object_utf8_or_base64(); });
CMD2_DL("d.name.or_as_binary", [](auto* download, auto) { return download->info()->name().object_utf8_or_as_binary(); });
CMD2_DL("d.creation_date", [](auto* download, auto) { return download->info()->creation_date(); });
CMD2_DL("d.load_date", [](auto* download, auto) { return download->info()->load_date(); });
//
// Network related:
//
CMD2_DL ("d.up.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(up_rate)));
CMD2_DL ("d.up.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(up_rate)));
CMD2_DL ("d.down.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(down_rate)));
CMD2_DL ("d.down.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(down_rate)));
CMD2_DL ("d.skip.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(skip_rate)));
CMD2_DL ("d.skip.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(skip_rate)));
CMD2_DL ("d.up.rate", [](auto* download, auto) { return download->info()->up_rate()->rate(); });
CMD2_DL ("d.up.total", [](auto* download, auto) { return download->info()->up_rate()->total(); });
CMD2_DL ("d.down.rate", [](auto* download, auto) { return download->info()->down_rate()->rate(); });
CMD2_DL ("d.down.total", [](auto* download, auto) { return download->info()->down_rate()->total(); });
CMD2_DL ("d.skip.rate", [](auto* download, auto) { return download->info()->skip_rate()->rate(); });
CMD2_DL ("d.skip.total", [](auto* download, auto) { return download->info()->skip_rate()->total(); });
CMD2_DL ("d.peer_exchange", CMD2_ON_INFO(is_pex_enabled));
CMD2_DL_VALUE_V ("d.peer_exchange.set", std::bind(&torrent::Download::set_pex_enabled, CMD2_BIND_DL, std::placeholders::_2));
CMD2_DL ("d.peer_exchange", [](auto* download, auto) { return download->info()->is_pex_enabled(); });
CMD2_DL_VALUE_V ("d.peer_exchange.set", [](auto* download, auto value) { download->download()->set_pex_enabled(value); });
CMD2_DL_LIST ("d.create_link", std::bind(&apply_d_change_link, std::placeholders::_1, std::placeholders::_2, 0));
CMD2_DL_LIST ("d.delete_link", std::bind(&apply_d_change_link, std::placeholders::_1, std::placeholders::_2, 1));
@@ -680,16 +701,16 @@ initialize_command_download() {
// Control functinos:
//
CMD2_DL ("d.is_open", CMD2_ON_INFO(is_open));
CMD2_DL ("d.is_active", CMD2_ON_INFO(is_active));
CMD2_DL ("d.is_open", [](auto* download, auto) { return download->info()->is_open(); });
CMD2_DL ("d.is_active", [](auto* download, auto) { return download->info()->is_active(); });
CMD2_DL ("d.is_hash_checked", std::bind(&torrent::Download::is_hash_checked, CMD2_BIND_DL));
CMD2_DL ("d.is_hash_checking", std::bind(&torrent::Download::is_hash_checking, CMD2_BIND_DL));
CMD2_DL ("d.is_multi_file", std::bind(&torrent::FileList::is_multi_file, CMD2_BIND_FL));
CMD2_DL ("d.is_private", CMD2_ON_INFO(is_private));
CMD2_DL ("d.is_pex_active", CMD2_ON_INFO(is_pex_active));
CMD2_DL ("d.is_private", [](auto* download, auto) { return download->info()->is_private(); });
CMD2_DL ("d.is_pex_active", [](auto* download, auto) { return download->info()->is_pex_active(); });
CMD2_DL ("d.is_partially_done", CMD2_ON_DATA(is_partially_done));
CMD2_DL ("d.is_not_partially_done", CMD2_ON_DATA(is_not_partially_done));
CMD2_DL ("d.is_meta", CMD2_ON_INFO(is_meta_download));
CMD2_DL ("d.is_meta", [](auto* download, auto) { return download->info()->is_meta_download(); });
CMD2_DL_V ("d.resume", std::bind(&core::DownloadList::resume_default, control->core()->download_list(), std::placeholders::_1));
CMD2_DL_V ("d.pause", std::bind(&core::DownloadList::pause_default, control->core()->download_list(), std::placeholders::_1));
@@ -891,8 +912,26 @@ initialize_command_download() {
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_path.hex");
rpc::rpc.mark_safe("d.base_path.base64");
rpc::rpc.mark_safe("d.base_path.base64_as_binary");
rpc::rpc.mark_safe("d.base_path.as_binary");
rpc::rpc.mark_safe("d.base_path.or_base64");
rpc::rpc.mark_safe("d.base_path.or_as_binary");
rpc::rpc.mark_safe("d.base_filename");
rpc::rpc.mark_safe("d.base_filename.hex");
rpc::rpc.mark_safe("d.base_filename.base64");
rpc::rpc.mark_safe("d.base_filename.base64_as_binary");
rpc::rpc.mark_safe("d.base_filename.as_binary");
rpc::rpc.mark_safe("d.base_filename.or_base64");
rpc::rpc.mark_safe("d.base_filename.or_as_binary");
rpc::rpc.mark_safe("d.name");
rpc::rpc.mark_safe("d.name.hex");
rpc::rpc.mark_safe("d.name.base64");
rpc::rpc.mark_safe("d.name.base64_as_binary");
rpc::rpc.mark_safe("d.name.as_binary");
rpc::rpc.mark_safe("d.name.or_base64");
rpc::rpc.mark_safe("d.name.or_as_binary");
rpc::rpc.mark_safe("d.directory");
rpc::rpc.mark_safe("d.directory_base");
rpc::rpc.mark_safe("d.creation_date");
+27 -6
View File
@@ -2,12 +2,14 @@
#include <functional>
#include <cstdio>
#include <rak/string_manip.h>
#include <string>
#include <vector>
#include <torrent/rate.h>
#include <torrent/hash_string.h>
#include <torrent/utils/log.h>
#include <torrent/utils/directory_events.h>
#include <torrent/utils/file_stat.h>
#include <torrent/utils/string_manip.h>
#include "globals.h"
#include "control.h"
@@ -19,6 +21,7 @@
#include "rpc/command_scheduler.h"
#include "rpc/parse.h"
#include "rpc/parse_commands.h"
#include "utils/watch_ready_queue.h"
torrent::Object
apply_on_ratio(const torrent::Object& rawArgs) {
@@ -208,7 +211,7 @@ apply_download_list(const torrent::Object::list_type& args) {
for (core::View::const_iterator itr = (*view_itr)->begin_visible(), last = (*view_itr)->end_visible(); itr != last; itr++) {
const torrent::HashString* hashString = &(*itr)->info()->hash();
resultList.push_back(rak::transform_hex(hashString->begin(), hashString->end()));
resultList.push_back(torrent::utils::transform_to_hex_str(*hashString));
}
return result;
@@ -291,22 +294,39 @@ call_watch_command(const std::string& command, const std::string& path) {
}
torrent::Object
directory_watch_added(const torrent::Object::list_type& args) {
directory_watch(const torrent::Object::list_type& args, int flags) {
if (args.size() != 2)
throw torrent::input_error("Too few arguments.");
auto& path = args.front().as_string();
auto& command = args.back().as_string();
std::string expanded_path = expand_path(path);
if (!control->directory_events()->open())
throw torrent::input_error("Could not open inotify:" + std::string(std::strerror(errno)));
control->directory_events()->notify_on(path.c_str(),
torrent::directory_events::flag_on_added | torrent::directory_events::flag_on_updated,
std::bind(&call_watch_command, command, std::placeholders::_1));
torrent::watch_descriptor::slot_string slot =
flags == torrent::directory_events::flag_on_ready ?
torrent::watch_descriptor::slot_string([command](const auto& arg) { control->watch_ready_queue()->push(command, arg); }) :
torrent::watch_descriptor::slot_string(std::bind(&call_watch_command, command, std::placeholders::_1));
control->directory_events()->notify_on(expanded_path.c_str(), flags, slot);
return torrent::Object();
}
torrent::Object
directory_watch_added(const torrent::Object::list_type& args) {
return directory_watch(args,
torrent::directory_events::flag_on_added |
torrent::directory_events::flag_on_updated);
}
torrent::Object
directory_watch_ready(const torrent::Object::list_type& args) {
return directory_watch(args,
torrent::directory_events::flag_on_ready);
}
void
initialize_command_events() {
CMD2_ANY_STRING ("on_ratio", std::bind(&apply_on_ratio, std::placeholders::_2));
@@ -344,6 +364,7 @@ initialize_command_events() {
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));
CMD2_ANY_LIST ("directory.watch.ready", std::bind(&directory_watch_ready, std::placeholders::_2));
rpc::rpc.mark_safe("start_tied");
rpc::rpc.mark_safe("stop_untied");
+50 -23
View File
@@ -24,24 +24,34 @@ apply_f_path(torrent::File* file) {
if (file->path()->empty())
return std::string();
torrent::Object resultRaw(*file->path()->begin());
torrent::Object::string_type& result = resultRaw.as_string();
torrent::Object result_raw(file->path()->begin()->str());
auto& result = result_raw.as_string();
for (torrent::Path::const_iterator itr = ++file->path()->begin(), last = file->path()->end(); itr != last; itr++)
result += '/' + *itr;
result += '/' + itr->str();
return resultRaw;
return result_raw;
}
torrent::Object
apply_f_path_components(torrent::File* file) {
torrent::Object resultRaw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list();
apply_f_path_components(torrent::File* file, int output_type) {
auto result_raw = torrent::Object::create_list();
auto& result = result_raw.as_list();
for (const auto& itr : *file->path())
result.push_back(itr);
for (const auto& itr : *file->path()) {
switch (output_type) {
case 0: result.push_back(itr.str()); break;
case 1: result.push_back(itr.object_hex()); break;
case 2: result.push_back(itr.object_base64()); break;
case 3: result.push_back(itr.object_base64_as_binary()); break;
case 4: result.push_back(itr.object_as_binary()); break;
case 5: result.push_back(itr.object_utf8_or_base64()); break;
case 6: result.push_back(itr.object_utf8_or_as_binary()); break;
default: throw torrent::input_error("apply_f_path_components(): invalid output type");
};
}
return resultRaw;
return result_raw;
}
torrent::Object
@@ -57,7 +67,7 @@ apply_fi_filename_last(torrent::FileListIterator* itr) {
if (itr->depth() >= itr->file()->path()->size())
return "ERROR";
return itr->file()->path()->at(itr->depth());
return itr->file()->path()->at(itr->depth()).str();
}
void
@@ -80,21 +90,32 @@ initialize_command_file() {
CMD2_FILE_V("f.prioritize_last.enable", std::bind(&torrent::File::set_flags, std::placeholders::_1, torrent::File::flag_prioritize_last));
CMD2_FILE_V("f.prioritize_last.disable", std::bind(&torrent::File::unset_flags, std::placeholders::_1, torrent::File::flag_prioritize_last));
CMD2_FILE("f.size_bytes", std::bind(&torrent::File::size_bytes, std::placeholders::_1));
CMD2_FILE("f.size_chunks", std::bind(&torrent::File::size_chunks, std::placeholders::_1));
CMD2_FILE("f.completed_chunks", std::bind(&torrent::File::completed_chunks, std::placeholders::_1));
CMD2_FILE("f.size_bytes", [](auto* file, auto) { return file->size_bytes(); });
CMD2_FILE("f.size_chunks", [](auto* file, auto) { return file->size_chunks(); });
CMD2_FILE("f.completed_chunks", [](auto* file, auto) { return file->completed_chunks(); });
CMD2_FILE("f.offset", std::bind(&torrent::File::offset, std::placeholders::_1));
CMD2_FILE("f.range_first", std::bind(&torrent::File::range_first, std::placeholders::_1));
CMD2_FILE("f.range_second", std::bind(&torrent::File::range_second, std::placeholders::_1));
CMD2_FILE("f.offset", [](auto* file, auto) { return file->offset(); });
CMD2_FILE("f.range_first", [](auto* file, auto) { return file->range_first(); });
CMD2_FILE("f.range_second", [](auto* file, auto) { return file->range_second(); });
CMD2_FILE("f.priority", std::bind(&torrent::File::priority, std::placeholders::_1));
CMD2_FILE_VALUE_V("f.priority.set", std::bind(&apply_f_set_priority, std::placeholders::_1, std::placeholders::_2));
CMD2_FILE("f.priority", [](auto* file, auto) { return file->priority(); });
CMD2_FILE_VALUE_V("f.priority.set", [](auto* file, auto v) { apply_f_set_priority(file, v); });
CMD2_FILE("f.path", std::bind(&apply_f_path, std::placeholders::_1));
CMD2_FILE("f.path_components", std::bind(&apply_f_path_components, std::placeholders::_1));
CMD2_FILE("f.path_depth", std::bind(&apply_f_path_depth, std::placeholders::_1));
CMD2_FILE("f.frozen_path", std::bind(&torrent::File::frozen_path, std::placeholders::_1));
CMD2_FILE("f.path", [](auto* file, auto) { return apply_f_path(file); });
CMD2_FILE("f.path_components", [](auto* file, auto) { return apply_f_path_components(file, 0); });
CMD2_FILE("f.path_components.hex", [](auto* file, auto) { return apply_f_path_components(file, 1); });
CMD2_FILE("f.path_components.base64", [](auto* file, auto) { return apply_f_path_components(file, 2); });
CMD2_FILE("f.path_components.base64_as_binary", [](auto* file, auto) { return apply_f_path_components(file, 3); });
CMD2_FILE("f.path_components.as_binary", [](auto* file, auto) { return apply_f_path_components(file, 4); });
CMD2_FILE("f.path_components.or_base64", [](auto* file, auto) { return apply_f_path_components(file, 5); });
CMD2_FILE("f.path_components.or_as_binary", [](auto* file, auto) { return apply_f_path_components(file, 6); });
CMD2_FILE("f.path_depth", [](auto* file, auto) { return apply_f_path_depth(file); });
CMD2_FILE("f.frozen_path", [](auto* file, auto) { return file->frozen_path().str(); });
CMD2_FILE("f.frozen_path.hex", [](auto* file, auto) { return file->frozen_path().object_hex(); });
CMD2_FILE("f.frozen_path.base64", [](auto* file, auto) { return file->frozen_path().object_base64(); });
CMD2_FILE("f.frozen_path.as_binary", [](auto* file, auto) { return file->frozen_path().object_as_binary(); });
CMD2_FILE("f.frozen_path.or_base64", [](auto* file, auto) { return file->frozen_path().object_utf8_or_base64(); });
CMD2_FILE("f.frozen_path.or_as_binary", [](auto* file, auto) { return file->frozen_path().object_utf8_or_as_binary(); });
CMD2_FILE("f.match_depth_prev", std::bind(&torrent::File::match_depth_prev, std::placeholders::_1));
CMD2_FILE("f.match_depth_next", std::bind(&torrent::File::match_depth_next, std::placeholders::_1));
@@ -108,6 +129,12 @@ initialize_command_file() {
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.frozen_path.hex");
rpc::rpc.mark_safe("f.frozen_path.base64");
rpc::rpc.mark_safe("f.frozen_path.base64_as_binary");
rpc::rpc.mark_safe("f.frozen_path.as_binary");
rpc::rpc.mark_safe("f.frozen_path.or_base64");
rpc::rpc.mark_safe("f.frozen_path.or_as_binary");
rpc::rpc.mark_safe("f.offset");
rpc::rpc.mark_safe("f.size_bytes");
rpc::rpc.mark_safe("f.size_chunks");
+8 -3
View File
@@ -29,7 +29,7 @@ torrent::Object
apply_ip_tables_size_data(const std::string& args) {
rpc::ip_table_list::const_iterator itr = ip_tables.find(args);
if (itr != ip_tables.end())
if (itr == ip_tables.end())
throw torrent::input_error("IP table does not exist.");
uint32_t size = itr->table.sizeof_data();
@@ -203,9 +203,14 @@ ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address
uint32_t mask=0;
uint32_t end_mask=0;
mask = (~mask) << (32-mask_bits);
if (mask_bits == 0) {
mask = 0;
end_mask = ~(uint32_t)0;
} else {
mask = (~mask) << (32-mask_bits);
end_mask = (~end_mask) >> mask_bits;
}
*address_start = ip & mask;
end_mask = (~end_mask) >> mask_bits;
*address_end = (ip & mask) | end_mask;
valid=true;
+8 -4
View File
@@ -11,13 +11,13 @@
#include <torrent/chunk_manager.h>
#include <torrent/data/file_manager.h>
#include <torrent/data/chunk_utils.h>
#include <torrent/runtime/runtime.h>
#include <torrent/utils/chrono.h>
#include <torrent/utils/option_strings.h>
#include "core/download.h"
#include "core/download_list.h"
#include "core/manager.h"
#include "rak/string_manip.h"
#include "rpc/parse_commands.h"
#include "rpc/scgi.h"
#include "session/session_manager.h"
@@ -95,8 +95,12 @@ post_increment(torrent::Object::list_const_iterator& itr, const torrent::Object:
inline const std::string&
check_name(const std::string& str) {
if (!rak::is_all_name(str))
throw torrent::input_error("Non-alphanumeric characters found.");
auto itr = std::find_if(str.begin(), str.end(), [](char c) {
return !std::isalnum(c, std::locale::classic()) && c != '_';
});
if (itr != str.end())
throw torrent::input_error("Invalid characters found in name.");
return str;
}
@@ -204,7 +208,7 @@ initialize_command_local() {
CMD2_VAR_C_STRING("system.api_version", (int64_t)API_VERSION);
CMD2_VAR_C_STRING("system.client_version", PACKAGE_VERSION);
CMD2_VAR_C_STRING("system.library_version", torrent::version());
CMD2_VAR_C_STRING("system.library_version", torrent::runtime::version());
CMD2_VAR_VALUE ("system.file.allocate", 0);
CMD2_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30);
+11 -39
View File
@@ -8,53 +8,23 @@
#include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
#include "globals.h"
#include "control.h"
#include "command_helpers.h"
#include "globals.h"
#include "setup.h"
#include "core/download.h"
#include "core/download_list.h"
#include "core/manager.h"
#include "rpc/parse_commands.h"
static const int log_flag_use_gz = 0x1;
static const int log_flag_append_pid = 0x2;
static const int log_flag_append_file = 0x4;
static const int log_flag_flush = 0x8;
void
log_add_group_output_str(const char* group_name, const char* output_id) {
int log_group = torrent::option_find_string(torrent::OPTION_LOG_GROUP, group_name);
torrent::log_add_group_output(log_group, output_id);
}
torrent::Object
apply_log_open(int output_flags, const torrent::Object::list_type& args) {
if (args.size() < 2)
throw torrent::input_error("Invalid number of arguments.");
apply_log_open(int output_flags, const torrent::Object::list_type& raw_args) {
std::vector<std::string> args;
torrent::Object::list_const_iterator itr = args.begin();
auto output_id = (itr++)->as_string();
auto file_name = expand_path((itr++)->as_string());
if ((output_flags & log_flag_append_pid)) {
char buffer[32];
snprintf(buffer, 32, ".%li", (long)getpid());
file_name += buffer;
}
bool append = (output_flags & log_flag_append_file);
bool flush = (output_flags & log_flag_flush);
if ((output_flags & log_flag_use_gz))
torrent::log_open_gz_file_output(output_id.c_str(), file_name.c_str(), append);
else
torrent::log_open_file_output(output_id.c_str(), file_name.c_str(), append, flush);
while (itr != args.end())
log_add_group_output_str((itr++)->as_string().c_str(), output_id.c_str());
for (const auto& arg : raw_args)
args.push_back(arg.as_string());
apply_log_open_str(output_flags, args);
return torrent::Object();
}
@@ -63,8 +33,7 @@ apply_log_add_output(const torrent::Object::list_type& args) {
if (args.size() != 2)
throw torrent::input_error("Invalid number of arguments.");
log_add_group_output_str(args.front().as_string().c_str(),
args.back().as_string().c_str());
log_add_group_output_str(args.front().as_string(), args.back().as_string());
return torrent::Object();
}
@@ -118,6 +87,9 @@ log_vmmap_dump(const std::string& str) {
FILE* log_file = fopen(str.c_str(), "w");
if (log_file == NULL)
throw torrent::input_error("Could not open log file: " + str);
for (auto& all_mapping : all_mappings) {
fprintf(log_file, "%8p-%8p [%5llxk]\n", all_mapping.ptr, (char*)all_mapping.ptr + all_mapping.length, (long long unsigned int)(all_mapping.length / 1024));
}
+38 -37
View File
@@ -3,18 +3,15 @@
#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>
#include <torrent/data/file_manager.h>
#include <torrent/download/resource_manager.h>
#include <torrent/net/http_stack.h>
#include <torrent/net/network_config.h>
#include <torrent/net/socket_address.h>
#include <torrent/runtime/network_config.h>
#include <torrent/runtime/runtime.h>
#include <torrent/runtime/socket_manager.h>
#include <torrent/tracker/tracker.h>
#include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
@@ -29,20 +26,25 @@
#include "rpc/parse.h"
#include "rpc/parse_commands.h"
#ifdef HAVE_SYSTEMD
#include <sys/socket.h>
#include <systemd/sd-daemon.h>
#endif
torrent::Object
apply_encryption(const torrent::Object::list_type& args) {
uint32_t options_mask = torrent::net::NetworkConfig::encryption_none;
uint32_t options_mask = torrent::runtime::NetworkConfig::encryption_none;
for (const auto& arg : args) {
uint32_t opt = torrent::option_find_string(torrent::OPTION_ENCRYPTION, arg.as_string().c_str());
if (opt == torrent::net::NetworkConfig::encryption_none)
options_mask = torrent::net::NetworkConfig::encryption_none;
if (opt == torrent::runtime::NetworkConfig::encryption_none)
options_mask = torrent::runtime::NetworkConfig::encryption_none;
else
options_mask |= opt;
}
torrent::config::network_config()->set_encryption_options(options_mask);
torrent::runtime::network_config()->set_encryption_options(options_mask);
return torrent::Object();
}
@@ -54,7 +56,7 @@ apply_tos(const torrent::Object::string_type& arg) {
if (!rpc::parse_whole_value_nothrow(arg.c_str(), &value, 16, 1))
value = torrent::option_find_string(torrent::OPTION_IP_TOS, arg.c_str());
torrent::config::network_config()->set_priority(value);
torrent::runtime::network_config()->set_priority(value);
return torrent::Object();
}
@@ -85,14 +87,13 @@ apply_scgi(const std::string& arg, int type) {
initialize_rpc_handlers();
rpc::SCgi* scgi = new rpc::SCgi;
rak::address_info* ai = NULL;
torrent::sa_unique_ptr sa;
auto scgi = std::make_unique<rpc::SCgi>();
try {
int port, err;
char dummy;
int port{};
char dummy{};
char address[1024];
std::string path;
@@ -106,10 +107,11 @@ apply_scgi(const std::string& arg, int type) {
} 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
if ((err = rak::address_info::get_address_info(address, PF_UNSPEC, SOCK_STREAM, &ai)) != 0)
throw torrent::input_error("Could not bind address: " + std::string(rak::address_info::strerror(err)) + ".");
sa = torrent::sa_copy(ai->c_addrinfo()->ai_addr);
try {
sa = torrent::sa_copy(torrent::sa_lookup_address(address, AF_UNSPEC).get());
} catch (torrent::input_error& e) {
throw torrent::input_error("Could not bind address: " + std::string(e.what()));
}
lt_log_print(torrent::LOG_RPC_EVENTS, "SCGI socket is bound to an address and might be a security risk");
@@ -134,16 +136,11 @@ apply_scgi(const std::string& arg, int type) {
break;
}
if (ai != NULL) rak::address_info::free_address_info(ai);
} catch (torrent::local_error& e) {
if (ai != NULL) rak::address_info::free_address_info(ai);
delete scgi;
throw torrent::input_error(e.what());
}
scgi_thread::set_scgi(scgi);
scgi_thread::set_scgi(scgi.release());
return torrent::Object();
}
@@ -220,10 +217,9 @@ apply_xmlrpc_dialect(const std::string& arg) {
void
initialize_command_network() {
auto cm = torrent::connection_manager();
auto file_manager = torrent::file_manager();
auto http_stack = torrent::net_thread::http_stack();
auto nw_config = torrent::config::network_config();
auto file_manager = torrent::file_manager();
auto http_stack = torrent::net_thread::http_stack();
auto nw_config = torrent::runtime::network_config();
CMD2_ANY_STRING ("encoding.add", std::bind(&apply_encoding_list, std::placeholders::_2));
@@ -293,19 +289,24 @@ initialize_command_network() {
CMD2_ANY ("network.open_files", [file_manager](auto, auto) { return file_manager->open_files(); });
CMD2_ANY ("network.max_open_files", [file_manager](auto, auto) { return file_manager->max_open_files(); });
CMD2_ANY_VALUE_V ("network.max_open_files.set", [file_manager](auto, auto& value) { return file_manager->set_max_open_files(value); });
CMD2_ANY ("network.total_handshakes", [](auto, auto) { return torrent::total_handshakes(); });
CMD2_ANY ("network.open_sockets", [cm](auto, auto) { return cm->size(); });
CMD2_ANY ("network.max_open_sockets", [cm](auto, auto) { return cm->max_size(); });
CMD2_ANY_VALUE_V ("network.max_open_sockets.set", [cm](auto, auto& value) { return cm->set_max_size(value); });
CMD2_ANY ("network.total_handshakes", [](auto, auto) { return torrent::runtime::total_handshakes(); });
CMD2_ANY ("network.open_sockets", [](auto, auto) { return torrent::runtime::socket_manager()->size(); });
CMD2_ANY ("network.max_open_sockets", [](auto, auto) { return torrent::runtime::socket_manager()->max_size(); });
CMD2_ANY_VALUE_V ("network.max_open_sockets.set", [](auto, auto& value) { return torrent::runtime::socket_manager()->set_max_size_and_adjust(value); });
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(); });
CMD2_ANY_VALUE_V ("network.xmlrpc.size_limit.set", [](const auto&, const auto& arg) { return rpc::rpc.set_size_limit(arg); });
CMD2_ANY ("network.scgi.use_gzip", [](const auto&, const auto&) { return rpc::rpc.scgi_allow_compression(); });
CMD2_ANY_VALUE_V ("network.scgi.use_gzip.set", [](const auto&, const auto& arg) { return rpc::rpc.set_scgi_allow_compression(arg); });
CMD2_ANY ("network.scgi.gzip.min_size", [](const auto&, const auto&) { return rpc::rpc.scgi_min_compress_size(); });
CMD2_ANY_VALUE_V ("network.scgi.gzip.min_size.set", [](const auto&, const auto& arg) { return rpc::rpc.set_scgi_min_compress_size(arg); });
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(); });
CMD2_ANY_VALUE_V ("network.xmlrpc.size_limit.set", [](const auto&, const auto& arg) { return rpc::rpc.set_size_limit(arg); });
CMD2_VAR_BOOL ("network.rpc.use_xmlrpc", true);
CMD2_VAR_BOOL ("network.rpc.use_jsonrpc", true);
+8 -28
View File
@@ -1,33 +1,18 @@
#include "config.h"
#include <rak/string_manip.h>
#include <torrent/bitfield.h>
#include <torrent/rate.h>
#include <torrent/net/socket_address.h>
#include <torrent/peer/connection_list.h>
#include <torrent/peer/peer.h>
#include <torrent/peer/peer_info.h>
#include "core/manager.h"
#include "display/utils.h"
#include <torrent/utils/string_manip.h>
#include "globals.h"
#include "control.h"
#include "command_helpers.h"
torrent::Object
retrieve_p_id(torrent::Peer* peer) {
const torrent::HashString* hashString = &peer->id();
return rak::transform_hex(hashString->begin(), hashString->end());
}
torrent::Object
retrieve_p_id_html(torrent::Peer* peer) {
const torrent::HashString* hashString = &peer->id();
return rak::copy_escape_html(hashString->begin(), hashString->end());
}
#include "control.h"
#include "core/manager.h"
#include "display/utils.h"
torrent::Object
retrieve_p_address(torrent::Peer* peer) {
@@ -40,11 +25,6 @@ retrieve_p_address(torrent::Peer* peer) {
return addr_str;
}
torrent::Object
retrieve_p_port(torrent::Peer* peer) {
return torrent::sa_port(peer->peer_info()->socket_address());
}
torrent::Object
retrieve_p_client_version(torrent::Peer* peer) {
char buf[128];
@@ -55,7 +35,7 @@ retrieve_p_client_version(torrent::Peer* peer) {
torrent::Object
retrieve_p_options_str(torrent::Peer* peer) {
return rak::transform_hex(peer->peer_info()->options(), peer->peer_info()->options() + 8);
return torrent::utils::transform_to_hex_str(peer->peer_info()->options(), peer->peer_info()->options() + 8);
}
torrent::Object
@@ -67,8 +47,8 @@ retrieve_p_completed_percent(torrent::Peer* peer) {
void
initialize_command_peer() {
CMD2_PEER("p.id", std::bind(&retrieve_p_id, std::placeholders::_1));
CMD2_PEER("p.id_html", std::bind(&retrieve_p_id_html, std::placeholders::_1));
CMD2_PEER("p.id", [](auto* peer, auto) { return torrent::utils::transform_to_hex_str(peer->id()); });
CMD2_PEER("p.id_html", [](auto* peer, auto) { return torrent::utils::copy_escape_html_str(peer->id()); });
CMD2_PEER("p.client_version", std::bind(&retrieve_p_client_version, std::placeholders::_1));
CMD2_PEER("p.options_str", std::bind(&retrieve_p_options_str, std::placeholders::_1));
@@ -82,7 +62,7 @@ initialize_command_peer() {
CMD2_PEER("p.is_preferred", std::bind(&torrent::PeerInfo::is_preferred, std::bind(&torrent::Peer::peer_info, std::placeholders::_1)));
CMD2_PEER("p.address", std::bind(&retrieve_p_address, std::placeholders::_1));
CMD2_PEER("p.port", std::bind(&retrieve_p_port, std::placeholders::_1));
CMD2_PEER("p.port", [](auto* peer, auto) { return torrent::sa_port(peer->peer_info()->socket_address()); });
CMD2_PEER("p.completed_percent", std::bind(&retrieve_p_completed_percent, std::placeholders::_1));
+17 -12
View File
@@ -8,7 +8,6 @@
#include "core/manager.h"
#include "ui/root.h"
#include "rak/address_info.h"
#include "rpc/parse.h"
#include "rpc/parse_commands.h"
@@ -21,21 +20,26 @@ parse_address_range(const torrent::Object::list_type& args, torrent::Object::lis
unsigned int prefixWidth, ret;
char dummy;
char host[1024];
rak::address_info* ai;
torrent::sa_unique_ptr sa;
ret = std::sscanf(itr->as_string().c_str(), "%1023[^/]/%d%c", host, &prefixWidth, &dummy);
if (ret < 1 || rak::address_info::get_address_info(host, PF_INET, SOCK_STREAM, &ai) != 0)
throw torrent::input_error("Could not resolve host.");
if (ret < 1)
throw torrent::input_error("Invalid address/prefix.");
try {
sa = torrent::sa_copy(torrent::sa_lookup_address(host, AF_INET).get());
} catch (torrent::input_error& e) {
throw torrent::input_error("Could not resolve host: " + std::string(e.what()));
}
uint32_t begin, end;
auto sa = torrent::sa_copy(ai->c_addrinfo()->ai_addr);
auto sa_addr = htonl(reinterpret_cast<sockaddr_in*>(sa.get())->sin_addr.s_addr);
begin = end = sa_addr;
rak::address_info::free_address_info(ai);
if (ret == 2) {
if (++itr != args.end())
throw torrent::input_error("Cannot specify both network and range end.");
@@ -48,13 +52,14 @@ parse_address_range(const torrent::Object::list_type& args, torrent::Object::lis
end = sa_addr | ~netmask;
} else if (++itr != args.end()) {
if (rak::address_info::get_address_info(itr->as_string().c_str(), PF_INET, SOCK_STREAM, &ai) != 0)
throw torrent::input_error("Could not resolve host.");
try {
sa = torrent::sa_copy(torrent::sa_lookup_address(itr->as_string(), AF_INET).get());
} catch (torrent::input_error& e) {
throw torrent::input_error("Could not resolve host: " + std::string(e.what()));
}
sa = torrent::sa_copy(ai->c_addrinfo()->ai_addr);
sa_addr = htonl(reinterpret_cast<sockaddr_in*>(sa.get())->sin_addr.s_addr);
rak::address_info::free_address_info(ai);
end = sa_addr;
}
+32 -35
View File
@@ -3,9 +3,10 @@
#include <cassert>
#include <cstdio>
#include <netdb.h>
#include <torrent/net/network_config.h>
#include <torrent/net/resolver.h>
#include <torrent/runtime/network_config.h>
#include <torrent/runtime/network_manager.h>
#include <torrent/system/callbacks.h>
#include <torrent/tracker/dht_controller.h>
#include <torrent/tracker/tracker.h>
#include <torrent/utils/log.h>
@@ -48,8 +49,10 @@ apply_dht_add_node(const std::string& arg) {
// TODO: Move this lookup to DhtController.
auto callback_id = torrent::system::make_callback_id();
// Currently discarding SOCK_STREAM.
torrent::this_thread::resolver()->resolve_specific(nullptr, host_str, PF_INET, [host_str, port](torrent::c_sa_shared_ptr sa, int err) {
torrent::this_thread::resolver()->resolve_specific(callback_id, host_str, PF_INET, [host_str, port](torrent::c_sa_shared_ptr sa, int err) {
if (sa == nullptr) {
lt_log_print(torrent::LOG_DHT_ERROR, "dht.add_node : could not resolve host : %s (%s)", gai_strerror(err), host_str.c_str());
return;
@@ -68,16 +71,6 @@ apply_enable_trackers(int64_t arg) {
for (auto download : *control->core()->download_list())
download->tracker_controller().for_each([](auto& tracker) { tracker.disable(); });
} else if (rpc::call_command_value("trackers.use_udp") == 0) {
for (auto download : *control->core()->download_list()) {
download->tracker_controller().for_each([](auto& tracker) {
if (tracker.type() == torrent::TRACKER_UDP)
tracker.disable();
else
tracker.enable();
});
}
} else {
for (auto download : *control->core()->download_list())
download->tracker_controller().for_each([](auto& tracker) { tracker.enable(); });
@@ -88,10 +81,10 @@ apply_enable_trackers(int64_t arg) {
void
initialize_command_tracker() {
CMD2_TRACKER ("t.is_busy", std::bind(&torrent::tracker::Tracker::is_busy, std::placeholders::_1));
CMD2_TRACKER ("t.is_busy", std::bind(&torrent::tracker::Tracker::is_requesting, std::placeholders::_1));
CMD2_TRACKER ("t.is_enabled", std::bind(&torrent::tracker::Tracker::is_enabled, std::placeholders::_1));
CMD2_TRACKER ("t.is_extra_tracker", std::bind(&torrent::tracker::Tracker::is_extra_tracker, std::placeholders::_1));
CMD2_TRACKER ("t.is_open", std::bind(&torrent::tracker::Tracker::is_busy, std::placeholders::_1));
CMD2_TRACKER ("t.is_open", std::bind(&torrent::tracker::Tracker::is_requesting, std::placeholders::_1));
CMD2_TRACKER ("t.is_scrapable", std::bind(&torrent::tracker::Tracker::is_scrapable, std::placeholders::_1));
CMD2_TRACKER ("t.is_usable", std::bind(&torrent::tracker::Tracker::is_usable, std::placeholders::_1));
@@ -108,36 +101,40 @@ initialize_command_tracker() {
CMD2_TRACKER ("t.type", std::bind(&torrent::tracker::Tracker::type, std::placeholders::_1));
CMD2_TRACKER ("t.id", std::bind(&torrent::tracker::Tracker::tracker_id, std::placeholders::_1));
CMD2_TRACKER ("t.latest_event", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().latest_event(); });
CMD2_TRACKER ("t.latest_new_peers", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().latest_new_peers(); });
CMD2_TRACKER ("t.latest_sum_peers", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().latest_sum_peers(); });
CMD2_TRACKER ("t.latest_event", [](auto* tracker, auto) { return tracker->state().latest_event(); });
CMD2_TRACKER ("t.latest_new_peers", [](auto* tracker, auto) { return tracker->state().latest_new_peers(); });
CMD2_TRACKER ("t.latest_sum_peers", [](auto* tracker, auto) { return tracker->state().latest_sum_peers(); });
CMD2_TRACKER ("t.normal_interval", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().normal_interval(); });
CMD2_TRACKER ("t.min_interval", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().min_interval(); });
CMD2_TRACKER ("t.normal_interval", [](auto* tracker, auto) { return tracker->state().normal_interval(); });
CMD2_TRACKER ("t.min_interval", [](auto* tracker, auto) { return tracker->state().min_interval(); });
CMD2_TRACKER ("t.activity_time_next", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().activity_time_next(); });
CMD2_TRACKER ("t.activity_time_last", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().activity_time_last(); });
CMD2_TRACKER ("t.activity_time_next", [](auto* tracker, auto) { return tracker->state().activity_time_next(); });
CMD2_TRACKER ("t.activity_time_last", [](auto* tracker, auto) { return tracker->state().activity_time_last(); });
CMD2_TRACKER ("t.success_time_next", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().success_time_next(); });
CMD2_TRACKER ("t.success_time_last", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().success_time_last(); });
CMD2_TRACKER ("t.success_counter", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().success_counter(); });
CMD2_TRACKER ("t.success_time_next", [](auto* tracker, auto) { return tracker->state().success_time_next(); });
CMD2_TRACKER ("t.success_time_last", [](auto* tracker, auto) { return tracker->state().success_time_last(); });
CMD2_TRACKER ("t.success_counter", [](auto* tracker, auto) { return tracker->state().success_counter(); });
CMD2_TRACKER ("t.failed_time_next", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().failed_time_next(); });
CMD2_TRACKER ("t.failed_time_last", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().failed_time_last(); });
CMD2_TRACKER ("t.failed_counter", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().failed_counter(); });
CMD2_TRACKER ("t.failed_time_next", [](auto* tracker, auto) { return tracker->state().failed_time_next(); });
CMD2_TRACKER ("t.failed_time_last", [](auto* tracker, auto) { return tracker->state().failed_time_last(); });
CMD2_TRACKER ("t.failed_counter", [](auto* tracker, auto) { return tracker->state().failed_counter(); });
CMD2_TRACKER ("t.scrape_time_last", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_time_last(); });
CMD2_TRACKER ("t.scrape_counter", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_counter(); });
CMD2_TRACKER ("t.scrape_time_last", [](auto* tracker, auto) { return tracker->state().scrape_time_last(); });
CMD2_TRACKER ("t.scrape_counter", [](auto* tracker, auto) { return tracker->state().scrape_counter(); });
CMD2_TRACKER ("t.scrape_complete", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_complete(); });
CMD2_TRACKER ("t.scrape_incomplete", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_incomplete(); });
CMD2_TRACKER ("t.scrape_downloaded", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_downloaded(); });
CMD2_TRACKER ("t.scrape_complete", [](auto* tracker, auto) { return tracker->state().scrape_complete(); });
CMD2_TRACKER ("t.scrape_incomplete", [](auto* tracker, auto) { return tracker->state().scrape_incomplete(); });
CMD2_TRACKER ("t.scrape_downloaded", [](auto* tracker, auto) { return tracker->state().scrape_downloaded(); });
CMD2_ANY_VALUE ("trackers.enable", std::bind(&apply_enable_trackers, int64_t(1)));
CMD2_ANY_VALUE ("trackers.disable", std::bind(&apply_enable_trackers, int64_t(0)));
CMD2_VAR_BOOL ("trackers.delay_scrape", false);
CMD2_VAR_VALUE ("trackers.numwant", -1);
CMD2_VAR_BOOL ("trackers.use_udp", true);
CMD2_ANY ("trackers.use_udp", [](auto, auto) { return true; });
CMD2_ANY_VALUE_V ("trackers.use_udp.set", [](auto, auto) {
lt_log_print(torrent::LOG_ERROR, "trackers.use_udp.set is no longer supported", 0);
})
auto dht_manager = control->dht_manager();
@@ -146,8 +143,8 @@ initialize_command_tracker() {
CMD2_ANY_VALUE_V ("dht.port.set", [](auto, auto) {
lt_log_print(torrent::LOG_DHT_ERROR, "dht.port.set is no longer supported, use dht.override_port.set", 0);
});
CMD2_ANY ("dht.override_port", std::bind(&torrent::net::NetworkConfig::override_dht_port, torrent::config::network_config()));
CMD2_ANY_VALUE_V ("dht.override_port.set", std::bind(&torrent::net::NetworkConfig::set_override_dht_port, torrent::config::network_config(), std::placeholders::_2));
CMD2_ANY ("dht.override_port", std::bind(&torrent::runtime::NetworkConfig::override_dht_port, torrent::runtime::network_config()));
CMD2_ANY_VALUE_V ("dht.override_port.set", std::bind(&torrent::runtime::NetworkConfig::set_override_dht_port, torrent::runtime::network_config(), std::placeholders::_2));
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));
+22 -2
View File
@@ -4,7 +4,6 @@
#include <ctime>
#include <regex>
#include <rak/algorithm.h>
#include <torrent/utils/log.h>
#include "core/manager.h"
@@ -730,6 +729,26 @@ apply_arith_count(const torrent::Object::list_type& args) {
return val;
}
// Get the median of an unordered set of numbers of arbitrary
// type by modifing the underlying dataset
template <typename T = double, typename _InputIter>
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;
}
int64_t
apply_arith_other(const char* op, const torrent::Object::list_type& args) {
if (args.size() == 0)
@@ -739,7 +758,8 @@ apply_arith_other(const char* op, const torrent::Object::list_type& args) {
return (int64_t)(apply_math_basic(op, std::plus<int64_t>(), args) / apply_arith_count(args));
} else if (strcmp(op, "median") == 0) {
std::vector<int64_t> result = as_vector(args);
return (int64_t)rak::median(result.begin(), result.end());
return (int64_t)median(result.begin(), result.end());
} else {
throw torrent::input_error("Wrong operation supplied to apply_arith_other.");
}
+6 -3
View File
@@ -27,6 +27,7 @@
#include "rpc/object_storage.h"
#include "session/session_manager.h"
#include "ui/root.h"
#include "utils/watch_ready_queue.h"
Control::Control()
: m_ui(new ui::Root()),
@@ -36,7 +37,8 @@ Control::Control()
m_commandScheduler(new rpc::CommandScheduler()),
m_objectStorage(new rpc::object_storage()),
m_lua_engine(new rpc::LuaEngine()),
m_directory_events(new torrent::directory_events()) {
m_directory_events(new torrent::directory_events()),
m_watch_ready_queue(new utils::WatchReadyQueue()) {
m_core = std::make_unique<core::Manager>();
m_view_manager = std::make_unique<core::ViewManager>();
@@ -121,11 +123,13 @@ Control::is_shutdown_completed() {
if (torrent::net_thread::http_stack()->size() != 0 || !core()->http_queue()->empty())
return false;
return torrent::is_inactive();
return core()->is_download_shutdown_completed();
}
void
Control::handle_shutdown() {
m_watch_ready_queue->shutdown();
rpc::commands.call_catch("event.system.shutdown", rpc::make_target(), "shutdown", "System shutdown event action failed: ");
if (scgi_thread::thread()->is_active())
@@ -147,4 +151,3 @@ Control::handle_shutdown() {
m_shutdownQuick = true;
m_shutdownReceived = false;
}
+6
View File
@@ -38,6 +38,10 @@ namespace torrent {
class directory_events;
}
namespace utils {
class WatchReadyQueue;
}
class Control {
public:
Control();
@@ -70,6 +74,7 @@ public:
rpc::LuaEngine* lua_engine() { return m_lua_engine.get(); }
torrent::directory_events* directory_events() { return m_directory_events.get(); }
utils::WatchReadyQueue* watch_ready_queue() { return m_watch_ready_queue.get(); }
uint64_t tick() const { return m_tick; }
void inc_tick() { m_tick++; }
@@ -94,6 +99,7 @@ private:
std::unique_ptr<rpc::object_storage> m_objectStorage;
std::unique_ptr<rpc::LuaEngine> m_lua_engine;
std::unique_ptr<torrent::directory_events> m_directory_events;
std::unique_ptr<utils::WatchReadyQueue> m_watch_ready_queue;
uint64_t m_tick{};
+1 -16
View File
@@ -32,21 +32,6 @@ Download::~Download() {
m_download = download_type();
}
void
Download::enable_udp_trackers(bool state) {
for (int idx = 0, end = m_download.tracker_controller().size(); idx < end; ++idx) {
auto tracker = m_download.tracker_controller().at(idx);
if (tracker.type() != torrent::TRACKER_UDP)
continue;
if (state)
tracker.enable();
else
tracker.disable();
}
}
uint32_t
Download::priority() {
return bencode()->get_key("rtorrent").get_key_value("priority");
@@ -131,7 +116,7 @@ Download::set_root_directory(const std::string& path) {
(file_list->is_multi_file() ?
!file_list->is_root_dir_created() :
!file_stat.update(file_list->front()->frozen_path()))) {
!file_stat.update(file_list->front()->frozen_path().str()))) {
set_message("Cannot change the directory of an open download after the files have been moved.");
rpc::call_command("d.state.set", (int64_t)0, rpc::make_target(this));
-2
View File
@@ -70,8 +70,6 @@ public:
const std::string& message() const { return m_message; }
void set_message(const std::string& msg) { m_message = msg; }
void enable_udp_trackers(bool state);
uint32_t priority();
void set_priority(uint32_t p);
+4 -6
View File
@@ -15,6 +15,7 @@
#include <torrent/rate.h>
#include <torrent/data/file_utils.h>
#include <torrent/net/http_stack.h>
#include <torrent/utils/string_manip.h>
#include "control.h"
#include "globals.h"
@@ -108,8 +109,8 @@ DownloadFactory::receive_load() {
HttpQueue::iterator itr = m_manager->http_queue()->insert(m_uri, m_stream);
itr->add_done_slot([this]() { receive_loaded(); });
itr->add_failed_slot([this](const std::string& error) { receive_failed(error); });
itr->add_done_slot(torrent::this_thread::thread(), [this]() { receive_loaded(); });
itr->add_failed_slot(torrent::this_thread::thread(), [this](const std::string& error) { receive_failed(error); });
m_variables["tied_to_file"] = (int64_t)false;
@@ -237,9 +238,6 @@ DownloadFactory::receive_success() {
rpc::call_command("d.peers_max.set", rpc::call_command("throttle.max_peers.seed"), rpc::make_target(download));
}
if (!rpc::call_command_value("trackers.use_udp"))
download->enable_udp_trackers(false);
// Skip forcing trackers to scrape when rtorrent starts
if (m_initLoad && rpc::call_command_value("trackers.delay_scrape"))
download->set_resume_flags(torrent::Download::start_skip_tracker);
@@ -327,7 +325,7 @@ void
DownloadFactory::log_created(Download* download, torrent::Object* rtorrent) {
std::stringstream dump;
dump << "info_hash = " << torrent::hash_string_to_hex_str(download->info()->hash()) << std::endl;
dump << "info_hash = " << torrent::utils::transform_to_hex_str(download->info()->hash()) << std::endl;
dump << "session = " << (m_session ? "true" : "false") << std::endl;
if (download->download()->info()->is_meta_download())
+13 -4
View File
@@ -1,9 +1,9 @@
#include "config.h"
#include <algorithm>
#include <cstring>
#include <fstream>
#include <iostream>
#include <rak/string_manip.h>
#include <torrent/data/file.h>
#include <torrent/utils/resume.h>
#include <torrent/exceptions.h>
@@ -13,6 +13,7 @@
#include <torrent/object_stream.h>
#include <torrent/torrent.h>
#include <torrent/utils/log.h>
#include <torrent/utils/string_manip.h>
#include "rpc/parse_commands.h"
@@ -82,10 +83,13 @@ DownloadList::find(const torrent::HashString& hash) {
DownloadList::iterator
DownloadList::find_hex(const char* hash) {
if (strlen(hash) < 40)
return end();
torrent::HashString key;
for (torrent::HashString::iterator itr = key.begin(), last = key.end(); itr != last; itr++, hash += 2)
*itr = (rak::hexchar_to_value(*hash) << 4) + rak::hexchar_to_value(*(hash + 1));
if (torrent::utils::transform_from_hex(hash, hash + 40, key) != key.end())
return end();
return std::find_if(begin(), end(), [key](Download* d) { return key == d->info()->hash(); });
}
@@ -679,8 +683,10 @@ DownloadList::process_meta_download(Download* download) {
rpc::call_command("d.stop", torrent::Object(), rpc::make_target(download));
rpc::call_command("d.close", torrent::Object(), rpc::make_target(download));
std::string metafile = (*download->file_list()->begin())->frozen_path();
std::string metafile = (*download->file_list()->begin())->frozen_path().str();
std::fstream file(metafile.c_str(), std::ios::in | std::ios::binary);
if (!file.is_open()) {
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not read download metadata.");
return;
@@ -688,11 +694,13 @@ DownloadList::process_meta_download(Download* download) {
torrent::Object* bencode = new torrent::Object(torrent::Object::create_map());
file >> bencode->insert_key("info", torrent::Object());
if (file.fail()) {
delete bencode;
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download, the input is not a valid torrent.");
return;
}
file.close();
// Steal the keys we still need. The old download has no use for them.
@@ -703,6 +711,7 @@ DownloadList::process_meta_download(Download* download) {
bencode->insert_key("announce-list", torrent::Object()).swap(download->bencode()->get_key("announce-list"));
erase_ptr(download);
control->core()->try_create_download_from_meta_download(bencode, metafile);
}
+2 -2
View File
@@ -15,8 +15,8 @@ HttpQueue::insert(const std::string& url, std::shared_ptr<std::ostream> stream)
for (auto& slot : m_signal_insert)
slot(*itr);
itr->add_done_slot([this, itr]() { erase(itr); });
itr->add_failed_slot([this, itr](auto) { erase(itr); });
itr->add_done_slot(torrent::this_thread::thread(), [this, itr]() { erase(itr); });
itr->add_failed_slot(torrent::this_thread::thread(), [this, itr](auto) { erase(itr); });
// TODO: Downloading http torrents doesn't seem to work.
// TODO: Quitting no longer works.
+88 -21
View File
@@ -8,21 +8,19 @@
#include <sstream>
#include <unistd.h>
#include <sys/select.h>
#include <rak/address_info.h>
#include <rak/regex.h>
#include <rak/string_manip.h>
#include <torrent/utils/resume.h>
#include <torrent/object.h>
#include <torrent/connection_manager.h>
#include <torrent/error.h>
#include <torrent/exceptions.h>
#include <torrent/object_stream.h>
#include <torrent/throttle.h>
#include <torrent/net/http_stack.h>
#include <torrent/net/network_config.h>
#include <torrent/net/socket_address.h>
#include <torrent/runtime/network_config.h>
#include <torrent/runtime/network_manager.h>
#include <torrent/utils/log.h>
#include <torrent/utils/string_manip.h>
#include "rpc/parse_commands.h"
#include "utils/directory.h"
@@ -66,6 +64,20 @@ Manager::~Manager() {
torrent::Throttle::destroy_throttle(m_throttles["NULL"].first);
}
bool
Manager::is_download_shutdown_completed() {
for (const auto& download : *download_list()) {
// TODO: Why is this being checked?
// if (download->tracker_controller().is_active())
// return false;
if (download->tracker_controller().has_active_trackers_not_dht_scrape_disownable())
return false;
}
return true;
}
void
Manager::set_hashing_view(View* v) {
if (v == nullptr || m_hashingView != nullptr)
@@ -199,7 +211,7 @@ Manager::listen_open() {
void
Manager::set_proxy_address(const std::string& addr) {
int port;
rak::address_info* ai;
torrent::sa_unique_ptr sa;
std::string buf(addr.length() + 1, '\0');
@@ -211,20 +223,18 @@ Manager::set_proxy_address(const std::string& addr) {
if (err == 1)
port = 80;
if ((err = rak::address_info::get_address_info(buf.data(), PF_INET, SOCK_STREAM, &ai)) != 0)
throw torrent::input_error("Could not set proxy address: " + std::string(rak::address_info::strerror(err)) + ".");
try {
auto sa = torrent::sa_copy(ai->c_addrinfo()->ai_addr);
torrent::sap_set_port(sa, port);
torrent::config::network_config()->set_proxy_address(sa.get());
rak::address_info::free_address_info(ai);
sa = torrent::sa_copy(torrent::sa_lookup_address(buf, AF_INET).get());
} catch (torrent::input_error& e) {
throw torrent::input_error("Could not resolve proxy address: " + std::string(e.what()));
}
try {
torrent::sa_set_port(sa.get(), port);
torrent::runtime::network_config()->set_proxy_address(sa.get());
} catch (torrent::input_error& e) {
rak::address_info::free_address_info(ai);
throw e;
}
}
@@ -241,9 +251,10 @@ is_data_uri(const std::string& uri) {
std::string
decode_data_uri(const std::string& uri) {
const auto start = uri.find("base64,", 5) + 7;
if (start == std::string::npos)
const auto pos = uri.find("base64,", 5);
if (pos == std::string::npos)
throw torrent::input_error("Invalid data uri: not base64 encoded.");
const auto start = pos + 7;
if (start >= uri.size())
throw torrent::input_error("Empty base64.");
return utils::decode_base64(uri.substr(start));
@@ -314,14 +325,70 @@ path_expand_transform(std::string path, const utils::directory_entry& entry) {
return path + entry.s_name;
}
namespace {
// Consider rewritting such that m_seq is replaced by first/last.
template <typename Sequence>
class split_iterator_t {
public:
typedef typename Sequence::const_iterator const_iterator;
typedef typename Sequence::value_type value_type;
split_iterator_t() {}
split_iterator_t(const Sequence& seq, value_type delim) :
m_seq(&seq),
m_delim(delim),
m_pos(seq.begin()),
m_next(std::find(seq.begin(), seq.end(), delim)) {
}
Sequence operator * () { return Sequence(m_pos, m_next); }
split_iterator_t& operator ++ () {
m_pos = m_next;
if (m_pos == m_seq->end())
return *this;
m_pos++;
m_next = std::find(m_pos, m_seq->end(), m_delim);
return *this;
}
bool operator == (const split_iterator_t&) const { return m_pos == m_seq->end(); }
bool operator != (const split_iterator_t&) const { return m_pos != m_seq->end(); }
private:
const Sequence* m_seq;
value_type m_delim;
const_iterator m_pos;
const_iterator m_next;
};
template <typename Sequence>
inline split_iterator_t<Sequence>
split_iterator(const Sequence& seq, typename Sequence::value_type delim) {
return split_iterator_t<Sequence>(seq, delim);
}
template <typename Sequence>
inline split_iterator_t<Sequence>
split_iterator(const Sequence&) {
return split_iterator_t<Sequence>();
}
}
// Move this somewhere better.
void
path_expand(std::vector<std::string>* paths, const std::string& pattern) {
std::vector<utils::Directory> currentCache;
std::vector<utils::Directory> nextCache;
rak::split_iterator_t<std::string> first = rak::split_iterator(pattern, '/');
rak::split_iterator_t<std::string> last = rak::split_iterator(pattern);
auto first = split_iterator(pattern, '/');
auto last = split_iterator(pattern);
if (first == last)
return;
@@ -330,7 +397,7 @@ path_expand(std::vector<std::string>* paths, const std::string& pattern) {
if ((*first).empty()) {
currentCache.push_back(utils::Directory("/"));
++first;
} else if (rak::trim(*first) == "~") {
} else if (torrent::utils::trim_spaces_str(*first) == "~") {
currentCache.push_back(utils::Directory("~"));
++first;
} else {
+2
View File
@@ -35,6 +35,8 @@ public:
Manager();
~Manager();
bool is_download_shutdown_completed();
DownloadList* download_list() { return m_download_list.get(); }
FileStatusCache* file_status_cache() { return m_file_status_cache.get(); }
+2 -1
View File
@@ -35,7 +35,8 @@ ViewManager::insert(const std::string& name) {
View* view = new View();
view->initialize(name);
return base_type::insert(end(), view);
base_type::push_back(view);
return --end();
}
ViewManager::iterator
+4 -4
View File
@@ -2,16 +2,16 @@
#define RTORRENT_CORE_VIEW_MANAGER_H
#include <string>
#include <rak/unordered_vector.h>
#include <torrent/utils/unordered_vector.h>
#include "view.h"
namespace core {
class ViewManager : public rak::unordered_vector<View*> {
class ViewManager : public torrent::utils::unordered_vector<View*> {
public:
typedef rak::unordered_vector<View*> base_type;
typedef std::list<std::string> filter_args;
typedef torrent::utils::unordered_vector<View*> base_type;
typedef std::list<std::string> filter_args;
using base_type::iterator;
using base_type::const_iterator;
+3 -1
View File
@@ -4,7 +4,9 @@
#include <string>
#include <vector>
#if defined(HAVE_NCURSESW_CURSES_H)
#if defined(HAVE_NO_NCURSES)
#include "curses_stub.h"
#elif defined(HAVE_NCURSESW_CURSES_H)
#include <ncursesw/curses.h>
#elif defined(HAVE_NCURSESW_H)
#include <ncursesw.h>
+4
View File
@@ -4,7 +4,11 @@
#include <array>
#include <map>
#if defined(HAVE_NO_NCURSES)
#include "curses_stub.h"
#else
#include <curses.h>
#endif
namespace display {
+6
View File
@@ -0,0 +1,6 @@
// Dummy ncurses stub: provides the stdscr symbol expected by the codebase.
#include "curses_stub.h"
// Define stdscr as a null pointer; all ncurses calls are no-ops in this stub.
WINDOW* stdscr = nullptr;
+108
View File
@@ -0,0 +1,108 @@
// Dummy ncurses stub header for building rtorrent without ncurses.
// Provides types, macros, and no-op function stubs so the codebase
// compiles without a real curses library. All display functions become
// no-ops; use with system.daemon.set = true.
#ifndef RTORRENT_DISPLAY_CURSES_STUB_H
#define RTORRENT_DISPLAY_CURSES_STUB_H
#include <cstdarg>
#include <cstdio>
#include <cstddef>
// Opaque window type
typedef void* WINDOW;
// Character type used by curses
typedef unsigned int chtype;
typedef unsigned int attr_t;
// The standard screen
extern WINDOW* stdscr;
// Boolean constants and return codes
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef ERR
#define ERR (-1)
#endif
// Attribute constants
#define A_NORMAL 0
#define A_STANDOUT (1U << 8)
#define A_UNDERLINE (1U << 9)
#define A_REVERSE (1U << 10)
#define A_BLINK (1U << 11)
#define A_DIM (1U << 12)
#define A_BOLD (1U << 13)
// Color support
#define COLORS 0
#define COLOR_BLACK 0
#define COLOR_RED 1
#define COLOR_GREEN 2
#define COLOR_YELLOW 3
#define COLOR_BLUE 4
#define COLOR_MAGENTA 5
#define COLOR_CYAN 6
#define COLOR_WHITE 7
#define COLOR_PAIR(n) 0
// Key constants
#define KEY_DOWN 0x102
#define KEY_UP 0x103
#define KEY_LEFT 0x104
#define KEY_RIGHT 0x105
#define KEY_HOME 0x106
#define KEY_BACKSPACE 0x107
#define KEY_DC 0x14a
#define KEY_ENTER 0x157
#define KEY_NPAGE 0x152
#define KEY_PPAGE 0x153
#define KEY_END 0x168
#define KEY_MOUSE 0x199
#define KEY_RESIZE 0x19a
// Stubbed functions
inline WINDOW* initscr() { return nullptr; }
inline int endwin() { return 0; }
inline WINDOW* newwin(int, int, int, int) { return nullptr; }
inline int delwin(WINDOW*) { return 0; }
inline int wresize(WINDOW*, int, int) { return 0; }
inline int mvwin(WINDOW*, int, int) { return 0; }
inline int wnoutrefresh(WINDOW*) { return 0; }
inline int redrawwin(WINDOW*) { return 0; }
inline int werase(WINDOW*) { return 0; }
inline int wmove(WINDOW*, int, int) { return 0; }
inline int vw_printw(WINDOW*, char*, va_list) { return 0; }
inline int waddch(WINDOW*, const chtype) { return 0; }
inline int mvwaddch(WINDOW*, int, int, const chtype) { return 0; }
inline int mvwchgat(WINDOW*, int, int, int, attr_t, short, const void*) { return 0; }
inline int wattrset(WINDOW*, int) { return 0; }
inline int wattr_get(WINDOW*, attr_t*, short*, void*) { return 0; }
inline int wattr_set(WINDOW*, attr_t, short, void*) { return 0; }
inline int doupdate() { return 0; }
inline int start_color() { return 0; }
inline int use_default_colors() { return 0; }
inline int raw() { return 0; }
inline int noraw() { return 0; }
inline int noecho() { return 0; }
inline int nodelay(WINDOW*, bool) { return 0; }
inline int keypad(WINDOW*, bool) { return 0; }
inline int curs_set(int) { return 0; }
inline int init_pair(short, short, short) { return 0; }
inline int pair_content(short, short*, short*) { return 0; }
inline int resizeterm(int, int) { return 0; }
inline int getch() { return ERR; }
inline int set_escdelay(int) { return 0; }
// getyx / getmaxyx are macros in real ncurses; stub them as no-ops
#define getyx(w, y, x) do { (y) = 0; (x) = 0; } while (0)
#define getmaxyx(w, y, x) do { (y) = 24; (x) = 80; } while (0)
#endif // RTORRENT_DISPLAY_CURSES_STUB_H
+13 -46
View File
@@ -1,43 +1,10 @@
// 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 <sundell.software@gmail.com>
#include "config.h"
#include <rak/string_manip.h>
#include "display/text_element_string.h"
#include <torrent/utils/string_manip.h>
#include "rpc/parse_commands.h"
#include "text_element_string.h"
namespace display {
@@ -53,17 +20,17 @@ TextElementStringBase::print(char* first, char* last, Canvas::attributes_list* a
auto buffer = std::make_unique<char[]>(last - first);
char* bufferLast = copy_string(buffer.get(), buffer.get() + (last - first), target);
first = rak::transform_hex(buffer.get(), bufferLast, first, last);
first = torrent::utils::transform_to_hex(buffer.get(), bufferLast, first, last);
} else if (m_flags & flag_escape_html) {
auto buffer = std::make_unique<char[]>(last - first);
char* bufferLast = copy_string(buffer.get(), buffer.get() + (last - first), target);
first = rak::copy_escape_html(buffer.get(), bufferLast, first, last);
first = torrent::utils::copy_escape_html(buffer.get(), bufferLast, first, last);
} else {
first = copy_string(first, last, target);
}
}
push_attribute(attributes, Attributes(first, baseAttribute));
@@ -71,16 +38,16 @@ TextElementStringBase::print(char* first, char* last, Canvas::attributes_list* a
}
char*
TextElementString::copy_string(char* first, char* last, rpc::target_type target) {
TextElementString::copy_string(char* first, char* last, [[maybe_unused]] rpc::target_type target) {
extent_type length = std::min<extent_type>(last - first, m_string.size());
std::memcpy(first, m_string.c_str(), length);
return first + length;
}
char*
TextElementCString::copy_string(char* first, char* last, rpc::target_type target) {
TextElementCString::copy_string(char* first, char* last, [[maybe_unused]] rpc::target_type target) {
extent_type length = std::min<extent_type>(last - first, m_length);
std::memcpy(first, m_string, length);
@@ -104,22 +71,22 @@ TextElementCommand::print(char* first, char* last, Canvas::attributes_list* attr
const std::string& str = result.as_string();
if (m_flags & flag_escape_hex) {
first = rak::transform_hex(str.c_str(), str.c_str() + str.size(), first, last);
first = torrent::utils::transform_to_hex(str.c_str(), str.c_str() + str.size(), first, last);
} else if (m_flags & flag_escape_html) {
first = rak::copy_escape_html(str.c_str(), str.c_str() + str.size(), first, last);
first = torrent::utils::copy_escape_html(str.c_str(), str.c_str() + str.size(), first, last);
} else {
size_t length = std::min<size_t>(str.size(), std::distance(first, last));
std::memcpy(first, str.c_str(), length);
first += std::min<size_t>(str.size(), length);
}
}
break;
}
case torrent::Object::TYPE_VALUE:
{
{
first += std::min<ptrdiff_t>(std::max(snprintf(first, last - first + 1, "%lld", (long long int)result.as_value()), 0), last - first + 1);
break;
}
-34
View File
@@ -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 <sundell.software@gmail.com>
#ifndef RTORRENT_DISPLAY_TEXT_ELEMENT_STRING_H
#define RTORRENT_DISPLAY_TEXT_ELEMENT_STRING_H
+10 -9
View File
@@ -7,7 +7,6 @@
#include <sstream>
#include <iomanip>
#include <torrent/exceptions.h>
#include <torrent/connection_manager.h>
#include <torrent/rate.h>
#include <torrent/throttle.h>
#include <torrent/torrent.h>
@@ -16,9 +15,11 @@
#include <torrent/data/file_manager.h>
#include <torrent/download/resource_manager.h>
#include <torrent/net/http_stack.h>
#include <torrent/net/network_config.h>
#include <torrent/net/socket_address.h>
#include <torrent/peer/client_info.h>
#include <torrent/runtime/network_config.h>
#include <torrent/runtime/runtime.h>
#include <torrent/runtime/socket_manager.h>
#include "control.h"
#include "globals.h"
@@ -154,7 +155,7 @@ print_download_status(char* first, char* last, core::Download* d) {
} else if (d->tracker_controller().has_active_trackers_not_scrape()) {
auto tracker = d->tracker_controller().find_if([](const auto& t) {
return t.is_busy_not_scrape();
return t.is_requesting_not_scrape();
});
if (tracker.is_valid()) {
@@ -376,7 +377,7 @@ print_status_info(char* first, char* last) {
first = print_buffer(first, last, " KB]");
first = print_buffer(first, last, " [Port: %i]", (unsigned int)torrent::runtime::listen_port());
auto local_address = torrent::config::network_config()->local_address_best_match();
auto local_address = torrent::runtime::network_config()->local_address_best_match();
if (!torrent::sa_is_any(local_address.get())) {
first = print_buffer(first, last, " [Local ");
@@ -387,8 +388,8 @@ print_status_info(char* first, char* last) {
if (first > last)
throw torrent::internal_error("print_status_info(...) wrote past end of the buffer.");
auto bind_inet_address = torrent::config::network_config()->bind_inet_address();
auto bind_inet6_address = torrent::config::network_config()->bind_inet6_address();
auto bind_inet_address = torrent::runtime::network_config()->bind_inet_address();
auto bind_inet6_address = torrent::runtime::network_config()->bind_inet6_address();
bool show_bind_inet = !torrent::sa_is_any(bind_inet_address.get());
bool show_bind_inet6 = !torrent::sa_is_any(bind_inet6_address.get());
@@ -426,9 +427,9 @@ print_status_extra(char* first, char* last) {
torrent::net_thread::http_stack()->max_total_connections());
first = print_buffer(first, last, " [S %i/%i/%i]",
torrent::total_handshakes(),
torrent::connection_manager()->size(),
torrent::connection_manager()->max_size());
torrent::runtime::total_handshakes(),
torrent::runtime::socket_manager()->size(),
torrent::runtime::socket_manager()->max_size());
first = print_buffer(first, last, " [F %i/%i]",
torrent::file_manager()->open_files(),
+58 -1
View File
@@ -3,7 +3,7 @@
#include <functional>
#include <torrent/utils/scheduler.h>
#include <torrent/utils/thread.h>
#include <torrent/system/thread.h>
#include "canvas.h"
#include "globals.h"
@@ -102,6 +102,63 @@ protected:
torrent::utils::SchedulerEntry m_task_update;
};
// Return a range with a distance of no more than __distance and
// between __first and __last, centered on __middle1.
template <typename _InputIter, typename _Distance>
std::pair<_InputIter, _InputIter>
advance_bidirectional(_InputIter __first, _InputIter __middle1, _InputIter __last, _Distance __distance) {
_InputIter __middle2 = __middle1;
do {
if (!__distance)
break;
if (__middle2 != __last) {
++__middle2;
--__distance;
} else if (__middle1 == __first) {
break;
}
if (!__distance)
break;
if (__middle1 != __first) {
--__middle1;
--__distance;
} else if (__middle2 == __last) {
break;
}
} while (true);
return std::make_pair(__middle1, __middle2);
}
template <typename _InputIter, typename _Distance>
_InputIter
advance_forward(_InputIter __first, _InputIter __last, _Distance __distance) {
while (__first != __last && __distance != 0) {
__first++;
__distance--;
}
return __first;
}
template <typename _InputIter, typename _Distance>
_InputIter
advance_backward(_InputIter __first, _InputIter __last, _Distance __distance) {
while (__first != __last && __distance != 0) {
__first--;
__distance--;
}
return __first;
}
}
#endif
+4 -4
View File
@@ -1,17 +1,17 @@
#include "config.h"
#include "display/window_download_chunks_seen.h"
#include <cmath>
#include <stdexcept>
#include <rak/string_manip.h>
#include <torrent/bitfield.h>
#include <torrent/data/block.h>
#include <torrent/data/block_list.h>
#include <torrent/data/transfer_list.h>
#include <torrent/utils/string_manip.h>
#include "core/download.h"
#include "window_download_chunks_seen.h"
namespace display {
WindowDownloadChunksSeen::WindowDownloadChunksSeen(core::Download* d, unsigned int *focus) :
@@ -82,7 +82,7 @@ WindowDownloadChunksSeen::redraw() {
attr = A_BOLD;
}
m_canvas->print_char(attr | rak::value_to_hexchar<0>(std::min<uint8_t>(*chunk, 0xF)));
m_canvas->print_char(attr | torrent::utils::value_to_hex0(std::min<uint8_t>(*chunk, 0xF)));
chunk++;
if ((chunk - seen) % 10 == 0) {
+8 -10
View File
@@ -1,17 +1,15 @@
#include "config.h"
#include "display/color_map.h"
#include <rak/algorithm.h>
#include "globals.h"
#include "core/download.h"
#include "core/view.h"
#include "display/canvas.h"
#include "display/utils.h"
#include "display/window_download_list.h"
#include "rpc/parse_commands.h"
#include "canvas.h"
#include "globals.h"
#include "utils.h"
#include "window_download_list.h"
namespace display {
WindowDownloadList::WindowDownloadList() :
@@ -104,10 +102,10 @@ WindowDownloadList::redraw() {
typedef std::pair<core::View::iterator, core::View::iterator> Range;
Range range = rak::advance_bidirectional(m_view->begin_visible(),
m_view->focus() != m_view->end_visible() ? m_view->focus() : m_view->begin_visible(),
m_view->end_visible(),
page_size(layout_name));
Range range = advance_bidirectional(m_view->begin_visible(),
m_view->focus() != m_view->end_visible() ? m_view->focus() : m_view->begin_visible(),
m_view->end_visible(),
page_size(layout_name));
// Make sure we properly fill out the last lines so it looks like
// there are more torrents, yet don't hide it if we got the last one
@@ -2,7 +2,6 @@
#include <cmath>
#include <stdexcept>
#include <rak/string_manip.h>
#include <torrent/data/block_list.h>
#include <torrent/data/transfer_list.h>
+11 -4
View File
@@ -135,8 +135,11 @@ WindowFileList::redraw() {
m_canvas->print(0, pos, "%*c%-*s", 16, ' ', filenameWidth, "EMPTY");
} else if (itr.is_entering()) {
m_canvas->print(0, pos, "%*c %ls", 16 + itr.depth(), '\\',
itr.depth() < itr.file()->path()->size() ? wstring_width(itr.file()->path()->at(itr.depth()), filenameWidth - itr.depth() - 1).c_str() : L"UNKNOWN");
if (itr.depth() < itr.file()->path()->size())
m_canvas->print(0, pos, "%*c %ls", 16 + itr.depth(), '\\',
wstring_width(itr.file()->path()->at(itr.depth()).str(), filenameWidth - itr.depth() - 1).c_str());
else
m_canvas->print(0, pos, "%*c %ls", 16 + itr.depth(), '\\', L"UNKNOWN");
} else if (itr.is_leaving()) {
m_canvas->print(0, pos, "%*c %-*s", 16 + (itr.depth() - 1), '/', filenameWidth - (itr.depth() - 1), "");
@@ -166,8 +169,12 @@ WindowFileList::redraw() {
else
m_canvas->print(8, pos, "%5.1f T", (double)val / (int64_t(1) << 40));
m_canvas->print(15, pos, "%*c %ls", 1 + itr.depth(), '|',
itr.depth() < itr.file()->path()->size() ? wstring_width(itr.file()->path()->at(itr.depth()), filenameWidth - itr.depth() - 1).c_str() : L"UNKNOWN");
if (itr.depth() < itr.file()->path()->size())
m_canvas->print(15, pos, "%*c %ls", 1 + itr.depth(), '|',
wstring_width(itr.file()->path()->at(itr.depth()).str(), filenameWidth - itr.depth() - 1).c_str());
else
m_canvas->print(15, pos, "%*c %ls", 1 + itr.depth(), '|', L"UNKNOWN");
} else {
m_canvas->print(0, pos, "BORK BORK");
+12 -8
View File
@@ -2,7 +2,8 @@
#include <ctime>
#include <torrent/torrent.h>
#include <torrent/utils/thread.h>
#include <torrent/system/callbacks.h>
#include <torrent/system/thread.h>
#include "canvas.h"
#include "utils.h"
@@ -14,13 +15,14 @@ WindowLog::WindowLog(torrent::log_buffer* l) :
Window(new Canvas, 0, 0, 0, extent_full, extent_static),
m_log(l) {
m_task_update.slot() = std::bind(&WindowLog::receive_update, this);
m_task_update.slot() = [this]() { receive_update(); };
unsigned int signal_index = torrent::main_thread::thread()->signal_bitfield()->add_signal(std::bind(&WindowLog::receive_update, this));
m_log->lock_and_set_update_slot([this]() {
if (m_log_updating.exchange(true) == true)
return;
m_log->lock_and_set_update_slot([signal_index]() {
torrent::main_thread::thread()->send_event_signal(signal_index, false);
});
torrent::main_thread::callback([this]() { receive_update(); });
});
}
WindowLog::~WindowLog() {
@@ -52,11 +54,13 @@ WindowLog::redraw() {
// gets updated.
void
WindowLog::receive_update() {
m_log_updating = false;
if (!is_active())
return;
iterator itr = find_older();
extent_type height = std::min(std::distance(itr, (iterator)m_log->end()), (std::iterator_traits<iterator>::difference_type)10);
auto itr = find_older();
auto height = std::min(std::distance(itr, (iterator)m_log->end()), (std::iterator_traits<iterator>::difference_type)10);
if (height != m_max_height) {
m_min_height = height != 0 ? 1 : 0;
+2
View File
@@ -24,6 +24,8 @@ private:
torrent::log_buffer* m_log;
torrent::utils::SchedulerEntry m_task_update;
align_cacheline std::atomic<bool> m_log_updating{};
};
}
+8 -9
View File
@@ -1,5 +1,7 @@
#include "config.h"
#include "display/window_peer_list.h"
#include <stdexcept>
#include <torrent/rate.h>
#include <torrent/data/block_transfer.h>
@@ -9,11 +11,8 @@
#include <torrent/peer/peer_info.h>
#include "core/download.h"
#include "rak/algorithm.h"
#include "canvas.h"
#include "utils.h"
#include "window_peer_list.h"
#include "display/canvas.h"
#include "display/utils.h"
namespace display {
@@ -50,10 +49,10 @@ WindowPeerList::redraw() {
typedef std::pair<PList::iterator, PList::iterator> Range;
Range range = rak::advance_bidirectional(m_list->begin(),
*m_focus != m_list->end() ? *m_focus : m_list->begin(),
m_list->end(),
m_canvas->height() - y);
Range range = advance_bidirectional(m_list->begin(),
*m_focus != m_list->end() ? *m_focus : m_list->begin(),
m_list->end(),
m_canvas->height() - y);
if (m_download->download()->file_list()->size_chunks() <= 0)
throw std::logic_error("WindowPeerList::redraw() m_slotChunksTotal() returned invalid value");
+6 -7
View File
@@ -2,10 +2,9 @@
#include "window_tracker_list.h"
#include <rak/algorithm.h>
#include <rak/string_manip.h>
#include <torrent/exceptions.h>
#include <torrent/tracker/tracker.h>
#include <torrent/utils/string_manip.h>
#include "core/download.h"
@@ -37,7 +36,7 @@ WindowTrackerList::redraw() {
if (tc_size == 0 || *m_focus >= tc_size)
return;
auto range = rak::advance_bidirectional<unsigned int>(0, *m_focus, tc_size, (m_canvas->height() - 1) / 2);
auto range = advance_bidirectional<unsigned int>(0, *m_focus, tc_size, (m_canvas->height() - 1) / 2);
auto group = tc.at(range.first).group();
while (range.first != range.second) {
@@ -52,9 +51,9 @@ WindowTrackerList::redraw() {
if (pos < m_canvas->height()) {
const char* state;
if (tracker.is_busy_not_scrape())
if (tracker.is_requesting_not_scrape())
state = "req ";
else if (tracker.is_busy())
else if (tracker.is_requesting())
state = "scr ";
else
state = " ";
@@ -63,7 +62,7 @@ WindowTrackerList::redraw() {
m_canvas->print(0, pos++, "%s Id: %s Counters: %uf / %us (%u) %s S/L/D: %u/%u/%u (%u/%u)",
state,
rak::copy_escape_html(tracker.tracker_id()).c_str(),
torrent::utils::copy_escape_html_str(tracker.tracker_id()).c_str(),
tracker_state.failed_counter(),
tracker_state.success_counter(),
tracker_state.scrape_counter(),
@@ -80,7 +79,7 @@ WindowTrackerList::redraw() {
m_canvas->set_attr(4, pos - 1, m_canvas->width(), is_focused() ? A_REVERSE : A_BOLD, COLOR_PAIR(0));
}
if (tracker.is_busy()) {
if (tracker.is_requesting()) {
m_canvas->set_attr(0, pos - 2, 4, A_REVERSE, COLOR_PAIR(0));
m_canvas->set_attr(0, pos - 1, 4, A_REVERSE, COLOR_PAIR(0));
}
+4 -3
View File
@@ -2,6 +2,7 @@
#include "globals.h"
#include <cstdlib>
#include <torrent/exceptions.h>
rpc::ip_table_list ip_tables;
@@ -14,13 +15,13 @@ expand_path(const std::string& path) {
return std::string();
if (path[0] == '~') {
if (path.size() < 2 || path[1] != '/')
throw torrent::input_error("Could not expand ~ in session path, only ~/<path> is supported.");
if (path.size() >= 2 && path[1] != '/')
throw torrent::input_error("Could not expand ~ in path, only '~' or '~/...' is supported.");
const char* home = std::getenv("HOME");
if (home == nullptr || *home == '\0')
throw torrent::input_error("Could not expand ~ in session path, HOME environment variable not set.");
throw torrent::input_error("Could not expand ~ in path, HOME environment variable not set.");
return home + path.substr(1);
}
+11 -13
View File
@@ -22,28 +22,26 @@ class SessionManager;
namespace scgi_thread {
torrent::utils::Thread* thread();
std::thread::id thread_id();
torrent::system::Thread* thread();
std::thread::id thread_id();
void callback(void* target, std::function<void ()>&& fn);
void cancel_callback(void* target);
void cancel_callback_and_wait(void* target);
void callback_interrupt(torrent::system::callback_id& id, std::function<void ()>&& fn);
rpc::SCgi* scgi();
void set_scgi(rpc::SCgi* scgi);
void set_rpc_log(const std::string& filename);
rpc::SCgi* scgi();
void set_scgi(rpc::SCgi* scgi);
void set_rpc_log(const std::string& filename);
} // namespace torrent::scgi_thread
namespace session_thread {
torrent::utils::Thread* thread();
std::thread::id thread_id();
torrent::system::Thread* thread();
std::thread::id thread_id();
void callback(void* target, std::function<void ()>&& fn);
void cancel_callback(void* target);
void cancel_callback_and_wait(void* target);
void callback(std::function<void ()>&& fn);
void callback(torrent::system::callback_id& id, std::function<void ()>&& fn);
void cancel_callback(torrent::system::callback_id& id);
session::SessionManager* manager();
std::string session_path();
+2 -2
View File
@@ -7,13 +7,13 @@
namespace input {
void
InputEvent::insert(torrent::net::Poll* p) {
InputEvent::insert(torrent::system::Poll* p) {
p->open(this);
p->insert_read(this);
}
void
InputEvent::remove(torrent::net::Poll* p) {
InputEvent::remove(torrent::system::Poll* p) {
p->remove_read(this);
p->close(this);
}
+3 -3
View File
@@ -4,7 +4,7 @@
#include <functional>
#include <torrent/event.h>
#include <torrent/net/poll.h>
#include <torrent/system/poll.h>
namespace input {
@@ -16,8 +16,8 @@ public:
const char* type_name() const override { return "input"; }
void insert(torrent::net::Poll* p);
void remove(torrent::net::Poll* p);
void insert(torrent::system::Poll* p);
void remove(torrent::system::Poll* p);
void event_read() override;
void event_write() override;
+48 -2
View File
@@ -1,7 +1,7 @@
#include "config.h"
#include <algorithm>
#include <functional>
#include <rak/algorithm.h>
#include <dirent.h>
#include <sys/stat.h>
@@ -10,6 +10,52 @@
#include "path_input.h"
namespace {
template <typename _Value>
struct compare_base {
bool operator () (const _Value& complete, const _Value& base) const {
return !complete.compare(0, base.size(), base);
}
};
// Count the number of elements from the start of the containers to
// the first inequal element.
template <typename _InputIter1, typename _InputIter2>
typename std::iterator_traits<_InputIter1>::difference_type
count_base(_InputIter1 __first1, _InputIter1 __last1,
_InputIter2 __first2, _InputIter2 __last2) {
typename std::iterator_traits<_InputIter1>::difference_type __n = 0;
for ( ;__first1 != __last1 && __first2 != __last2; ++__first1, ++__first2, ++__n)
if (*__first1 != *__first2)
return __n;
return __n;
}
template <typename _Return, typename _InputIter, typename _Ftor>
_Return
make_base(_InputIter __first, _InputIter __last, _Ftor __ftor) {
if (__first == __last)
return "";
_Return __base = __ftor(*__first++);
for ( ;__first != __last; ++__first) {
auto __pos = count_base(__base.begin(), __base.end(),
__ftor(*__first).begin(), __ftor(*__first).end());
if (__pos < (typename std::iterator_traits<_InputIter>::difference_type)__base.size())
__base.resize(__pos);
}
return __base;
}
}
namespace input {
bool
@@ -59,7 +105,7 @@ PathInput::receive_do_complete() {
if (r.first == r.second)
return; // Show some nice colors here.
std::string base = rak::make_base<std::string>(r.first, r.second, std::mem_fn(&utils::directory_entry::s_name));
std::string base = make_base<std::string>(r.first, r.second, [](auto& de) { return de.s_name; });
// Clear the path after the cursor to make this code cleaner. It's
// not really nessesary to add the complexity just because someone
+48 -110
View File
@@ -20,7 +20,6 @@
#include "core/dht_manager.h"
#include "core/download.h"
#include "core/download_factory.h"
#include "core/manager.h"
#include "display/canvas.h"
#include "display/window.h"
@@ -30,15 +29,15 @@
#include "rpc/command_scheduler_item.h"
#include "rpc/parse_commands.h"
#include "scgi/thread_scgi.h"
#include "session/download_storer.h"
#include "session/thread_session.h"
#include "session/session_manager.h"
#include "session/thread_session.h"
#include "ui/root.h"
#include "utils/directory.h"
#include "control.h"
#include "command_helpers.h"
#include "globals.h"
#include "setup.h"
#include "signal_handler.h"
#include "option_parser.h"
@@ -50,37 +49,6 @@ void do_panic(int signum);
void print_help();
void initialize_commands();
void do_nothing() {}
void do_nothing_str(const std::string&) {}
int
parse_options(int argc, char** argv) {
try {
OptionParser optionParser;
// Converted.
optionParser.insert_flag('h', std::bind(&print_help));
optionParser.insert_flag('n', std::bind(&do_nothing_str, std::placeholders::_1));
optionParser.insert_flag('D', std::bind(&do_nothing_str, std::placeholders::_1));
optionParser.insert_flag('I', std::bind(&do_nothing_str, std::placeholders::_1));
optionParser.insert_flag('K', std::bind(&do_nothing_str, std::placeholders::_1));
optionParser.insert_option('b', std::bind(&rpc::call_command_set_string, "network.bind_address.set", std::placeholders::_1));
optionParser.insert_option('d', std::bind(&rpc::call_command_set_string, "directory.default.set", std::placeholders::_1));
optionParser.insert_option('i', std::bind(&rpc::call_command_set_string, "ip", std::placeholders::_1));
optionParser.insert_option('p', std::bind(&rpc::call_command_set_string, "network.port_range.set", std::placeholders::_1));
optionParser.insert_option('s', std::bind(&rpc::call_command_set_string, "session", std::placeholders::_1));
optionParser.insert_option('O', std::bind(&rpc::parse_command_single_std, std::placeholders::_1));
optionParser.insert_option_list('o', std::bind(&rpc::call_command_set_std_string, std::placeholders::_1, std::placeholders::_2));
return optionParser.process(argc, argv);
} catch (torrent::input_error& e) {
throw torrent::input_error("Failed to parse command line option: " + std::string(e.what()));
}
}
void
initialize_rpc_slots() {
rpc::rpc.slot_find_download() = [](const char* hash) {
@@ -110,43 +78,6 @@ initialize_rpc_slots() {
};
}
void
load_session_torrents() {
utils::Directory entries = session::DownloadStorer::get_formated_entries(session_thread::manager()->path());
for (const auto& entry : entries) {
// We don't really support session torrents that are links. These
// would be overwritten anyway on exit, and thus not really be
// useful.
if (!entry.is_file())
continue;
core::DownloadFactory* f = new core::DownloadFactory(control->core());
// Replace with session torrent flag.
f->set_session(true);
f->set_init_load(true);
f->slot_finished([f](){ delete f; });
f->load(entries.path() + entry.s_name);
f->commit();
}
}
void
load_arg_torrents(char** first, char** last) {
//std::for_each(begin, end, std::bind1st(std::mem_fun(&core::Manager::insert), &control->get_core()));
for (; first != last; ++first) {
core::DownloadFactory* f = new core::DownloadFactory(control->core());
// Replace with session torrent flag.
f->set_start(true);
f->set_init_load(true);
f->slot_finished([f](){ delete f; });
f->load(*first);
f->commit();
}
}
static void
client_perform() {
// Use throw exclusively.
@@ -176,31 +107,39 @@ main(int argc, char** argv) {
srandom(random_seed);
srand48(random_seed);
torrent::initialize_main_thread();
torrent::log_initialize();
control = new Control;
// TODO: Create a fake thread object for initializing other processes and enabling logging.
torrent::initialize_main_thread();
// All signal handlers must restore errno if they return.
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));
SignalHandler::set_handler(SIGILL, std::bind(&do_panic, SIGILL));
SignalHandler::set_handler(SIGFPE, std::bind(&do_panic, SIGFPE));
SignalHandler::set_sigaction_handler(SIGBUS, &handle_sigbus);
// SIGUSR1 is used for interrupting polling, forcing the target
// thread to process new non-socket events.
// Limited list of commands with the following format:
//
// LibTorrent uses sockets for this purpose on Solaris and other
// platforms that do not properly pass signals to the target
// threads. Use '--enable-interrupt-socket' when configuring
// LibTorrent to enable this workaround.
if (torrent::utils::Thread::should_handle_sigusr1())
SignalHandler::set_handler(SIGUSR1, std::bind(&do_nothing));
// cat ~/.rtorrent.rc:
//
// # do:log.open_file=system,/usr/rakshasa/system.log
// # do:log.add_output=system,system
//
parse_config_file(argc, argv, [](auto& path) {
if (path.empty())
return;
parse_config_file_comments(path);
});
control = new Control;
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_sigaction_handler(SIGBUS, &handle_sigbus);
torrent::log_add_group_output(torrent::LOG_NOTICE, "important");
torrent::log_add_group_output(torrent::LOG_DHT_ERROR, "important");
@@ -212,7 +151,7 @@ main(int argc, char** argv) {
initialize_rpc_slots();
torrent::initialize();
torrent::set_main_thread_slots(std::bind(&client_perform));
torrent::main_thread::set_client_callback(&client_perform);
scgi::ThreadScgi::create_thread();
session::ThreadSession::create_thread();
@@ -261,7 +200,14 @@ main(int argc, char** argv) {
"method.insert = event.download.hash_queued,multi|rlookup|static\n"
"method.set_key = event.download.inserted, 1_send_scrape, ((d.tracker.send_scrape,30))\n"
"method.set_key = event.download.inserted_new, 1_prepare, {(branch,((d.state)),((view.set_visible,started)),((view.set_visible,stopped)) ),(d.save_full_session)}\n"
"method.set_key = event.download.inserted_new, 1_prepare, {(branch,((d.state)),((view.set_visible,started)),((view.set_visible,stopped)) )}\n"
// d.save_full_session runs LAST (~ prefix is ASCII 0x7E, sorts after all alphanumerics,
// matching the existing ~_delete_tied precedent on event.download.erased) so the on-disk
// snapshot includes custom fields written by other inserted_new handlers (addtime,
// seedingtime, etc.). Was previously inside 1_prepare alongside view setup, which
// snapshotted bencode before later handlers ran, so d.set_custom=addtime did not reach
// .rtorrent until the next periodic resume save — a window where rtorrent restart lost them.
"method.set_key = event.download.inserted_new, ~_save_full, ((d.save_full_session))\n"
"method.set_key = event.download.inserted_session, 1_prepare, {(branch,((d.state)),((view.set_visible,started)),((view.set_visible,stopped)) )}\n"
"method.set_key = event.download.inserted, 1_prioritize_toc, \"branch=file.prioritize_toc=,{\\\"f.multicall=(file.prioritize_toc.first),f.prioritize_first.enable=\\\",\\\"f.multicall=(file.prioritize_toc.last),f.prioritize_last.enable=\\\",d.update_priorities=}\"\n"
@@ -459,23 +405,16 @@ main(int argc, char** argv) {
}
}
int firstArg = parse_options(argc, argv);
int firstArg = parse_main_options(argc, argv);
if (OptionParser::has_flag('n', argc, argv)) {
lt_log_print(torrent::LOG_WARN, "Ignoring rtorrent.rc.");
} else {
char* config_dir = std::getenv("XDG_CONFIG_HOME");
char* home_dir = std::getenv("HOME");
parse_config_file(argc, argv, [](auto& path) {
if (path.empty()) {
lt_log_print(torrent::LOG_WARN, "Ignoring rtorrent.rc.");
return;
}
if (config_dir != NULL && config_dir[0] == '/' &&
access((std::string(config_dir) + "/rtorrent/rtorrent.rc").c_str(), F_OK) != -1) {
rpc::parse_command_single(rpc::make_target(), "try_import = " + std::string(config_dir) + "/rtorrent/rtorrent.rc");
} else if (home_dir != NULL && access((std::string(home_dir) + "/.config/rtorrent/rtorrent.rc").c_str(), F_OK) != -1) {
rpc::parse_command_single(rpc::make_target(), "try_import = ~/.config/rtorrent/rtorrent.rc");
} else {
rpc::parse_command_single(rpc::make_target(), "try_import = ~/.rtorrent.rc");
}
}
rpc::parse_command_single(rpc::make_target(), "try_import=" + path);
});
LT_LOG("seeded srandom and srand48 (seed:%u)", random_seed);
@@ -485,11 +424,8 @@ main(int argc, char** argv) {
// Load session torrents and perform scheduled tasks to ensure
// session torrents are loaded before arg torrents.
control->dht_manager()->load_dht_cache();
load_session_torrents();
// TODO: Check if this is required.
// rak::priority_queue_perform(&taskScheduler, cachedTime);
load_session_torrents(session_thread::manager()->path());
load_arg_torrents(argv + firstArg, argv + argc);
// Make sure we update the display before any scheduled tasks can
@@ -500,7 +436,7 @@ main(int argc, char** argv) {
rpc::commands.call_catch("event.system.startup_done", rpc::make_target(), "startup_done", "System startup_done event action failed: ");
torrent::utils::Thread::self()->event_loop();
torrent::system::Thread::self()->event_loop();
control->core()->download_list()->session_save();
control->cleanup();
@@ -616,7 +552,9 @@ do_panic(int signum) {
// Use the default signal handler in the future to avoid infinit
// loops.
SignalHandler::set_default(signum);
display::Canvas::cleanup();
if (control != nullptr)
display::Canvas::cleanup();
std::stringstream output;
+2 -2
View File
@@ -1,5 +1,7 @@
#include "config.h"
#include "option_parser.h"
#include <algorithm>
#include <cstring>
#include <cstdio>
@@ -8,8 +10,6 @@
#include <stdexcept>
#include <unistd.h>
#include "option_parser.h"
void
OptionParser::insert_flag(char c, slot_string s) {
m_container[c].m_slot = s;
-34
View File
@@ -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 <sundell.software@gmail.com>
#ifndef RTORRENT_OPTION_PARSER_H
#define RTORRENT_OPTION_PARSER_H
-1
View File
@@ -5,7 +5,6 @@
#include <algorithm>
#include <cstdlib>
#include <time.h>
#include <rak/string_manip.h>
#include <torrent/exceptions.h>
#include <torrent/utils/chrono.h>
+1 -1
View File
@@ -7,7 +7,7 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <torrent/utils/thread.h>
#include <torrent/system/thread.h>
#include "exec_file.h"
#include "parse.h"
+34 -5
View File
@@ -6,6 +6,7 @@
#include <string>
#include <torrent/common.h>
#include <torrent/torrent.h>
#include <torrent/utils/string_manip.h>
#include "rpc/rpc_manager.h"
#include "rpc/command.h"
@@ -33,24 +34,29 @@ json_to_object(const json& value) {
case json::value_t::number_unsigned:
case json::value_t::number_integer:
return torrent::Object(value.get<int64_t>());
case json::value_t::boolean:
return value.get<bool>() ? torrent::Object(int64_t(1)) : torrent::Object(int64_t(0));
case json::value_t::string:
return torrent::Object(value.get<std::string>());
case json::value_t::array: {
auto array_raw = torrent::Object::create_list();
auto& array = array_raw.as_list();
for (const auto& entry : value) {
for (const auto& entry : value)
array.push_back(json_to_object(entry));
}
return array_raw;
}
case json::value_t::object: {
auto map_raw = torrent::Object::create_map();
auto& map = map_raw.as_map();
for (const auto& entry : value.items()) {
for (const auto& entry : value.items())
map[entry.key()] = json_to_object(entry.value());
}
return map_raw;
}
case json::value_t::number_float:
@@ -61,17 +67,38 @@ json_to_object(const json& value) {
}
}
// Note: Only throw rpc_errors here.
json
object_to_json(const torrent::Object& object) noexcept {
object_to_json(const torrent::Object& object) {
switch (object.type()) {
case torrent::Object::TYPE_VALUE:
return object.as_value();
case torrent::Object::TYPE_STRING:
if (object.flags() & torrent::Object::flag_as_binary) {
// We should optimize our imported json library to support copying base64 strings directly.
if (object.flags() & torrent::Object::flag_base64) {
auto binary_data = torrent::utils::transform_from_base64_unsafe(object.as_string());
if (!binary_data.has_value())
throw rpc_error(JSONRPC_INTERNAL_ERROR, "invalid base64 string in base64-as-binary object");
return json::binary(*binary_data);
}
return json::binary({object.as_string().begin(), object.as_string().end()});
}
return object.as_string();
case torrent::Object::TYPE_LIST: {
json result = json::array();
for (const auto& obj : object.as_list())
result.push_back(object_to_json(obj));
return result;
}
case torrent::Object::TYPE_MAP: {
@@ -135,7 +162,9 @@ jsonrpc_call_command(const std::string& method, const json& params) {
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());
}
+13 -11
View File
@@ -7,12 +7,13 @@
#include <unistd.h>
#include "rpc/lua.h"
#ifdef HAVE_LUA
#include <lua.hpp>
#endif
#include <rak/string_manip.h>
#include <torrent/object.h>
#include <torrent/utils/string_manip.h>
#include "core/download.h"
#include "rpc/command.h"
@@ -169,19 +170,18 @@ object_to_target(const torrent::Object& obj, int call_flags, rpc::target_type* t
}
// Length of SHA1 hash is 40
if (target_string.size() < 40) {
if (target_string.size() < 40)
throw torrent::input_error("invalid parameters: invalid target");
}
char type = 'd';
std::string hash;
std::string index;
const auto& delim_pos = target_string.find_first_of(':', 40);
if (delim_pos == target_string.npos || delim_pos + 2 >= target_string.size()) {
if (require_index) {
if (require_index)
throw torrent::input_error("invalid parameters: no index");
}
hash = target_string;
@@ -211,18 +211,20 @@ object_to_target(const torrent::Object& obj, int call_flags, rpc::target_type* t
auto tracker = new torrent::tracker::Tracker(rpc.slot_find_tracker()(download, std::stoi(std::string(index))));
*deleter = [tracker]() { delete tracker; };
*target = rpc::make_target(command_base::target_tracker, target);
*target = rpc::make_target(command_base::target_tracker, tracker);
}
break;
case 'p':
{
if (index.size() < 40) {
throw torrent::input_error("Not a hash string.");
}
if (index.size() != 40)
throw torrent::input_error("invalid parameters: target is not 40 bytes long");
torrent::HashString hash;
torrent::hash_string_from_hex_c_str(index.c_str(), hash);
if (torrent::utils::transform_from_hex(index.c_str(), index.c_str() + 40, hash) != hash.end())
throw torrent::input_error("invalid parameters: target is not a hex string");
*target = rpc::make_target(command_base::target_peer, rpc.slot_find_peer()(download, hash));
}
break;
@@ -381,7 +383,7 @@ execute_lua(LuaEngine* engine, rpc::target_type target_type, torrent::Object con
case (command_base::target_download):
core::Download* dl_target = (core::Download*)target_type.second;
torrent::HashString infohash = dl_target->info()->hash();
target_string = rak::transform_hex_str(infohash);
target_string = torrent::utils::transform_to_hex_str(infohash);
break;
}
+234 -135
View File
@@ -1,9 +1,9 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
/****************************************************************************\
@@ -34,10 +34,10 @@
// #include <nlohmann/adl_serializer.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -47,10 +47,10 @@
// #include <nlohmann/detail/abi_macros.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -59,15 +59,15 @@
#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
#if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
#if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3
#if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 12 || NLOHMANN_JSON_VERSION_PATCH != 0
#warning "Already included a different version of the library!"
#endif
#endif
#endif
#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum)
#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum)
#define NLOHMANN_JSON_VERSION_PATCH 3 // NOLINT(modernize-macro-to-enum)
#define NLOHMANN_JSON_VERSION_MINOR 12 // NOLINT(modernize-macro-to-enum)
#define NLOHMANN_JSON_VERSION_PATCH 0 // NOLINT(modernize-macro-to-enum)
#ifndef JSON_DIAGNOSTICS
#define JSON_DIAGNOSTICS 0
@@ -160,10 +160,10 @@
// #include <nlohmann/detail/conversions/from_json.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -186,10 +186,10 @@
// #include <nlohmann/detail/exceptions.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -206,10 +206,10 @@
// #include <nlohmann/detail/value_t.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -222,10 +222,10 @@
// #include <nlohmann/detail/macro_scope.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -234,10 +234,10 @@
// #include <nlohmann/detail/meta/detected.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -247,10 +247,10 @@
// #include <nlohmann/detail/meta/void_t.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -334,10 +334,10 @@ NLOHMANN_JSON_NAMESPACE_END
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2016 - 2021 Evan Nemerson <evan@nemerson.com>
// SPDX-License-Identifier: MIT
@@ -2398,15 +2398,20 @@ JSON_HEDLEY_DIAGNOSTIC_POP
// C++ language standard detection
// if the user manually specified the used c++ version this is skipped
#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)
#if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
#if !defined(JSON_HAS_CPP_23) && !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)
#if (defined(__cplusplus) && __cplusplus > 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG > 202002L)
#define JSON_HAS_CPP_23
#define JSON_HAS_CPP_20
#define JSON_HAS_CPP_17
#define JSON_HAS_CPP_14
#elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
#elif (defined(__cplusplus) && __cplusplus > 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG > 201703L)
#define JSON_HAS_CPP_20
#define JSON_HAS_CPP_17
#define JSON_HAS_CPP_14
#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
#elif (defined(__cplusplus) && __cplusplus > 201402L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
#define JSON_HAS_CPP_17
#define JSON_HAS_CPP_14
#elif (defined(__cplusplus) && __cplusplus > 201103L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
#define JSON_HAS_CPP_14
#endif
// the cpp 11 flag is always specified because it is the minimal required version
@@ -2767,88 +2772,140 @@ JSON_HEDLEY_DIAGNOSTIC_POP
@brief macro
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE
@since version 3.9.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
/*!
@brief macro
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT
@since version 3.11.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
/*!
@brief macro
@def NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE
@since version 3.11.x
@since version 3.11.3
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }
/*!
@brief macro
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
@since version 3.9.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
/*!
@brief macro
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT
@since version 3.11.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
/*!
@brief macro
@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE
@since version 3.11.x
@since version 3.11.3
@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
*/
#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }
/*!
@brief macro
@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE
@since version 3.11.x
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(Type, BaseType, ...) \
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
/*!
@brief macro
@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
/*!
@brief macro
@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \
friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }
/*!
@brief macro
@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE
@since version 3.11.x
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(Type, BaseType, ...) \
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
/*!
@brief macro
@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
/*!
@brief macro
@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE
@since version 3.12.0
@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
*/
#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \
inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }
// inspired from https://stackoverflow.com/a/26745591
// allows to call any std function as if (e.g. with begin):
// allows calling any std function as if (e.g., with begin):
// using std::begin; begin(x);
//
// it allows using the detected idiom to retrieve the return type
@@ -3009,10 +3066,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/string_escape.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -3084,10 +3141,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/position_t.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -3126,10 +3183,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/cpp_future.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2018 The Abseil Authors
// SPDX-License-Identifier: MIT
@@ -3300,10 +3357,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/type_traits.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -3317,10 +3374,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/iterator_traits.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -3385,10 +3442,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/call_std/begin.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -3405,10 +3462,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/call_std/end.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -3429,10 +3486,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/json_fwd.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
@@ -4280,10 +4337,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/string_concat.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -4534,16 +4591,34 @@ class exception : public std::exception
{
return concat(a, '/', detail::escape(b));
});
return concat('(', str, ") ");
return concat('(', str, ") ", get_byte_positions(leaf_element));
#else
static_cast<void>(leaf_element);
return "";
return get_byte_positions(leaf_element);
#endif
}
private:
/// an exception object as storage for error messages
std::runtime_error m;
#if JSON_DIAGNOSTIC_POSITIONS
template<typename BasicJsonType>
static std::string get_byte_positions(const BasicJsonType* leaf_element)
{
if ((leaf_element->start_pos() != std::string::npos) && (leaf_element->end_pos() != std::string::npos))
{
return concat("(bytes ", std::to_string(leaf_element->start_pos()), "-", std::to_string(leaf_element->end_pos()), ") ");
}
return "";
}
#else
template<typename BasicJsonType>
static std::string get_byte_positions(const BasicJsonType* leaf_element)
{
static_cast<void>(leaf_element);
return "";
}
#endif
};
/// @brief exception indicating a parse error
@@ -4682,10 +4757,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/identity_tag.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -4706,10 +4781,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/std_fs.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -5158,6 +5233,12 @@ std::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<
return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...);
}
template<typename BasicJsonType>
std::tuple<> from_json_tuple_impl_base(BasicJsonType& /*unused*/, index_sequence<> /*unused*/)
{
return {};
}
template < typename BasicJsonType, class A1, class A2 >
std::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)
{
@@ -5243,7 +5324,12 @@ inline void from_json(const BasicJsonType& j, std_fs::path& p)
{
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
}
p = *j.template get_ptr<const typename BasicJsonType::string_t*>();
const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
#ifdef JSON_HAS_CPP_20
p = std_fs::path(std::u8string_view(reinterpret_cast<const char8_t*>(s.data()), s.size()));
#else
p = std_fs::u8path(s); // accepts UTF-8 encoded std::string in C++17, deprecated in C++20
#endif
}
#endif
@@ -5278,10 +5364,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/conversions/to_json.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -5304,10 +5390,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/iteration_proxy.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -5328,10 +5414,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/string_utils.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -5987,6 +6073,13 @@ inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<
j = { std::get<Idx>(t)... };
}
template<typename BasicJsonType, typename Tuple>
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& /*unused*/, index_sequence<> /*unused*/)
{
using array_t = typename BasicJsonType::array_t;
j = array_t();
}
template<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>
inline void to_json(BasicJsonType& j, const T& t)
{
@@ -5997,7 +6090,12 @@ inline void to_json(BasicJsonType& j, const T& t)
template<typename BasicJsonType>
inline void to_json(BasicJsonType& j, const std_fs::path& p)
{
j = p.string();
#ifdef JSON_HAS_CPP_20
const std::u8string s = p.u8string();
j = std::string(s.begin(), s.end());
#else
j = p.u8string(); // returns std::string in C++17
#endif
}
#endif
@@ -6072,10 +6170,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/byte_container_with_subtype.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -6184,10 +6282,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/hash.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -6317,10 +6415,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/binary_reader.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -6346,10 +6444,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/input_adapters.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -6901,10 +6999,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/json_sax.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -6920,10 +7018,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/lexer.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -8171,7 +8269,7 @@ scan_number_done:
// we are done scanning a number)
unget();
char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
char* endptr = nullptr; // NOLINT(misc-const-correctness,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
errno = 0;
// try to parse integers first and fall back to floats
@@ -9544,10 +9642,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/is_sax.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -12752,10 +12850,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/input/parser.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -13281,10 +13379,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/internal_iterator.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -13294,10 +13392,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -13453,10 +13551,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/iter_impl.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -14224,10 +14322,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/iterators/json_reverse_iterator.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -14359,10 +14457,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/json_custom_base_class.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -14401,10 +14499,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/json_pointer.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -14634,7 +14732,7 @@ class json_pointer
}
const char* p = s.c_str();
char* p_end = nullptr;
char* p_end = nullptr; // NOLINT(misc-const-correctness)
errno = 0; // strtoull doesn't reset errno
const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int)
if (p == p_end // invalid input or empty string
@@ -15156,7 +15254,7 @@ class json_pointer
// iterate array and use index as reference string
for (std::size_t i = 0; i < value.m_data.m_value.array->size(); ++i)
{
flatten(detail::concat(reference_string, '/', std::to_string(i)),
flatten(detail::concat<string_t>(reference_string, '/', std::to_string(i)),
value.m_data.m_value.array->operator[](i), result);
}
}
@@ -15175,7 +15273,7 @@ class json_pointer
// iterate object and use keys as reference string
for (const auto& element : *value.m_data.m_value.object)
{
flatten(detail::concat(reference_string, '/', detail::escape(element.first)), element.second, result);
flatten(detail::concat<string_t>(reference_string, '/', detail::escape(element.first)), element.second, result);
}
}
break;
@@ -15396,10 +15494,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/json_ref.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -15490,10 +15588,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/output/binary_writer.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -15516,10 +15614,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/output/output_adapters.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -17496,11 +17594,11 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/output/serializer.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2008 - 2009 Björn Hoehrmann <bjoern@hoehrmann.de>
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -17521,11 +17619,11 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/conversions/to_chars.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2009 Florian Loitsch <https://florian.loitsch.com/>
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -19617,10 +19715,10 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/ordered_map.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -20168,7 +20266,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
{
basic_json result;
result["copyright"] = "(C) 2013-2023 Niels Lohmann";
result["copyright"] = "(C) 2013-2025 Niels Lohmann";
result["name"] = "JSON for Modern C++";
result["url"] = "https://github.com/nlohmann/json";
result["version"]["string"] =
@@ -20433,7 +20531,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
object = nullptr; // silence warning, see #821
if (JSON_HEDLEY_UNLIKELY(t == value_t::null))
{
JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.11.3", nullptr)); // LCOV_EXCL_LINE
JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.12.0", nullptr)); // LCOV_EXCL_LINE
}
break;
}
@@ -25220,10 +25318,10 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC
// #include <nlohmann/detail/macro_unscope.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
@@ -25254,6 +25352,7 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC
#undef JSON_HAS_CPP_14
#undef JSON_HAS_CPP_17
#undef JSON_HAS_CPP_20
#undef JSON_HAS_CPP_23
#undef JSON_HAS_FILESYSTEM
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
#undef JSON_HAS_THREE_WAY_COMPARISON
@@ -25265,10 +25364,10 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC
// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
-34
View File
@@ -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 <sundell.software@gmail.com>
#include "config.h"
#include "object_storage.h"
+4 -38
View File
@@ -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 <sundell.software@gmail.com>
// The object_storage type is responsible for storing variables,
// commands, command lists and other types encoded in torrent::Object
// format.
@@ -42,8 +8,8 @@
#include <cstring>
#include <unordered_map>
#include <torrent/object.h>
#include <torrent/utils/unordered_vector.h>
#include "rak/unordered_vector.h"
#include "command.h"
#include "fixed_key.h"
@@ -51,7 +17,7 @@ namespace rpc {
struct object_storage_node {
torrent::Object object;
char flags;
unsigned int flags;
};
typedef std::unordered_map<fixed_key_type<64>, object_storage_node, hash_fixed_key_type> object_storage_base_type;
@@ -59,8 +25,8 @@ typedef std::unordered_map<fixed_key_type<64>, object_storage_node, hash_fixed_k
class object_storage : private object_storage_base_type {
public:
// Should really change rlookup_type into a set with pair values.
typedef object_storage_base_type base_type;
typedef std::map<std::string, rak::unordered_vector<base_type::value_type*> > rlookup_type;
typedef object_storage_base_type base_type;
typedef std::map<std::string, torrent::utils::unordered_vector<base_type::value_type*> > rlookup_type;
using base_type::key_type;
using base_type::value_type;
+7 -9
View File
@@ -151,23 +151,21 @@ parse_command_file(const std::string& path) {
try {
unsigned int getCount = 0;
while (file.good()
&& !file.getline(buffer + getCount, 4096 - getCount).fail()) {
while (file.good() && !file.getline(buffer + getCount, 4096 - getCount).fail()) {
if (file.gcount() == 0)
throw torrent::internal_error("parse_command_file(...) file.gcount() == 0.");
int lineLength = file.gcount() - 1;
int line_length = file.gcount() - 1;
// In case we are at the end of the file and the last character is
// not a line feed, we'll just increase the read character count so
// that the last would also be included in option line.
if (file.eof() && file.get() != '\n')
lineLength++;
line_length++;
int escaped = parse_count_escaped(buffer + getCount, buffer + getCount + lineLength);
int escaped = parse_count_escaped(buffer + getCount, buffer + getCount + line_length);
lineNumber++;
getCount += lineLength;
getCount += line_length;
if (getCount == 4096 - 1)
throw torrent::input_error("Exceeded max line length.");
@@ -258,9 +256,9 @@ command_function_call_object(const torrent::Object& cmd, target_type target, con
rpc::command_base::pop_stack(&stack, last_stack);
return result;
} catch (torrent::bencode_error& e) {
} catch (...) {
rpc::command_base::pop_stack(&stack, last_stack);
throw e;
throw;
}
}
+9 -8
View File
@@ -3,6 +3,7 @@
#include <cstring>
#include <torrent/exceptions.h>
#include <torrent/utils/string_manip.h>
#include "parse_commands.h"
#include "rpc/rpc_manager.h"
@@ -47,11 +48,11 @@ RpcManager::object_to_target(const torrent::Object& obj, int call_flags, rpc::ta
const auto& delim_pos = target_string.find_first_of(':', 40);
if (delim_pos == target_string.npos ||
delim_pos + 2 >= target_string.size()) {
if (delim_pos == target_string.npos || delim_pos + 2 >= target_string.size()) {
if (require_index) {
throw torrent::input_error("invalid parameters: no index");
}
hash = target_string;
} else {
@@ -87,15 +88,15 @@ RpcManager::object_to_target(const torrent::Object& obj, int call_flags, rpc::ta
break;
case 'p': {
if (index.size() < 40) {
throw torrent::input_error("invalid parameters: not a hash string.");
}
if (index.size() != 40)
throw torrent::input_error("invalid parameters: target is not 40 bytes long");
torrent::HashString hash;
torrent::hash_string_from_hex_c_str(index.c_str(), hash);
*target = rpc::make_target(command_base::target_peer,
rpc.slot_find_peer()(download, hash));
if (torrent::utils::transform_from_hex(index.c_str(), index.c_str() + 40, hash) != hash.end())
throw torrent::input_error("invalid parameters: target is not a hex string");
*target = rpc::make_target(command_base::target_peer, rpc.slot_find_peer()(download, hash));
break;
}
+30 -19
View File
@@ -1,6 +1,7 @@
#ifndef RTORRENT_RPC_MANAGER_H
#define RTORRENT_RPC_MANAGER_H
#include <atomic>
#include <cstdint>
#include <functional>
#include <torrent/common.h>
@@ -55,37 +56,44 @@ public:
RpcManager() = default;
~RpcManager() = default;
bool is_handlers_initialized() const { return m_handlers_initialized; }
bool is_handlers_initialized() const { return m_handlers_initialized; }
void initialize_handlers();
void cleanup();
void initialize_handlers();
void cleanup();
int64_t size_limit() { return m_xmlrpc.size_limit(); };
void set_size_limit(uint64_t size) { m_xmlrpc.set_size_limit(size); };
int64_t size_limit() { return m_xmlrpc.size_limit(); };
void set_size_limit(uint64_t size) { m_xmlrpc.set_size_limit(size); };
int dialect() { return m_xmlrpc.dialect(); }
void set_dialect(int dialect) { m_xmlrpc.set_dialect(dialect); }
int dialect() { return m_xmlrpc.dialect(); }
void set_dialect(int dialect) { m_xmlrpc.set_dialect(dialect); }
bool is_type_enabled(RPCType type) const;
void set_type_enabled(RPCType type, bool enabled);
bool is_type_enabled(RPCType type) const;
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);
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);
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; }
bool scgi_allow_compression() const { return m_scgi_allow_compression; }
void set_scgi_allow_compression(bool allow) { m_scgi_allow_compression = allow; }
unsigned int scgi_min_compress_size() const { return m_scgi_min_compress_size; }
void set_scgi_min_compress_size(unsigned int size) { m_scgi_min_compress_size = size; }
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;
bool is_trusted() const;
static void object_to_target(const torrent::Object& obj, int callFlags, rpc::target_type* target, std::function<void()>* deleter);
static void object_to_target(const torrent::Object& obj, int callFlags, rpc::target_type* target, std::function<void()>* deleter);
private:
bool m_trusted{true};
@@ -97,6 +105,9 @@ private:
bool m_is_jsonrpc_enabled{true};
bool m_is_xmlrpc_enabled{true};
std::atomic<bool> m_scgi_allow_compression{true};
std::atomic<unsigned int> m_scgi_min_compress_size{1000};
slot_download m_slot_find_download;
slot_file m_slot_find_file;
slot_tracker m_slot_find_tracker;
+50 -57
View File
@@ -5,12 +5,11 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/un.h>
#include <torrent/connection_manager.h>
#include <torrent/torrent.h>
#include <torrent/exceptions.h>
#include <torrent/net/fd.h>
#include <torrent/net/poll.h>
#include <torrent/net/socket_address.h>
#include <torrent/system/poll.h>
#include <torrent/runtime/socket_manager.h>
#include "control.h"
@@ -34,64 +33,60 @@ SCgi::~SCgi() {
void
SCgi::open_port(sockaddr* sa, unsigned int length, bool dont_route) {
torrent::runtime::socket_manager()->open_event_or_throw(this, [&]() {
int fd = torrent::fd_open_family(torrent::fd_flag_stream | torrent::fd_flag_nonblock | torrent::fd_flag_reuse_address,
sa->sa_family);
int fd = torrent::fd_open_family(torrent::fd_flag_stream | torrent::fd_flag_nonblock | torrent::fd_flag_reuse_address,
sa->sa_family);
if (fd == -1)
throw torrent::resource_error("Could not open socket for listening: " + std::string(std::strerror(errno)));
if (fd == -1)
throw torrent::resource_error("Could not open socket for listening: " + std::string(std::strerror(errno)));
if (dont_route && !torrent::fd_set_dont_route(fd, true)) {
torrent::fd_close(fd);
throw torrent::resource_error("Could not set socket option IP_DONTROUTE: " + std::string(std::strerror(errno)));
}
if (dont_route && !torrent::fd_set_dont_route(fd, true)) {
torrent::fd_close(fd);
throw torrent::resource_error("Could not set socket option IP_DONTROUTE: " + std::string(std::strerror(errno)));
}
set_file_descriptor(fd);
set_file_descriptor(fd);
open(reinterpret_cast<sockaddr*>(sa), length);
});
open(reinterpret_cast<sockaddr*>(sa), length);
torrent::connection_manager()->inc_socket_count();
torrent::runtime::socket_manager()->register_event_or_throw(this, torrent::runtime::category_scgi, []() {});
}
void
SCgi::open_named(const std::string& filename) {
if (filename.empty() || filename.size() > 4096)
if (filename.empty() || filename.size() >= sizeof(sockaddr_un::sun_path))
throw torrent::resource_error("Invalid filename length.");
auto buffer = std::make_unique<char[]>(sizeof(sockaddr_un) + filename.size() + 1);
sockaddr_un* sa = reinterpret_cast<sockaddr_un*>(buffer.get());
sa->sun_family = AF_LOCAL;
std::memcpy(sa->sun_path, filename.c_str(), filename.size() + 1);
torrent::runtime::socket_manager()->open_event_or_throw(this, [&]() {
int fd = torrent::fd_open_local(torrent::fd_flag_stream | torrent::fd_flag_nonblock | torrent::fd_flag_reuse_address);
int fd = torrent::fd_open_local(torrent::fd_flag_stream | torrent::fd_flag_nonblock | torrent::fd_flag_reuse_address);
if (fd == -1)
throw torrent::resource_error("Could not open socket for listening: " + std::string(std::strerror(errno)));
if (fd == -1)
throw torrent::resource_error("Could not open socket for listening: " + std::string(std::strerror(errno)));
set_file_descriptor(fd);
set_file_descriptor(fd);
open(reinterpret_cast<sockaddr*>(sa), offsetof(struct sockaddr_un, sun_path) + filename.size() + 1);
});
open(reinterpret_cast<sockaddr*>(sa), offsetof(struct sockaddr_un, sun_path) + filename.size() + 1);
torrent::connection_manager()->inc_socket_count();
torrent::runtime::socket_manager()->register_event_or_throw(this, torrent::runtime::category_scgi, []() {});
m_path = filename;
}
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.
});
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);
torrent::connection_manager()->inc_socket_count();
// fd is already bound and listening; no bind()/listen() needed.
torrent::runtime::socket_manager()->register_event_or_throw(this, torrent::runtime::category_scgi, []() {});
}
void
@@ -132,15 +127,13 @@ SCgi::stop() {
itr->close();
}
torrent::runtime::socket_manager()->close_event_or_throw(this, [this]() {
torrent::runtime::socket_manager()->unregister_event_or_throw(this, [this]() {
torrent::this_thread::poll()->remove_and_close(this);
torrent::fd_close(file_descriptor());
set_file_descriptor(-1);
});
torrent::connection_manager()->dec_socket_count();
if (!m_path.empty())
::unlink(m_path.c_str());
}
@@ -159,38 +152,38 @@ SCgi::event_read() {
if (m_current == m_tasks.end())
m_current = std::find_if(m_tasks.begin(), prev, [](const auto& task) { return !task->is_open(); });
int fd = torrent::fd_accept(file_descriptor());
if (fd == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
return;
// Force a new event_read() call just to be sure we don't enter an infinite loop.
if (errno == ECONNABORTED)
return;
throw torrent::resource_error("Listener port accept() failed: " + std::string(std::strerror(errno)));
}
if (m_current == prev) {
// TODO: Currently just close, although we should remove ourselves from read.
int fd = torrent::fd_accept(file_descriptor());
if (fd != -1)
torrent::fd_close(fd);
torrent::fd_close(fd);
continue;
}
auto open_func = [this, task = m_current->get()]() {
int fd = torrent::fd_accept(file_descriptor());
if (fd == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
return;
// Force a new event_read() call just to be sure we don't enter an infinite loop.
if (errno == ECONNABORTED)
return;
throw torrent::resource_error("Listener port accept() failed: " + std::string(std::strerror(errno)));
}
auto open_func = [this, fd, task = m_current->get()]() {
task->open(this, fd);
};
auto cleanup_func = [task = m_current->get()]() {
auto cleanup_func = [fd, task = m_current->get()](bool opened) {
if (!opened) {
torrent::fd_close(fd);
return;
}
task->cancel_open();
};
bool result = torrent::runtime::socket_manager()->open_event_or_cleanup(m_current->get(), open_func, cleanup_func);
bool result = torrent::runtime::socket_manager()->open_event_or_cleanup(m_current->get(), torrent::runtime::category_scgi, open_func, cleanup_func);
if (!result)
break;
+3 -4
View File
@@ -2,7 +2,6 @@
#define RTORRENT_RPC_SCGI_H
#include <array>
#include <functional>
#include <memory>
#include <torrent/event.h>
@@ -27,10 +26,10 @@ public:
void stop();
const std::string& path() const { return m_path; }
const std::string& path() const { return m_path; }
int log_fd() const { return m_logFd; }
void set_log_fd(int fd) { m_logFd = fd; }
int log_fd() const { return m_logFd; }
void set_log_fd(int fd) { m_logFd = fd; }
void event_read() override;
void event_write() override;
+274 -165
View File
@@ -2,40 +2,59 @@
#include "rpc/scgi_task.h"
#include <atomic>
#include <cassert>
#include <charconv>
#include <cstdio>
#include <unistd.h>
#include <vector>
#include <sys/types.h>
#include <sys/socket.h>
#include <torrent/exceptions.h>
#include <torrent/torrent.h>
#include <torrent/net/fd.h>
#include <torrent/net/poll.h>
#include <torrent/runtime/socket_manager.h>
#include <torrent/system/callbacks.h>
#include <torrent/system/poll.h>
#include <torrent/utils/log.h>
#include <torrent/utils/thread.h>
#include "control.h"
#include "globals.h"
#include "scgi.h"
#include "rpc/parse_commands.h"
#include "rpc/scgi.h"
#include "utils/gzip.h"
namespace rpc {
SCgiTask::SCgiTask()
: m_callback_id(torrent::system::make_callback_id()) {
set_file_descriptor(-1);
}
void
SCgiTask::open(SCgi* parent, int fd) {
set_file_descriptor(fd);
m_buffer.reset(new char[default_buffer_size + 1]);
m_parent = parent;
m_buffer_size = default_buffer_size;
m_position = m_buffer.get();
m_body = nullptr;
m_position = 0;
m_body = 0;
m_content_length = 0;
m_content_type = XML;
m_content_type_set = false;
m_accepts_compression = false;
m_trusted = true; // SCgiTask is pooled and reused; reset trust to default
// so a prior untrusted connection does not leak its
// m_trusted=false into the next reuse, given that the
// UNTRUSTED_CONNECTION=0 parse branch is a no-op.
torrent::this_thread::poll()->open(this);
torrent::this_thread::poll()->insert_read(this);
torrent::this_thread::poll()->insert_error(this);
auto lock = std::lock_guard<std::mutex>(m_result_mutex);
// Leave room for terminating nul byte for parsing the header.
m_buffer.resize(default_buffer_size + 1);
}
void
@@ -54,8 +73,7 @@ SCgiTask::close() {
if (!is_open())
return;
torrent::main_thread::thread()->cancel_callback_and_wait(this);
torrent::utils::Thread::self()->cancel_callback(this);
torrent::system::cancel_callback_and_wait(m_callback_id, scgi_thread::thread(), torrent::main_thread::thread());
torrent::runtime::socket_manager()->close_event_or_throw(this, [this]() {
torrent::this_thread::poll()->remove_and_close(this);
@@ -64,14 +82,23 @@ SCgiTask::close() {
set_file_descriptor(-1);
});
// The callbacks are guaranteed to be finished/canceled at this point.
auto lock = std::lock_guard<std::mutex>(m_result_mutex);
m_buffer = nullptr;
m_buffer.clear();
}
void
SCgiTask::event_read() {
int bytes = ::recv(m_fileDesc, m_position, m_buffer_size - (m_position - m_buffer.get()), 0);
int read_length = m_buffer.size() - m_position;
if (m_content_length == 0)
read_length--;
if (read_length <= 0)
throw torrent::internal_error("SCgiTask::event_read() no space in buffer for event_read.");
int bytes = ::recv(m_fileDesc, m_buffer.data() + m_position, read_length, 0);
if (bytes <= 0) {
if (bytes == 0 || !(errno == EAGAIN || errno == EINTR))
@@ -82,104 +109,48 @@ SCgiTask::event_read() {
// The buffer has space to nul-terminate to ease the parsing below.
m_position += bytes;
*m_position = '\0';
if (m_body == NULL) {
// Don't bother caching the parsed values, as we're likely to
// receive all the data we need the first time.
char* current;
if (m_content_length == 0) {
// While reading the header, we leave room in the buffer for a nul byte.
m_buffer[m_position] = '\0';
int header_size = strtol(m_buffer.get(), &current, 0);
// Don't bother caching the parsed values, as we're likely to receive all the data we need the
// first time.
unsigned int header_size{};
if (current == m_position)
auto [current, ec] = std::from_chars(m_buffer.data(), m_buffer.data() + m_position, header_size);
// If the request doesn't start with an integer or if it didn't end in ':', then close the
// connection.
if (current == m_buffer.data())
return;
// If the request doesn't start with an integer or if it didn't
// end in ':', then close the connection.
if (current == m_buffer.get() || *current != ':' || header_size < 17 || header_size > max_header_size)
goto event_read_failed;
if (std::distance(++current, m_position) < header_size + 1)
if (current == m_buffer.data() + m_position)
return;
// We'll parse this fully below, but the SCGI spec requires it to
// be the first header.
if (std::memcmp(current, "CONTENT_LENGTH", 15) != 0)
if (*current != ':' || header_size < 17 || header_size > max_header_size)
goto event_read_failed;
std::string content_type = "";
size_t content_length = 0;
const char* header_end = current + header_size;
current++;
// Assume trusted until we find the UNTRUSTED_CONNECTION header.
m_trusted = true;
// The header size starts after the ':' and ends at the first ','.
if ((m_buffer.data() + m_position) - current < static_cast<int>(header_size) + 1)
return;
// Parse out the null-terminated header keys and values, with
// checks to ensure it doesn't scan beyond the limits of the
// header
while (current < header_end) {
char* key = current;
char* key_end = static_cast<char*>(std::memchr(current, '\0', header_end - current));
if (!key_end)
goto event_read_failed;
current = key_end + 1;
if (current >= header_end)
goto event_read_failed;
char* value = current;
char* value_end = static_cast<char*>(std::memchr(current, '\0', header_end - current));
if (!value_end)
goto event_read_failed;
current = value_end + 1;
if (strcmp(key, "CONTENT_LENGTH") == 0) {
char* content_pos;
content_length = strtol(value, &content_pos, 10);
if (*content_pos != '\0' || content_length <= 0 || content_length > max_content_size)
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;
}
}
if (current != header_end)
// Check for ',' after the header, if it's not there, then close the connection.
if (*(current + header_size) != ',')
goto event_read_failed;
if (content_length <= 0)
// The header must start with "CONTENT_LENGTH" followed by a null byte.
if (std::memcmp(current, "CONTENT_LENGTH", 14+1) != 0)
goto event_read_failed;
m_body = current + 1;
header_size = std::distance(m_buffer.get(), m_body);
if (!detect_content_type(content_type))
if (!parse_headers(current, header_size))
goto event_read_failed;
if ((unsigned int)(content_length + header_size) < m_buffer_size) {
m_buffer_size = content_length + header_size;
} else if ((unsigned int)content_length <= default_buffer_size) {
m_buffer_size = content_length;
std::memmove(m_buffer.get(), m_body, std::distance(m_body, m_position));
m_position = m_buffer.get() + std::distance(m_body, m_position);
m_body = m_buffer.get();
} else {
realloc_buffer((m_buffer_size = content_length) + 1, m_body, std::distance(m_body, m_position));
m_position = m_buffer.get() + std::distance(m_body, m_position);
m_body = m_buffer.get();
}
}
if ((unsigned int)std::distance(m_buffer.get(), m_position) != m_buffer_size)
if (m_position < m_body + m_content_length)
return;
torrent::this_thread::poll()->remove_read(this);
@@ -189,13 +160,18 @@ SCgiTask::event_read() {
// Clean up logging, this is just plain ugly...
// write(m_logFd, "\n---\n", sizeof("\n---\n"));
result = ::write(m_parent->log_fd(), m_buffer.get(), m_buffer_size);
result = ::write(m_parent->log_fd(), m_buffer.data() + m_body, m_position - m_body);
result = ::write(m_parent->log_fd(), "\n---\n", sizeof("\n---\n"));
}
lt_log_print_dump(torrent::LOG_RPC_DUMP, m_body, m_buffer_size - std::distance(m_buffer.get(), m_body), "scgi", "RPC read.", 0);
lt_log_print_dump(torrent::LOG_RPC_DUMP, m_buffer.data() + m_body, m_content_length, "scgi", "RPC read.", 0);
receive_call(m_body, m_buffer_size - std::distance(m_buffer.get(), m_body));
if (!m_content_type_set) {
if (m_buffer[m_body] == '{' || m_buffer[m_body] == '[')
m_content_type = ContentType::JSON;
}
receive_call(m_buffer.data() + m_body, m_content_length);
return;
event_read_failed:
@@ -205,7 +181,7 @@ event_read_failed:
void
SCgiTask::event_write() {
int bytes = ::send(m_fileDesc, m_position, m_buffer_size, 0);
int bytes = ::send(m_fileDesc, m_buffer.data() + m_position, m_buffer.size() - m_position, 0);
if (bytes == -1) {
if (!(errno == EAGAIN || errno == EINTR))
@@ -215,9 +191,8 @@ SCgiTask::event_write() {
}
m_position += bytes;
m_buffer_size -= bytes;
if (bytes == 0 || m_buffer_size == 0)
if (bytes == 0 || m_position == m_buffer.size())
return close();
}
@@ -226,6 +201,96 @@ SCgiTask::event_error() {
close();
}
bool
SCgiTask::parse_headers(const char* current, unsigned int header_length) {
std::string content_type;
const char* header_end = current + header_length;
// Parse out the null-terminated header keys and values, with
// checks to ensure it doesn't scan beyond the limits of the
// header
while (current < header_end) {
auto* key = current;
auto* key_end = static_cast<const char*>(std::memchr(current, '\0', header_end - current));
if (key_end == nullptr)
return false;
current = key_end + 1;
if (current >= header_end)
return false;
auto* value = current;
auto* value_end = static_cast<const char*>(std::memchr(current, '\0', header_end - current));
if (value_end == nullptr)
return false;
current = value_end + 1;
if (std::strncmp(key, "CONTENT_LENGTH", 14+1) == 0) {
auto [content_pos, ec] = std::from_chars(value, value_end, m_content_length);
if (*content_pos != '\0' || m_content_length <= 0 || m_content_length > max_content_size)
return false;
} else if (std::strncmp(key, "CONTENT_TYPE", 12+1) == 0) {
content_type = value;
} else if (std::strncmp(key, "ACCEPT_ENCODING", 15+1) == 0) {
std::string accept_encoding(value, value_end - value);
if (accept_encoding.find("gzip") != std::string::npos)
m_accepts_compression = true;
} else if (std::strncmp(key, "UNTRUSTED_CONNECTION", 20+1) == 0) {
if (std::strncmp(value, "1", 1+1) == 0)
m_trusted = false;
else if (std::strncmp(value, "0", 1+1) == 0)
m_trusted = true; // Explicit reset (open() also resets, but be defensive in case
// future refactors stop pooling tasks or skip the open() reset).
else
return false;
}
}
if (current != header_end)
return false;
if (m_content_length == 0)
return false;
// Move past the ',' that ends the header.
current++;
if (current > m_buffer.data() + m_buffer.size())
throw torrent::internal_error("SCgiTask::event_read() header parsing overflow : body start is beyond buffer end");
m_body = current - m_buffer.data();
if (!detect_content_type(content_type))
return false;
if (m_body + m_content_length > m_buffer.size()) {
if (m_content_length > default_buffer_size) {
std::vector<char> tmp(m_content_length);
std::memcpy(tmp.data(), m_buffer.data() + m_body, m_position - m_body);
m_buffer.swap(tmp);
} else {
std::memmove(m_buffer.data(), m_buffer.data() + m_body, m_position - m_body);
}
m_position = m_position - m_body;
m_body = 0;
}
return true;
}
static inline bool
scgi_match_content_type(const std::string& content_type, const char* type) {
std::string::size_type pos = content_type.find_first_of(" ;");
@@ -239,18 +304,17 @@ scgi_match_content_type(const std::string& content_type, const char* type) {
bool
SCgiTask::detect_content_type(const std::string& content_type) {
if (content_type.empty()) {
// If no CONTENT_TYPE was supplied, peek at the body to check if it's JSON
// { is a single request object, while [ is a batch array
if (*m_body == '{' || *m_body == '[')
m_content_type = ContentType::JSON;
else
m_content_type = ContentType::XML;
// Defer body-peek detection until the full body is received.
// event_read() will auto-detect from the first body byte after
// confirming m_position >= m_body + m_content_length.
} else if (scgi_match_content_type(content_type, "application/json")) {
m_content_type = ContentType::JSON;
m_content_type = ContentType::JSON;
m_content_type_set = true;
} else if (scgi_match_content_type(content_type, "text/xml")) {
m_content_type = ContentType::XML;
m_content_type = ContentType::XML;
m_content_type_set = true;
} else {
// If the content type is not JSON or XML, we don't know how to handle it.
@@ -260,36 +324,9 @@ SCgiTask::detect_content_type(const std::string& content_type) {
return true;
}
// If bufferSize is zero then memcpy won't do anything.
void
SCgiTask::realloc_buffer(uint32_t size, const char* buffer, uint32_t bufferSize) {
auto tmp = new char[size];
std::memcpy(tmp, buffer, bufferSize);
m_buffer.reset(tmp);
}
void
SCgiTask::receive_call(const char* buffer, uint32_t length) {
// 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);
scgi_thread->callback_interrupt_polling(this, [this]() {
// Only need to lock once here as a memory barrier.
m_result_mutex.lock();
m_result_mutex.unlock();
torrent::this_thread::poll()->insert_write(this);
});
};
auto lock = std::lock_guard<std::mutex>(m_result_mutex);
assert(torrent::this_thread::thread() == scgi_thread::thread());
RpcManager::RPCType rpc_type;
@@ -304,49 +341,121 @@ SCgiTask::receive_call(const char* buffer, uint32_t length) {
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;
};
// TODO: Rewrite RpcManager.process to pass the result buffer instead of having to copy it.
// TODO: Also allow us to request RpcManager.process to reserve space for a header.
// TODO: Completely remove the mutex, and align m_buffer?
if (trusted)
rpc.process(rpc_type, buffer, length, callback);
auto result_callback = [this](const char* b, uint32_t l) {
receive_write(b, l);
// Memory barrier for the result data.
// std::atomic_thread_fence(std::memory_order_release);
m_result_mutex.lock();
m_result_mutex.unlock();
scgi_thread::callback_interrupt(m_callback_id, [this]() {
if (!is_open())
return;
torrent::this_thread::poll()->insert_write(this);
// Memory barrier for the result data.
// std::atomic_thread_fence(std::memory_order_acquire);
m_result_mutex.lock();
m_result_mutex.unlock();
});
return true;
};
// Memory barrier for the input data.
// std::atomic_thread_fence(std::memory_order_release);
m_result_mutex.lock();
m_result_mutex.unlock();
torrent::main_thread::callback_interrupt(m_callback_id, [this, rpc_type, buffer, length, result_callback]() {
// Memory barrier for the input data.
// std::atomic_thread_fence(std::memory_order_acquire);
m_result_mutex.lock();
m_result_mutex.unlock();
if (m_trusted)
rpc.process(rpc_type, buffer, length, result_callback);
else
rpc.process_untrusted(rpc_type, buffer, length, callback);
rpc.process_untrusted(rpc_type, buffer, length, result_callback);
});
}
void
SCgiTask::receive_write(const char* buffer, uint32_t length) {
if (buffer == NULL || length > (100 << 20))
assert(torrent::this_thread::thread() == torrent::main_thread::thread());
if (buffer == nullptr || length > (100 << 20))
throw torrent::internal_error("SCgiTask::receive_write(...) received bad input.");
auto lock = std::lock_guard<std::mutex>(m_result_mutex);
// Need to cast due to a bug in MacOSX gcc-4.0.1.
if (length + 256 > std::max(m_buffer_size, (unsigned int)default_buffer_size))
realloc_buffer(length + 256, NULL, 0);
const auto header = m_content_type == ContentType::JSON
? "Status: 200 OK\r\nContent-Type: application/json\r\nContent-Length: %i\r\n\r\n"
: "Status: 200 OK\r\nContent-Type: text/xml\r\nContent-Length: %i\r\n\r\n";
// Who ever bothers to check the return value?
int headerSize = snprintf(m_buffer.get(), m_buffer_size, header, length);
m_position = m_buffer.get();
m_buffer_size = length + headerSize;
std::memcpy(m_buffer.get() + headerSize, buffer, length);
// Main thread callback already locked this mutex.
// auto lock = std::lock_guard<std::mutex>(m_result_mutex);
// Write to log prior to possible compression
if (m_parent->log_fd() >= 0) {
[[maybe_unused]] int result;
result = write(m_parent->log_fd(), m_buffer.get(), m_buffer_size);
int result [[maybe_unused]];
// Clean up logging, this is just plain ugly...
// write(m_logFd, "\n---\n", sizeof("\n---\n"));
result = write(m_parent->log_fd(), buffer, length);
result = write(m_parent->log_fd(), "\n---\n", sizeof("\n---\n"));
}
lt_log_print_dump(torrent::LOG_RPC_DUMP, m_buffer.get(), m_buffer_size, "scgi", "RPC write.", 0);
lt_log_print_dump(torrent::LOG_RPC_DUMP, buffer, length, "scgi", "RPC write.", 0);
if (m_accepts_compression && rpc.scgi_allow_compression() && length > rpc.scgi_min_compress_size())
gzip_response(buffer, length);
else
plaintext_response(buffer, length);
}
// We don't know the size of the content-length string, so leave sufficient space for the header
// strings plus max length of content-length.
void
SCgiTask::plaintext_response(const char* buffer, uint32_t content_length) {
auto header_first = content_type() == ContentType::XML ? header_xml : header_json;
auto header_first_size = content_type() == ContentType::XML ? header_xml_size : header_json_size;
auto length_str = std::to_string(content_length);
m_buffer.resize(header_first_size + length_str.size() + header_last_size + content_length);
std::memcpy(m_buffer.data(), header_first, header_first_size);
std::memcpy(m_buffer.data() + header_first_size, length_str.data(), length_str.size());
std::memcpy(m_buffer.data() + header_first_size + length_str.size(), header_last, header_last_size);
std::memcpy(m_buffer.data() + header_first_size + length_str.size() + header_last_size, buffer, content_length);
m_position = 0;
}
void
SCgiTask::gzip_response(const char* buffer, uint32_t content_length) {
auto header_first = content_type() == ContentType::XML ? header_xml : header_json;
auto header_first_size = content_type() == ContentType::XML ? header_xml_size : header_json_size;
unsigned int body_offset = header_first_size + 20 + header_last_size;
utils::gzip_compress_to_vector(buffer, content_length, m_buffer, body_offset);
auto length_str = std::to_string(m_buffer.size() - body_offset);
auto header_size = header_first_size + length_str.size() + header_last_size;
auto header_start = body_offset - header_size;
if (header_size > body_offset)
throw torrent::internal_error("SCgiTask::gzip_response(...) header overflow : start position is negative");
if (header_start > body_offset)
throw torrent::internal_error("SCgiTask::gzip_response(...) header overflow : start position is beyond buffer start");
std::memcpy(m_buffer.data() + header_start, header_first, header_first_size);
std::memcpy(m_buffer.data() + header_start + header_first_size, length_str.data(), length_str.size());
std::memcpy(m_buffer.data() + header_start + header_first_size + length_str.size(), header_last, header_last_size);
m_position = header_start;
}
} // namespace rpc
+26 -11
View File
@@ -3,6 +3,7 @@
#include <memory>
#include <mutex>
#include <vector>
#include <torrent/event.h>
namespace rpc {
@@ -11,13 +12,13 @@ class SCgi;
class SCgiTask : public torrent::Event {
public:
static const unsigned int default_buffer_size = 2047;
static const int max_header_size = 2000;
static const int max_content_size = (2 << 23);
static constexpr int default_buffer_size = 8191;
static constexpr int max_header_size = 2000;
static constexpr int max_content_size = (2 << 23);
enum ContentType { XML, JSON };
SCgiTask() { m_fileDesc = -1; }
SCgiTask();
const char* type_name() const override { return "scgi-task"; }
@@ -36,24 +37,38 @@ public:
void event_error() override;
private:
static constexpr char header_xml[] = "Status: 200 OK\r\nContent-Type: text/xml\r\nContent-Length: ";
static constexpr char header_json[] = "Status: 200 OK\r\nContent-Type: application/json\r\nContent-Length: ";
static constexpr char header_last[] = "\r\n\r\n";
static constexpr size_t header_xml_size = sizeof(header_xml) - 1;
static constexpr size_t header_json_size = sizeof(header_json) - 1;
static constexpr size_t header_last_size = sizeof(header_last) - 1;
bool parse_headers(const char* current, unsigned int header_length);
bool detect_content_type(const std::string& content_type);
void realloc_buffer(uint32_t size, const char* buffer, uint32_t bufferSize);
void receive_call(const char* buffer, uint32_t length);
void receive_write(const char* buffer, uint32_t length);
SCgi* m_parent{};
void plaintext_response(const char* buffer, uint32_t content_length);
void gzip_response(const char* buffer, uint32_t content_length);
SCgi* m_parent{};
torrent::system::callback_id m_callback_id;
std::mutex m_result_mutex;
std::unique_ptr<char[]> m_buffer;
char* m_position{};
char* m_body{};
std::vector<char> m_buffer;
unsigned int m_position{};
unsigned int m_body{};
unsigned int m_buffer_size{0};
unsigned int m_content_length{};
ContentType m_content_type{XML};
ContentType m_content_type{ XML };
bool m_accepts_compression{};
bool m_trusted{true};
bool m_content_type_set{false};
};
}
+71 -79
View File
@@ -27,7 +27,7 @@ distribution.
#include "tinyxml2.h"
#include <new> // yes, this one new style header, is in the Android SDK.
#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__)
#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) || defined(__CC_ARM)
# include <stddef.h>
# include <stdarg.h>
#else
@@ -239,13 +239,13 @@ char* StrPair::ParseName( char* p )
if ( !p || !(*p) ) {
return 0;
}
if ( !XMLUtil::IsNameStartChar( (unsigned char) *p ) ) {
if ( !XMLUtil::IsNameStartChar( static_cast<unsigned char>(*p) ) ) {
return 0;
}
char* const start = p;
++p;
while ( *p && XMLUtil::IsNameChar( (unsigned char) *p ) ) {
while ( *p && XMLUtil::IsNameChar( static_cast<unsigned char>(*p) ) ) {
++p;
}
@@ -472,102 +472,94 @@ void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length
}
const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
const char* XMLUtil::GetCharacterRef(const char* p, char* value, int* length)
{
// Presume an entity, and pull it out.
// Assume an entity, and pull it out.
*length = 0;
if ( *(p+1) == '#' && *(p+2) ) {
unsigned long ucs = 0;
TIXMLASSERT( sizeof( ucs ) >= 4 );
static const uint32_t MAX_CODE_POINT = 0x10FFFF;
if (*(p + 1) == '#' && *(p + 2)) {
uint32_t ucs = 0;
ptrdiff_t delta = 0;
unsigned mult = 1;
uint32_t mult = 1;
static const char SEMICOLON = ';';
if ( *(p+2) == 'x' ) {
bool hex = false;
uint32_t radix = 10;
const char* q = 0;
char terminator = '#';
if (*(p + 2) == 'x') {
// Hexadecimal.
const char* q = p+3;
if ( !(*q) ) {
return 0;
}
hex = true;
radix = 16;
terminator = 'x';
q = strchr( q, SEMICOLON );
if ( !q ) {
return 0;
}
TIXMLASSERT( *q == SEMICOLON );
delta = q-p;
--q;
while ( *q != 'x' ) {
unsigned int digit = 0;
if ( *q >= '0' && *q <= '9' ) {
digit = *q - '0';
}
else if ( *q >= 'a' && *q <= 'f' ) {
digit = *q - 'a' + 10;
}
else if ( *q >= 'A' && *q <= 'F' ) {
digit = *q - 'A' + 10;
}
else {
return 0;
}
TIXMLASSERT( digit < 16 );
TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
const unsigned int digitScaled = mult * digit;
TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
ucs += digitScaled;
TIXMLASSERT( mult <= UINT_MAX / 16 );
mult *= 16;
--q;
}
q = p + 3;
}
else {
// Decimal.
const char* q = p+2;
if ( !(*q) ) {
q = p + 2;
}
if (!(*q)) {
return 0;
}
q = strchr(q, SEMICOLON);
if (!q) {
return 0;
}
TIXMLASSERT(*q == SEMICOLON);
delta = q - p;
--q;
while (*q != terminator) {
uint32_t digit = 0;
if (*q >= '0' && *q <= '9') {
digit = *q - '0';
}
else if (hex && (*q >= 'a' && *q <= 'f')) {
digit = *q - 'a' + 10;
}
else if (hex && (*q >= 'A' && *q <= 'F')) {
digit = *q - 'A' + 10;
}
else {
return 0;
}
TIXMLASSERT(digit < radix);
q = strchr( q, SEMICOLON );
if ( !q ) {
return 0;
const unsigned int digitScaled = mult * digit;
ucs += digitScaled;
mult *= radix;
// Security check: could a value exist that is out of range?
// Easily; limit to the MAX_CODE_POINT, which also allows for a
// bunch of leading zeroes.
if (mult > MAX_CODE_POINT) {
mult = MAX_CODE_POINT;
}
TIXMLASSERT( *q == SEMICOLON );
delta = q-p;
--q;
while ( *q != '#' ) {
if ( *q >= '0' && *q <= '9' ) {
const unsigned int digit = *q - '0';
TIXMLASSERT( digit < 10 );
TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
const unsigned int digitScaled = mult * digit;
TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
ucs += digitScaled;
}
else {
return 0;
}
TIXMLASSERT( mult <= UINT_MAX / 10 );
mult *= 10;
--q;
}
}
// Out of range:
if (ucs > MAX_CODE_POINT) {
return 0;
}
// convert the UCS to UTF-8
ConvertUTF32ToUTF8( ucs, value, length );
ConvertUTF32ToUTF8(ucs, value, length);
if (length == 0) {
// If length is 0, there was an error. (Security? Bad input?)
// Fail safely.
return 0;
}
return p + delta + 1;
}
return p+1;
return p + 1;
}
void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
{
TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
@@ -705,7 +697,7 @@ bool XMLUtil::ToInt64(const char* str, int64_t* value)
bool XMLUtil::ToUnsigned64(const char* str, uint64_t* value) {
unsigned long long v = 0; // horrible syntax trick to make the compiler happy about %llu
if(TIXML_SSCANF(str, IsPrefixHex(str) ? "%llx" : "%llu", &v) == 1) {
*value = (uint64_t)v;
*value = static_cast<uint64_t>(v);
return true;
}
return false;
@@ -1982,7 +1974,7 @@ char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr )
}
// attribute.
if (XMLUtil::IsNameStartChar( (unsigned char) *p ) ) {
if (XMLUtil::IsNameStartChar( static_cast<unsigned char>(*p) ) ) {
XMLAttribute* attrib = CreateAttribute();
TIXMLASSERT( attrib );
attrib->_parseLineNum = _document->_parseCurLineNum;
+2 -2
View File
@@ -98,11 +98,11 @@ distribution.
/* Versioning, past 1.0.14:
http://semver.org/
*/
static const int TIXML2_MAJOR_VERSION = 10;
static const int TIXML2_MAJOR_VERSION = 11;
static const int TIXML2_MINOR_VERSION = 0;
static const int TIXML2_PATCH_VERSION = 0;
#define TINYXML2_MAJOR_VERSION 10
#define TINYXML2_MAJOR_VERSION 11
#define TINYXML2_MINOR_VERSION 0
#define TINYXML2_PATCH_VERSION 0
+7 -6
View File
@@ -9,20 +9,21 @@
namespace rpc {
std::vector<std::unique_ptr<const char>> XmlRpc::m_command_names;
std::vector<std::string> XmlRpc::m_command_names;
const char*
XmlRpc::store_command_name(const char* name) {
if (::strnlen(name, 8192) == 8192)
if (::strnlen(name, 1024 + 1) > 1024)
throw torrent::input_error("XMLRPC command name too long, limit is 8192 characters.");
for (const auto& itr : m_command_names) {
if (::strcmp(itr.get(), name) == 0)
return itr.get();
if (itr == name)
return itr.c_str();
}
m_command_names.push_back(std::unique_ptr<const char>(::strdup(name)));
return m_command_names.back().get();
m_command_names.push_back(name);
return m_command_names.back().c_str();
}
#ifndef HAVE_XMLRPC_C
+1 -1
View File
@@ -61,7 +61,7 @@ public:
private:
static const char* store_command_name(const char* name);
static std::vector<std::unique_ptr<const char>> m_command_names;
static std::vector<std::string> m_command_names;
slot_download m_slotFindDownload;
slot_file m_slotFindFile;
+17 -2
View File
@@ -272,12 +272,27 @@ object_to_xmlrpc(xmlrpc_env* env, const torrent::Object& object) {
#ifdef XMLRPC_HAVE_I8
if (rpc.dialect() != XmlRpc::dialect_generic)
return xmlrpc_i8_new(env, object.as_value());
#else
return xmlrpc_int_new(env, object.as_value());
#endif
return xmlrpc_int_new(env, object.as_value());
case torrent::Object::TYPE_STRING:
{
if (object.flags() & torrent::Object::flag_as_binary) {
// This causes decode-and-reencode for base64, as XMLRPC-C doesn't allow us to pass base64 strings.
if (object.flags() & torrent::Object::flag_base64) {
auto binary_data = torrent::utils::transform_from_base64_unsafe(object.as_string());
if (!binary_data.has_value())
throw torrent::input_error("invalid base64 string in base64-as-binary object");
return xmlrpc_base64_new(env, binary_data->size(), reinterpret_cast<const unsigned char*>(binary_data->data()));
}
return xmlrpc_base64_new(env, object.as_string().size(), reinterpret_cast<const unsigned char*>(object.as_string().c_str()));
}
#ifdef XMLRPC_HAVE_I8
// The versions that support I8 do implicit utf-8 validation.
xmlrpc_value* result = xmlrpc_string_new(env, object.as_string().c_str());
+59 -13
View File
@@ -11,9 +11,9 @@
#include <stdlib.h>
#include <rak/string_manip.h>
#include <torrent/exceptions.h>
#include <torrent/object.h>
#include <torrent/utils/string_manip.h>
#include "parse_commands.h"
#include "rpc/tinyxml2/tinyxml2.h"
@@ -67,56 +67,85 @@ xml_value_to_object(const tinyxml2::XMLNode* elem) {
if (elem == nullptr) {
throw rpc_error(XMLRPC_INTERNAL_ERROR, "received null element to convert");
}
if (std::strncmp(elem->Value(), "value", sizeof("value")) != 0) {
throw rpc_error(XMLRPC_INTERNAL_ERROR, "received non-value element to convert");
}
auto root_element = elem->FirstChild();
if (root_element == nullptr)
throw rpc_error(XMLRPC_TYPE_ERROR, "empty value element");
auto root_type = root_element->Value();
if (std::strncmp(root_type, "string", sizeof("string")) == 0) {
auto child_element = root_element->FirstChild();
if (child_element == nullptr) {
if (child_element == nullptr)
return torrent::Object("");
}
return torrent::Object(child_element->ToText()->Value());
} else if (std::strncmp(root_type, "int", sizeof("int")) == 0 ||
std::strncmp(root_type, "i4", sizeof("i4")) == 0 ||
std::strncmp(root_type, "i8", sizeof("i8")) == 0) {
return torrent::Object(element_to_int(root_element));
} else if (std::strncmp(root_type, "boolean", sizeof("boolean")) == 0) {
auto boolean_text = std::string(root_element->FirstChild()->ToText()->Value());
if (boolean_text == "1") {
auto child_element = root_element->FirstChild();
if (child_element == nullptr)
throw rpc_error(XMLRPC_TYPE_ERROR, "empty boolean element");
auto boolean_text = std::string(child_element->ToText()->Value());
if (boolean_text == "1")
return torrent::Object((int64_t)1);
} else if (boolean_text == "0") {
else if (boolean_text == "0")
return torrent::Object((int64_t)0);
}
throw rpc_error(XMLRPC_TYPE_ERROR, "unknown boolean value: " + boolean_text);
} else if (std::strncmp(root_type, "array", sizeof("array")) == 0) {
auto array_raw = torrent::Object::create_list();
auto& array = array_raw.as_list();
auto data_element = root_element->ToElement()->FirstChildElement("data");
if (data_element == nullptr)
throw rpc_error(XMLRPC_PARSE_ERROR, "could not find expected data element in array");
for (auto child = data_element->FirstChildElement("value"); child; child = child->NextSiblingElement("value")) {
for (auto child = data_element->FirstChildElement("value"); child; child = child->NextSiblingElement("value"))
array.push_back(xml_value_to_object(child));
}
return array_raw;
} else if (std::strncmp(root_type, "struct", sizeof("struct")) == 0) {
auto map_raw = torrent::Object::create_map();
auto& map = map_raw.as_map();
for (auto child = root_element->FirstChildElement("member"); child; child = child->NextSiblingElement("member")) {
auto key = child->FirstChildElement("name")->GetText();
map[key] = std::move(xml_value_to_object(child->FirstChildElement("value")));
auto name_element = child->FirstChildElement("name");
if (name_element == nullptr)
throw rpc_error(XMLRPC_PARSE_ERROR, "struct member missing name element");
map[name_element->GetText()] = std::move(xml_value_to_object(child->FirstChildElement("value")));
}
return map_raw;
} else if (std::strncmp(root_type, "base64", sizeof("base64")) == 0) {
auto child_element = root_element->FirstChild();
if (child_element == nullptr) {
if (child_element == nullptr)
return torrent::Object("");
}
return torrent::Object(utils::decode_base64(utils::remove_newlines(child_element->ToText()->Value())));
} else {
throw rpc_error(XMLRPC_INTERNAL_ERROR, "received unsupported value type: " + std::string(root_type));
}
return torrent::Object();
}
@@ -124,15 +153,32 @@ void
print_object_xml(const torrent::Object& obj, tinyxml2::XMLPrinter* printer) {
switch (obj.type()) {
case torrent::Object::TYPE_STRING:
if (obj.flags() & torrent::Object::flag_as_binary) {
// It is optimal for tinyxml2 to pass base64 strings directly from torrent::string_utf8 sources.
if (obj.flags() & torrent::Object::flag_base64) {
printer->OpenElement("base64", true);
printer->PushText(obj.as_string().c_str());
printer->CloseElement(true);
break;
}
printer->OpenElement("base64", true);
printer->PushText(torrent::utils::transform_to_base64(obj.as_string()).c_str());
printer->CloseElement(true);
break;
}
printer->OpenElement("string", true);
printer->PushText(obj.as_string().c_str());
printer->CloseElement(true);
break;
case torrent::Object::TYPE_VALUE:
printer->OpenElement("i8", true);
printer->PushText(std::to_string(obj.as_value()).c_str());
printer->CloseElement(true);
break;
case torrent::Object::TYPE_LIST:
printer->OpenElement("array", true);
printer->OpenElement("data", true);
+8 -15
View File
@@ -12,11 +12,6 @@
namespace scgi {
class ThreadScgiInternal {
public:
static ThreadScgi* thread_scgi() { return ThreadScgi::internal_thread_scgi(); }
};
ThreadScgi* ThreadScgi::m_thread_scgi{};
void
@@ -60,7 +55,7 @@ ThreadScgi::set_scgi(rpc::SCgi* scgi) {
change_rpc_log();
callback(nullptr, [this]() {
callback([this]() {
if (m_scgi == nullptr)
throw torrent::internal_error("Tried to start SCGI but object was not present.");
@@ -72,7 +67,7 @@ ThreadScgi::set_scgi(rpc::SCgi* scgi) {
void
ThreadScgi::set_rpc_log(const std::string& filename) {
callback(nullptr, [this, filename]() {
callback([this, filename]() {
m_rpc_log_filename = filename;
change_rpc_log();
});
@@ -127,15 +122,13 @@ ThreadScgi::next_timeout() {
namespace scgi_thread {
torrent::utils::Thread* thread() { return scgi::ThreadScgiInternal::thread_scgi(); }
std::thread::id thread_id() { return scgi::ThreadScgiInternal::thread_scgi()->thread_id(); }
torrent::system::Thread* thread() { return scgi::ThreadScgi::thread_scgi(); }
std::thread::id thread_id() { return scgi::ThreadScgi::thread_scgi()->thread_id(); }
void callback(void* target, std::function<void ()>&& fn) { scgi::ThreadScgiInternal::thread_scgi()->callback(target, std::move(fn)); }
void cancel_callback(void* target) { scgi::ThreadScgiInternal::thread_scgi()->cancel_callback(target); }
void cancel_callback_and_wait(void* target) { scgi::ThreadScgiInternal::thread_scgi()->cancel_callback_and_wait(target); }
void callback_interrupt(torrent::system::callback_id& id, std::function<void ()>&& fn) { scgi::ThreadScgi::thread_scgi()->callback_interrupt(id, std::move(fn)); }
rpc::SCgi* scgi() { return scgi::ThreadScgiInternal::thread_scgi()->scgi(); }
void set_scgi(rpc::SCgi* scgi) { scgi::ThreadScgiInternal::thread_scgi()->set_scgi(scgi); }
void set_rpc_log(const std::string& filename) { scgi::ThreadScgiInternal::thread_scgi()->set_rpc_log(filename); }
rpc::SCgi* scgi() { return scgi::ThreadScgi::thread_scgi()->scgi(); }
void set_scgi(rpc::SCgi* scgi) { scgi::ThreadScgi::thread_scgi()->set_scgi(scgi); }
void set_rpc_log(const std::string& filename) { scgi::ThreadScgi::thread_scgi()->set_rpc_log(filename); }
} // namespace scgi_thread
+4 -8
View File
@@ -3,7 +3,7 @@
#include <atomic>
#include <string>
#include <torrent/utils/thread.h>
#include <torrent/system/thread.h>
namespace rpc {
class SCgi;
@@ -11,9 +11,7 @@ class SCgi;
namespace scgi {
class ThreadScgiInternal;
class ThreadScgi : public torrent::utils::Thread {
class ThreadScgi : public torrent::system::Thread {
public:
static void create_thread();
@@ -28,8 +26,6 @@ public:
void set_rpc_log(const std::string& filename);
protected:
friend class ThreadScgiInternal;
ThreadScgi() = default;
static auto internal_thread_scgi() { return m_thread_scgi; }
@@ -43,9 +39,9 @@ private:
void task_touch_log();
void change_rpc_log();
static ThreadScgi* m_thread_scgi;
static ThreadScgi* m_thread_scgi;
std::atomic<rpc::SCgi*> m_scgi{nullptr};
std::atomic<rpc::SCgi*> m_scgi{};
std::string m_rpc_log_filename;
};
+2 -3
View File
@@ -10,6 +10,7 @@
#include <torrent/object_stream.h>
#include <torrent/utils/log.h>
#include <torrent/utils/resume.h>
#include <torrent/utils/string_manip.h>
#include "globals.h"
#include "core/download.h"
@@ -74,15 +75,13 @@ DownloadStorer::build_streams(bool skip_static) {
std::string
DownloadStorer::build_path(const std::string& session_path) {
auto info_hash = m_download->info()->info_hash();
if (session_path.empty())
throw torrent::internal_error("DownloadStorer::build_path() called with empty session path.");
if (session_path.back() != '/')
throw torrent::internal_error("DownloadStorer::build_path() session path missing trailing slash.");
return session_path + torrent::hash_string_to_hex_str(info_hash) + ".torrent";
return session_path + torrent::utils::transform_to_hex_str(m_download->info()->info_hash()) + ".torrent";
}
void
+104 -48
View File
@@ -5,6 +5,7 @@
#include <cassert>
#include <cstdlib>
#include <torrent/exceptions.h>
#include <torrent/system/callbacks.h>
#include <torrent/utils/log.h>
#include "globals.h"
@@ -14,10 +15,16 @@
#define LT_LOG(log_fmt, ...) \
lt_log_print(torrent::LOG_SESSION_EVENTS, "session-events: " log_fmt, __VA_ARGS__);
// TODO: Properly handle errors.
//
// TODO: If no more sockets can be opened, wait for a job to finish. if all is finished, use a
// timeout and try again.
namespace session {
SessionManager::SessionManager(torrent::utils::Thread* thread)
SessionManager::SessionManager(torrent::system::Thread* thread)
: m_thread(thread),
m_callback_id(torrent::system::make_callback_id()),
m_lockfile(std::make_unique<utils::Lockfile>()) {
}
@@ -68,19 +75,20 @@ SessionManager::save_resume_download(core::Download* download) {
{
std::unique_lock<std::mutex> lock(m_pending_builds_mutex);
// TODO: This is under the wrong lock.
if (!m_active)
throw torrent::internal_error("SessionManager::save_resume_download() called while not active.");
if (std::find(m_pending_builds.begin(), m_pending_builds.end(), download) != m_pending_builds.end()) {
// TODO: Do we need to make sure we're getting processed?
LT_LOG("download already in pending build of resume save : download:%p", download);
return;
}
if (m_pending_builds.empty())
torrent::main_thread::callback(this, [this]() { process_pending_resume_builds(false); });
m_pending_builds.push_back(download);
callback_pending_builds();
LT_LOG("build of resume save data queued : download:%p", download);
}
}
@@ -123,8 +131,7 @@ SessionManager::save_full_download(core::Download* download) {
LT_LOG("queued new full save request : download:%p", download);
}
if (!m_processing_saves_callback_scheduled.exchange(true))
session_thread::callback(this, [this]() { process_save_request(); });
callback_save_request();
}
void
@@ -216,12 +223,50 @@ SessionManager::cleanup() {
LT_LOG("session manager cleaned up", 0);
session_thread::cancel_callback(this);
torrent::main_thread::cancel_callback(this);
torrent::system::cancel_callback_and_wait(m_callback_id, session_thread::thread(), torrent::main_thread::thread());
}
void
SessionManager::process_pending_resume_builds(bool is_flushing) {
SessionManager::callback_pending_builds() {
if (m_save_request_counter >= max_concurrent_requests)
return;
if (m_callback_scheduled_process_pending_builds.exchange(true))
return;
torrent::main_thread::callback(m_callback_id, [this]() {
process_pending_builds(false);
});
}
void
SessionManager::callback_save_request() {
if (m_save_request_counter == 0)
return;
if (m_processing_save_counter >= max_concurrent_processing)
return;
if (m_callback_scheduled_process_saves_request.exchange(true))
return;
session_thread::callback(m_callback_id, [this]() {
process_save_request();
});
}
void
SessionManager::callback_finished_saves() {
if (m_callback_scheduled_process_finished_saves.exchange(true))
return;
session_thread::callback(m_callback_id, [this]() {
process_finished_saves();
});
}
void
SessionManager::process_pending_builds(bool is_flushing) {
assert(torrent::this_thread::thread() == torrent::main_thread::thread());
std::vector<SaveRequest> requests;
@@ -231,6 +276,8 @@ SessionManager::process_pending_resume_builds(bool is_flushing) {
{
std::unique_lock<std::mutex> lock(m_pending_builds_mutex);
m_callback_scheduled_process_pending_builds = false;
while (!m_pending_builds.empty()) {
if (!is_flushing && m_save_request_counter + requests.size() >= max_concurrent_requests)
break;
@@ -272,15 +319,14 @@ SessionManager::process_pending_resume_builds(bool is_flushing) {
}
m_save_requests.push_back(std::move(save_request));
m_save_request_counter = m_save_requests.size();
LT_LOG("queued new resume save request : download:%p", save_request.download);
}
m_save_request_counter = m_save_requests.size();
}
if (!is_flushing && !m_processing_saves_callback_scheduled.exchange(true))
session_thread::callback(this, [this]() { process_save_request_with_pending_callback(); });
if (!is_flushing)
callback_save_request();
}
void
@@ -290,25 +336,23 @@ SessionManager::process_save_request() {
if (m_path.empty())
throw torrent::internal_error("SessionManager::process_save_request() called with empty path.");
std::unique_lock<std::mutex> lock(m_mutex);
{
std::unique_lock<std::mutex> lock(m_mutex);
if (!m_active)
throw torrent::internal_error("SessionManager::process_save_request() called while not active.");
m_callback_scheduled_process_saves_request = false;
while (!m_save_requests.empty() && m_processing_saves.size() < max_concurrent_requests)
process_next_save_request_unsafe();
if (!m_active)
throw torrent::internal_error("SessionManager::process_save_request() called while not active.");
m_processing_saves_callback_scheduled = false;
}
while (!m_save_requests.empty()) {
if (m_processing_saves.size() >= max_concurrent_processing)
break;
void
SessionManager::process_save_request_with_pending_callback() {
process_save_request();
process_next_save_request_unsafe();
}
}
std::unique_lock<std::mutex> lock(m_pending_builds_mutex);
if (!m_pending_builds.empty())
torrent::main_thread::callback(this, [this]() { process_pending_resume_builds(false); });
callback_pending_builds();
}
void
@@ -319,19 +363,20 @@ SessionManager::process_next_save_request_unsafe() {
m_save_request_counter = m_save_requests.size();
auto itr = m_processing_saves.insert(m_processing_saves.end(), ProcessingSave{});
m_processing_save_counter = m_processing_saves.size();
itr->second = std::move(request);
itr->first = std::async(std::launch::async, [this, itr]() {
auto cleanup_fn = [this, itr]() {
std::unique_lock<std::mutex> lock(m_mutex);
if (m_finished_saves.empty())
session_thread::callback(this, [this]() { process_finished_saves(); });
callback_finished_saves();
m_finished_saves.push_back(std::move(*itr));
m_finished_condition.notify_all();
m_processing_saves.erase(itr);
m_processing_save_counter = m_processing_saves.size();
};
try {
@@ -346,6 +391,8 @@ SessionManager::process_next_save_request_unsafe() {
cleanup_fn();
});
LT_LOG("started save of download : download:%p path:%s", itr->second.download, itr->second.path.c_str());
}
void
@@ -354,6 +401,8 @@ SessionManager::process_finished_saves() {
std::unique_lock<std::mutex> lock(m_mutex);
m_callback_scheduled_process_finished_saves = false;
if (!m_active)
throw torrent::internal_error("SessionManager::process_finished_saves() called while not active.");
@@ -373,6 +422,7 @@ SessionManager::process_finished_saves() {
m_last_storage_error_message = torrent::this_thread::cached_time();
m_ignored_storage_error_count = 0;
continue;
} catch (torrent::internal_error& e) {
@@ -388,14 +438,18 @@ SessionManager::process_finished_saves() {
}
m_finished_saves.clear();
callback_save_request();
}
void
SessionManager::flush_all_and_wait_unsafe(std::unique_lock<std::mutex>& lock) {
LT_LOG("flushing all pending saves", 0);
// Caller already ensured pending builds are empty.
while (!m_save_requests.empty()) {
if (m_processing_saves.size() >= max_cleanup_processing) {
if (m_processing_save_counter >= max_cleanup_processing) {
m_finished_condition.wait(lock);
continue;
}
@@ -430,14 +484,17 @@ SessionManager::replace_save_request_unsafe(SaveRequest& save_request) {
// 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);
@@ -452,18 +509,6 @@ bool
SessionManager::remove_completely_unsafe(core::Download* download, std::unique_lock<std::mutex>& lock) {
assert(torrent::this_thread::thread() == torrent::main_thread::thread());
auto remove_requests = [this, download]() {
auto itr = std::remove_if(m_save_requests.begin(), m_save_requests.end(), [download](auto& req) {
return req.download == download;
});
if (itr == m_save_requests.end())
return false;
m_save_requests.erase(itr, m_save_requests.end());
return true;
};
auto remove_pending = [this, download]() {
std::unique_lock<std::mutex> pending_lock(m_pending_builds_mutex);
@@ -478,13 +523,28 @@ SessionManager::remove_completely_unsafe(core::Download* download, std::unique_l
return true;
};
auto remove_requests = [this, download]() {
auto itr = std::remove_if(m_save_requests.begin(), m_save_requests.end(), [download](auto& req) {
return req.download == download;
});
if (itr == m_save_requests.end())
return false;
m_save_requests.erase(itr, m_save_requests.end());
m_save_request_counter = m_save_requests.size();
return true;
};
bool removed_something = false;
if (remove_pending())
removed_something = true;
if (remove_requests())
if (remove_requests()) {
removed_something = true;
callback_pending_builds();
}
// This may block for a relatively long time if fdatasync is in use, however this is necessary.
while (true) {
@@ -507,9 +567,5 @@ SessionManager::remove_completely_unsafe(core::Download* download, std::unique_l
return removed_something;
}
// TODO: Properly handle errors.
// TODO: If no more sockets can be opened, wait for a job to finish. if all is finished, use a
// timeout and try again.
} // namespace session
+31 -22
View File
@@ -44,7 +44,7 @@ public:
constexpr static int max_concurrent_processing = 16;
constexpr static int max_cleanup_processing = 64;
SessionManager(torrent::utils::Thread* thread);
SessionManager(torrent::system::Thread* thread);
~SessionManager();
bool is_used() const;
@@ -74,9 +74,12 @@ protected:
void flush_all_pending_builds();
private:
void process_pending_resume_builds(bool is_flushing);
void callback_pending_builds();
void callback_save_request();
void callback_finished_saves();
void process_pending_builds(bool is_flushing);
void process_save_request();
void process_save_request_with_pending_callback();
void process_next_save_request_unsafe();
void process_finished_saves();
@@ -86,40 +89,46 @@ private:
bool replace_save_request_unsafe(SaveRequest& download);
bool remove_completely_unsafe(core::Download* download, std::unique_lock<std::mutex>& lock);
torrent::utils::Thread* m_thread;
using ProcessingSave = std::pair<std::future<void>, SaveRequest>;
bool m_freeze_info{};
std::string m_path;
bool m_use_fsyncdisk{true};
bool m_use_lock{true};
torrent::system::Thread* m_thread;
torrent::system::callback_id m_callback_id;
std::mutex m_mutex;
bool m_active{};
bool m_freeze_info{};
std::string m_path;
bool m_use_fsyncdisk{true};
bool m_use_lock{true};
typedef std::pair<std::future<void>, SaveRequest> ProcessingSave;
align_cacheline std::mutex m_mutex;
std::deque<SaveRequest> m_save_requests;
std::atomic<size_t> m_save_request_counter{};
std::list<ProcessingSave> m_processing_saves;
std::atomic<bool> m_processing_saves_callback_scheduled{};
std::condition_variable m_finished_condition;
std::vector<ProcessingSave> m_finished_saves;
bool m_active{};
std::deque<SaveRequest> m_save_requests;
std::atomic<size_t> m_save_request_counter{};
std::list<ProcessingSave> m_processing_saves;
std::atomic<size_t> m_processing_save_counter{};
std::condition_variable m_finished_condition;
std::vector<ProcessingSave> m_finished_saves;
std::atomic<bool> m_callback_scheduled_process_pending_builds{};
std::atomic<bool> m_callback_scheduled_process_saves_request{};
std::atomic<bool> m_callback_scheduled_process_finished_saves{};
std::unique_ptr<utils::Lockfile> m_lockfile;
std::chrono::microseconds m_last_storage_error_message{};
unsigned int m_ignored_storage_error_count{};
std::chrono::microseconds m_last_storage_error_message{};
unsigned int m_ignored_storage_error_count{};
// Pending builds are only ever locked by main thread.
std::mutex m_pending_builds_mutex;
std::deque<core::Download*> m_pending_builds;
std::mutex m_pending_builds_mutex;
std::deque<core::Download*> m_pending_builds;
};
inline bool SessionManager::is_used() const { return !m_path.empty(); }
inline std::string SessionManager::path() const { return m_path; }
inline bool SessionManager::use_fsyncdisk() const { return true; }
inline bool SessionManager::use_lock() const { return m_use_lock; }
inline void SessionManager::flush_all_pending_builds() { process_pending_resume_builds(true); }
inline void SessionManager::flush_all_pending_builds() { process_pending_builds(true); }
} // namespace session
+8 -12
View File
@@ -4,15 +4,11 @@
#include <torrent/exceptions.h>
#include "globals.h"
#include "session/session_manager.h"
namespace session {
class ThreadSessionInternal {
public:
static ThreadSession* thread_session() { return ThreadSession::internal_thread_session(); }
};
ThreadSession* ThreadSession::m_thread_session{nullptr};
void
@@ -74,14 +70,14 @@ ThreadSession::next_timeout() {
namespace session_thread {
torrent::utils::Thread* thread() { return session::ThreadSessionInternal::thread_session(); }
std::thread::id thread_id() { return session::ThreadSessionInternal::thread_session()->thread_id(); }
torrent::system::Thread* thread() { return session::ThreadSession::thread_session(); }
std::thread::id thread_id() { return thread()->thread_id(); }
void callback(void* target, std::function<void ()>&& fn) { session::ThreadSessionInternal::thread_session()->callback(target, std::move(fn)); }
void cancel_callback(void* target) { session::ThreadSessionInternal::thread_session()->cancel_callback(target); }
void cancel_callback_and_wait(void* target) { session::ThreadSessionInternal::thread_session()->cancel_callback_and_wait(target); }
void callback(std::function<void()>&& fn) { thread()->callback(std::move(fn)); }
void callback(torrent::system::callback_id& id, std::function<void()>&& fn) { thread()->callback(id, std::move(fn)); }
void cancel_callback(torrent::system::callback_id& id) { thread()->cancel_callback(id); }
session::SessionManager* manager() { return session::ThreadSessionInternal::thread_session()->manager(); }
std::string session_path() { return session::ThreadSessionInternal::thread_session()->manager()->path(); }
session::SessionManager* manager() { return session::ThreadSession::thread_session()->manager(); }
std::string session_path() { return manager()->path(); }
} // namespace session_thread
+2 -2
View File
@@ -1,14 +1,14 @@
#ifndef RTORRENT_SESSION_THREAD_SESSION_H
#define RTORRENT_SESSION_THREAD_SESSION_H
#include <torrent/utils/thread.h>
#include <torrent/system/thread.h>
namespace session {
class SessionManager;
class ThreadSessionInternal;
class ThreadSession : public torrent::utils::Thread {
class ThreadSession : public torrent::system::Thread {
public:
static void create_thread();
+220
View File
@@ -0,0 +1,220 @@
#include "config.h"
#include "setup.h"
#include <fstream>
#include <unistd.h>
#include <torrent/exceptions.h>
#include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
#include "control.h"
#include "globals.h"
#include "option_parser.h"
#include "core/download_factory.h"
#include "rpc/parse_commands.h"
#include "session/download_storer.h"
#include "utils/directory.h"
void do_panic(int signum);
void print_help();
//
// Parse command line args and config files:
//
int
parse_main_options(int argc, char** argv) {
try {
OptionParser optionParser;
// Converted.
optionParser.insert_flag('h', [](auto&) { print_help(); });
optionParser.insert_flag('n', [](auto&) { });
optionParser.insert_flag('D', [](auto&) { });
optionParser.insert_flag('I', [](auto&) { });
optionParser.insert_flag('K', [](auto&) { });
optionParser.insert_option('b', [](auto& arg) { rpc::call_command_set_string("network.bind_address.set", arg); });
optionParser.insert_option('d', [](auto& arg) { rpc::call_command_set_string("directory.default.set", arg); });
optionParser.insert_option('i', [](auto& arg) { rpc::call_command_set_string("ip", arg); });
optionParser.insert_option('p', [](auto& arg) { rpc::call_command_set_string("network.port_range.set", arg); });
optionParser.insert_option('s', [](auto& arg) { rpc::call_command_set_string("session", arg); });
optionParser.insert_option('O', [](auto& arg) { rpc::parse_command_single_std(arg); });
optionParser.insert_option_list('o', [](auto& arg1, auto& arg2) { rpc::call_command_set_std_string(arg1, arg2); });
return optionParser.process(argc, argv);
} catch (torrent::input_error& e) {
throw torrent::input_error("Failed to parse command line option: " + std::string(e.what()));
}
}
void
parse_config_file(int argc, char** argv, std::function<void (const std::string&)> parse_fn) {
if (OptionParser::has_flag('n', argc, argv))
return parse_fn("");
char* config_dir = std::getenv("XDG_CONFIG_HOME");
char* home_dir = std::getenv("HOME");
if (config_dir != nullptr && config_dir[0] == '/' && access((std::string(config_dir) + "/rtorrent/rtorrent.rc").c_str(), F_OK) != -1)
return parse_fn(std::string(config_dir) + "/rtorrent/rtorrent.rc");
if (home_dir == nullptr)
throw torrent::input_error("Could not find home directory, HOME environment variable not set.");
if (access((std::string(home_dir) + "/.config/rtorrent/rtorrent.rc").c_str(), F_OK) != -1)
return parse_fn(std::string(home_dir) + "/.config/rtorrent/rtorrent.rc");
return parse_fn(std::string(home_dir) + "/.rtorrent.rc");
}
void
config_comment_log(const std::string& command, const std::string& raw_args) {
std::vector<std::string> args;
size_t pos{};
while (pos < raw_args.size()) {
size_t next_pos = raw_args.find(',', pos);
if (next_pos == std::string::npos)
next_pos = raw_args.size();
args.push_back(raw_args.substr(pos, next_pos - pos));
pos = next_pos + 1;
}
if (command == "log.add_output")
log_add_group_output_str(args[0], args[1]);
else if (command == "log.open_file")
apply_log_open_str(0, args);
else if (command == "log.open_file.flush")
apply_log_open_str(log_flag_flush, args);
else if (command == "log.open_gz_file")
apply_log_open_str(log_flag_use_gz, args);
else if (command == "log.open_file_pid")
apply_log_open_str(log_flag_append_pid, args);
else if (command == "log.open_gz_file_pid")
apply_log_open_str(log_flag_append_pid | log_flag_use_gz, args);
else if (command == "log.append_file")
apply_log_open_str(log_flag_append_file, args);
else if (command == "log.append_file.flush")
apply_log_open_str(log_flag_append_file | log_flag_flush, args);
else if (command == "log.append_gz_file")
apply_log_open_str(log_flag_append_file | log_flag_use_gz, args);
else
throw torrent::input_error("Unknown log command: " + command);
}
// Call special commands in the format "# do:command=args" in the config file.
void
parse_config_file_comments(const std::string& path) {
std::fstream file(path, std::ios::in);
if (!file.is_open())
return;
std::string line;
while (std::getline(file, line)) {
if (line.size() <= 5 || line.compare(0, 5, "# do:") != 0)
continue;
auto equal_pos = line.find('=');
if (equal_pos == std::string::npos)
throw torrent::input_error("Invalid command in config file comment: " + line);
std::string command = line.substr(5, equal_pos - 5);
std::string args = line.substr(equal_pos + 1);
if (command.empty())
throw torrent::input_error("Invalid command in config file comment: " + line);
if (command.compare(0, 4, "log.") == 0)
config_comment_log(command, args);
else
throw torrent::input_error("Unknown command in config file comment: " + line);
lt_log_print(torrent::LOG_NOTICE, "Pre-config command: %s=%s", command.c_str(), args.c_str());
}
}
//
// Torrents:
//
void
load_session_torrents(const std::string& path) {
auto entries = session::DownloadStorer::get_formated_entries(path);
for (const auto& entry : entries) {
// We don't really support session torrents that are links. These
// would be overwritten anyway on exit, and thus not really be
// useful.
if (!entry.is_file())
continue;
auto* f = new core::DownloadFactory(control->core());
f->set_session(true);
f->set_init_load(true);
f->slot_finished([f](){ delete f; });
f->load(entries.path() + entry.s_name);
f->commit();
}
}
void
load_arg_torrents(char** first, char** last) {
for (; first != last; ++first) {
auto* f = new core::DownloadFactory(control->core());
f->set_start(true);
f->set_init_load(true);
f->slot_finished([f](){ delete f; });
f->load(*first);
f->commit();
}
}
//
// Logging:
//
void
log_add_group_output_str(const std::string& group_name, const std::string& output_id) {
int log_group = torrent::option_find_string(torrent::OPTION_LOG_GROUP, group_name.c_str());
torrent::log_add_group_output(log_group, output_id.c_str());
}
void
apply_log_open_str(int output_flags, const std::vector<std::string>& args) {
if (args.size() < 2)
throw torrent::input_error("Invalid number of arguments.");
auto output_id = args[0];
auto file_name = expand_path(args[1]);
if ((output_flags & log_flag_append_pid)) {
char buffer[32];
snprintf(buffer, 32, ".%li", (long)getpid());
file_name += buffer;
}
bool append = (output_flags & log_flag_append_file);
bool flush = (output_flags & log_flag_flush);
if ((output_flags & log_flag_use_gz))
torrent::log_open_gz_file_output(output_id.c_str(), file_name.c_str(), append);
else
torrent::log_open_file_output(output_id.c_str(), file_name.c_str(), append, flush);
for (size_t i = 2; i < args.size(); i++)
log_add_group_output_str(args[i], output_id);
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef RTORRENT_SETUP_H
#define RTORRENT_SETUP_H
#include <functional>
#include <string>
int parse_main_options(int argc, char** argv);
void parse_config_file(int argc, char** argv, std::function<void (const std::string&)> parse_fn);
void parse_config_file_comments(const std::string& path);
void load_session_torrents(const std::string& path);
void load_arg_torrents(char** first, char** last);
static constexpr int log_flag_use_gz = 0x1;
static constexpr int log_flag_append_pid = 0x2;
static constexpr int log_flag_append_file = 0x4;
static constexpr int log_flag_flush = 0x8;
void log_add_group_output_str(const std::string& group_name, const std::string& output_id);
void apply_log_open_str(int output_flags, const std::vector<std::string>& args);
#endif
+5 -5
View File
@@ -17,7 +17,7 @@ SignalHandler::slot_void SignalHandler::m_handlers[HIGHEST_SIGNAL];
void
SignalHandler::set_default(unsigned int signum) {
if (signum > HIGHEST_SIGNAL)
if (signum >= HIGHEST_SIGNAL)
throw std::logic_error("SignalHandler::set_default(...) received invalid signal value.");
signal(signum, SIG_DFL);
@@ -26,7 +26,7 @@ SignalHandler::set_default(unsigned int signum) {
void
SignalHandler::set_ignore(unsigned int signum) {
if (signum > HIGHEST_SIGNAL)
if (signum >= HIGHEST_SIGNAL)
throw std::logic_error("SignalHandler::set_ignore(...) received invalid signal value.");
signal(signum, SIG_IGN);
@@ -35,7 +35,7 @@ SignalHandler::set_ignore(unsigned int signum) {
void
SignalHandler::set_handler(unsigned int signum, slot_void slot) {
if (signum > HIGHEST_SIGNAL)
if (signum >= HIGHEST_SIGNAL)
throw std::logic_error("SignalHandler::set_handler(...) received invalid signal value.");
if (!slot)
@@ -54,7 +54,7 @@ SignalHandler::set_handler(unsigned int signum, slot_void slot) {
void
SignalHandler::set_sigaction_handler(unsigned int signum, handler_slot slot) {
if (signum > HIGHEST_SIGNAL)
if (signum >= HIGHEST_SIGNAL)
throw std::logic_error("SignalHandler::set_handler(...) received invalid signal value.");
struct sigaction sa;
@@ -69,7 +69,7 @@ SignalHandler::set_sigaction_handler(unsigned int signum, handler_slot slot) {
void
SignalHandler::caught(int signum) {
if ((unsigned)signum > HIGHEST_SIGNAL)
if ((unsigned)signum >= HIGHEST_SIGNAL)
throw std::logic_error("SignalHandler::caught(...) received invalid signal from the kernel, bork bork bork.");
if (!m_handlers[signum])
+1 -2
View File
@@ -2,7 +2,6 @@
#include <cassert>
#include <rak/string_manip.h>
#include <torrent/exceptions.h>
#include <torrent/chunk_manager.h>
#include <torrent/connection_manager.h>
@@ -251,7 +250,7 @@ Download::activate_display(Display displayType, bool focusDisplay) {
// Set title.
switch (displayType) {
case DISPLAY_MAX_SIZE: break;
default: control->ui()->window_title()->set_title(m_download->info()->name()); break;
default: control->ui()->window_title()->set_title(m_download->info()->name().str()); break;
}
control->display()->adjust_layout();
+15 -18
View File
@@ -1,35 +1,32 @@
#include "config.h"
#include "ui/download_list.h"
#include <cassert>
#include <sstream>
#include <rak/string_manip.h>
#include <torrent/exceptions.h>
#include <torrent/torrent.h>
#include <torrent/runtime/runtime.h>
#include <torrent/utils/log.h>
#include <torrent/utils/string_manip.h>
#include "control.h"
#include "core/download.h"
#include "core/download_list.h"
#include "core/manager.h"
#include "core/view.h"
#include "core/view_manager.h"
#include "display/window_log.h"
#include "display/window_title.h"
#include "input/bindings.h"
#include "input/manager.h"
#include "input/path_input.h"
#include "display/window_log.h"
#include "display/window_title.h"
#include "rpc/parse_commands.h"
#include "control.h"
#include "download.h"
#include "download_list.h"
#include "element_download_list.h"
#include "element_log_complete.h"
#include "element_string_list.h"
#include "root.h"
#include "ui/download.h"
#include "ui/element_download_list.h"
#include "ui/element_log_complete.h"
#include "ui/element_string_list.h"
#include "ui/root.h"
namespace ui {
@@ -180,7 +177,7 @@ DownloadList::activate_display(Display displayType) {
switch (displayType) {
case DISPLAY_DOWNLOAD_LIST:
control->ui()->window_title()->set_title("rTorrent " PACKAGE_VERSION "/" +
std::string(torrent::version()) + " - " +
std::string(torrent::runtime::version()) + " - " +
rpc::call_command_string("session.name"));
break;
case DISPLAY_LOG:
@@ -231,7 +228,7 @@ DownloadList::receive_view_input(Input type) {
while(ss.good()) {
std::getline(ss, view_name_var, ',');
if (current_view()->name() == rak::trim(view_name_var)) {
if (current_view()->name() == torrent::utils::trim_spaces_str(view_name_var)) {
control->core()->push_log_std("View '" + current_view()->name() + "' can't be filtered.");
return;
}
@@ -304,7 +301,7 @@ DownloadList::receive_exit_input(Input type) {
if ((*current_view()->focus())->is_open())
throw torrent::input_error("Cannot change root directory on an open download.");
rpc::call_command("d.directory.set", rak::trim(input->str()), rpc::make_target(*current_view()->focus()));
rpc::call_command("d.directory.set", torrent::utils::trim_spaces_str(input->str()), rpc::make_target(*current_view()->focus()));
control->core()->push_log_std("New root directory \"" + rpc::call_command_string("d.directory", rpc::make_target(*current_view()->focus())) + "\" for torrent.");
break;
-34
View File
@@ -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 <sundell.software@gmail.com>
#include "config.h"
#include <torrent/exceptions.h>
-34
View File
@@ -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 <sundell.software@gmail.com>
#ifndef RTORRENT_UI_ELEMENT_BASE_H
#define RTORRENT_UI_ELEMENT_BASE_H
+4 -39
View File
@@ -1,40 +1,5 @@
// 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 <sundell.software@gmail.com>
#include "config.h"
#include <rak/algorithm.h>
#include <torrent/exceptions.h>
#include <torrent/data/file.h>
#include <torrent/data/file_list.h>
@@ -80,10 +45,10 @@ element_file_list_create_info() {
element->push_back("File info:");
element->push_back("");
element->push_column("Filename:", te_command("fi.filename_last="));
element->push_back("");
element->push_column("Size:", te_command("if=$fi.is_file=,$convert.xb=$f.size_bytes=,---"));
element->push_column("Chunks:", te_command("cat=$f.completed_chunks=,\" / \",$f.size_chunks="));
element->push_column("Range:", te_command("cat=$f.range_first=,\" - \",$f.range_second="));
@@ -213,7 +178,7 @@ ElementFileList::receive_pagenext() {
m_selected = iterator(fl->begin());
} else {
m_selected = rak::advance_forward(m_selected, iterator(fl->end()), (m_window->height() - 1) / 2);
m_selected = display::advance_forward(m_selected, iterator(fl->end()), (m_window->height() - 1) / 2);
if (m_selected == iterator(fl->end()))
m_selected = --iterator(fl->end());
@@ -232,7 +197,7 @@ ElementFileList::receive_pageprev() {
if (m_selected == iterator(fl->begin()))
m_selected = --iterator(fl->end());
else
m_selected = rak::advance_backward(m_selected, iterator(fl->begin()), (m_window->height() - 1) / 2);
m_selected = display::advance_backward(m_selected, iterator(fl->begin()), (m_window->height() - 1) / 2);
update_itr();
}
-34
View File
@@ -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 <sundell.software@gmail.com>
#ifndef RTORRENT_UI_ELEMENT_FILE_LIST_H
#define RTORRENT_UI_ELEMENT_FILE_LIST_H
+9 -5
View File
@@ -2,7 +2,8 @@
#include <torrent/exceptions.h>
#include <torrent/torrent.h>
#include <torrent/utils/thread.h>
#include <torrent/system/callbacks.h>
#include <torrent/system/thread.h>
#include "display/frame.h"
#include "display/manager.h"
@@ -17,11 +18,12 @@ namespace ui {
ElementLogComplete::ElementLogComplete(torrent::log_buffer* l) :
m_log(l) {
unsigned int signal_index = torrent::main_thread::thread()->signal_bitfield()->add_signal(std::bind(&ElementLogComplete::received_update, this));
m_log->lock_and_set_update_slot([this]() {
if (m_log_updating.exchange(true) == true)
return;
m_log->lock_and_set_update_slot([signal_index]() {
torrent::main_thread::thread()->send_event_signal(signal_index, false);
});
torrent::main_thread::callback([this]() { received_update(); });
});
}
void
@@ -59,6 +61,8 @@ ElementLogComplete::window() {
void
ElementLogComplete::received_update() {
m_log_updating = false;
if (m_window != NULL)
m_window->mark_dirty();
}

Some files were not shown because too many files have changed in this diff Show More