mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-08-05 17:52:29 +00:00
Compare commits
79 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 088bf40bdf | |||
| 2f7a984992 | |||
| 840a57915e | |||
| 5e211c3a28 | |||
| 797a194abc | |||
| 501853cfbc | |||
| b0ef95592b | |||
| 26a7e9f545 | |||
| 0ed10984b3 | |||
| 5f09bb74de | |||
| c368b2de1e | |||
| 71ac256cb5 | |||
| b05b65bd65 | |||
| f05b48e228 | |||
| 1279bd9f7a | |||
| 981184574d | |||
| 2700b3141f | |||
| 4fec56a243 | |||
| dcf24711ef | |||
| 4e3ad1ab62 | |||
| b5a606649a | |||
| eb96876eb2 | |||
| a964b0a350 | |||
| 1adcf230d2 | |||
| 4235507292 | |||
| b7a21b1e9d | |||
| bd53959b40 | |||
| 5a4ba8bc32 | |||
| e64ec358fa | |||
| b78fd9aef5 | |||
| 21e399d099 | |||
| 08a907b547 | |||
| a0b8702895 | |||
| ad3c31862e | |||
| 2711e4c044 | |||
| 8d50aee96f | |||
| 71bb51c3fd | |||
| 63ea08bde6 | |||
| a82bdf22ac | |||
| 9f6731b4e2 | |||
| d935e0ffe9 | |||
| ba239bc8c5 | |||
| ea16276773 | |||
| f767053297 | |||
| 598914908f | |||
| 38fc815d52 | |||
| 674ae767aa | |||
| 0aaa470053 | |||
| 6f27159627 | |||
| de163293ab | |||
| fb4e775689 | |||
| 39f186e523 | |||
| 70e6964823 | |||
| 7ead88448b | |||
| 650f0299b5 | |||
| 5dfb2ae938 | |||
| f05a2ae520 | |||
| ecefdba734 | |||
| 87666199b3 | |||
| 9489793dcb | |||
| 2fa7568165 | |||
| f4b718f685 | |||
| b233e24465 | |||
| 289ab046bf | |||
| a8b6a47054 | |||
| 110591d5c1 | |||
| ae14baa357 | |||
| 0b0c824b4b | |||
| def6551488 | |||
| 787738e36a | |||
| 40c2d90c45 | |||
| 4a37fbde0b | |||
| 4bdeb58eb6 | |||
| 5dbb0020dc | |||
| 8f644e65dd | |||
| 16ff32b88c | |||
| 8806c06f9f | |||
| 60cfcd37c4 | |||
| a526ba58e9 |
@@ -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
|
||||
|
||||
@@ -15,6 +15,7 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get install -y \
|
||||
libcppunit-dev \
|
||||
zlib1g-dev \
|
||||
libcurl4-openssl-dev
|
||||
- name: Fetch libtorrent
|
||||
run: |
|
||||
|
||||
@@ -7,15 +7,7 @@ nobase_dist_pkgdata_DATA = \
|
||||
lua/rtorrent.lua
|
||||
|
||||
EXTRA_DIST= \
|
||||
rak/address_info.h \
|
||||
rak/algorithm.h \
|
||||
rak/error_number.h \
|
||||
rak/file_stat.h \
|
||||
rak/path.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
|
||||
|
||||
+9
-7
@@ -1,6 +1,6 @@
|
||||
m4_pattern_allow([PKG_CHECK_EXISTS])
|
||||
|
||||
AC_INIT([rtorrent],[0.16.3],[sundell.software@gmail.com])
|
||||
AC_INIT([rtorrent],[0.16.12],[sundell.software@gmail.com])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_MACRO_DIRS([scripts])
|
||||
@@ -11,7 +11,7 @@ LT_INIT
|
||||
|
||||
AC_PROG_CXX
|
||||
|
||||
AC_DEFINE([API_VERSION], [18], [api version])
|
||||
AC_DEFINE([API_VERSION], [20], [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)
|
||||
@@ -48,7 +48,8 @@ if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then
|
||||
fi
|
||||
|
||||
PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
|
||||
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.3])
|
||||
PKG_CHECK_MODULES([ZLIB], [zlib])
|
||||
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.12])
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
TORRENT_WITH_XMLRPC_C
|
||||
@@ -56,18 +57,19 @@ AC_LANG_POP(C++)
|
||||
|
||||
TORRENT_WITH_LUA
|
||||
TORRENT_WITH_TINYXML2
|
||||
TORRENT_WITH_SYSTEMD
|
||||
|
||||
if test ${with_xmlrpc_c+y} && test ${with_xmlrpc_tinyxml2+y}; then
|
||||
AC_MSG_ERROR([--with-xmlrpc-c and --with-xmlrpc-tinyxml2 cannot be used together. Please choose only one])
|
||||
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()
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
valgrind --leak-check=full --track-origins=yes --show-reachable=yes --suppressions=/Users/rakshasa/projects/rtorrent/doc/valgrind.suppression --gen-suppressions=all --log-file=valgrind.log /usr/local/bin/rtorrent
|
||||
@@ -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
@@ -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
|
||||
@@ -1,85 +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_ERROR_NUMBER_H
|
||||
#define RAK_ERROR_NUMBER_H
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
namespace rak {
|
||||
|
||||
class error_number {
|
||||
public:
|
||||
static const int e_access = EACCES;
|
||||
static const int e_again = EAGAIN;
|
||||
static const int e_connreset = ECONNRESET;
|
||||
static const int e_connaborted = ECONNABORTED;
|
||||
static const int e_deadlk = EDEADLK;
|
||||
|
||||
static const int e_noent = ENOENT;
|
||||
static const int e_nodev = ENODEV;
|
||||
static const int e_nomem = ENOMEM;
|
||||
static const int e_notdir = ENOTDIR;
|
||||
static const int e_isdir = EISDIR;
|
||||
|
||||
static const int e_intr = EINTR;
|
||||
|
||||
error_number() : m_errno(0) {}
|
||||
error_number(int e) : m_errno(e) {}
|
||||
|
||||
bool is_valid() const { return m_errno != 0; }
|
||||
|
||||
int value() const { return m_errno; }
|
||||
const char* c_str() const { return std::strerror(m_errno); }
|
||||
|
||||
bool is_blocked_momentary() const { return m_errno == e_again || m_errno == e_intr; }
|
||||
bool is_blocked_prolonged() const { return m_errno == e_deadlk; }
|
||||
|
||||
bool is_closed() const { return m_errno == e_connreset || m_errno == e_connaborted; }
|
||||
|
||||
bool is_bad_path() const { return m_errno == e_noent || m_errno == e_notdir || m_errno == e_access; }
|
||||
|
||||
static error_number current() { return errno; }
|
||||
static void clear_global() { errno = 0; }
|
||||
static void set_global(error_number err) { errno = err.m_errno; }
|
||||
|
||||
bool operator == (const error_number& e) const { return m_errno == e.m_errno; }
|
||||
|
||||
private:
|
||||
int m_errno;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,75 +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_FILE_STAT_H
|
||||
#define RAK_FILE_STAT_H
|
||||
|
||||
#include <string>
|
||||
#include <cinttypes>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace rak {
|
||||
|
||||
class file_stat {
|
||||
public:
|
||||
// Consider storing rak::error_number.
|
||||
|
||||
bool update(int fd) { return fstat(fd, &m_stat) == 0; }
|
||||
bool update(const char* filename) { return stat(filename, &m_stat) == 0; }
|
||||
bool update(const std::string& filename) { return update(filename.c_str()); }
|
||||
|
||||
bool update_link(const char* filename) { return lstat(filename, &m_stat) == 0; }
|
||||
bool update_link(const std::string& filename) { return update_link(filename.c_str()); }
|
||||
|
||||
bool is_regular() const { return S_ISREG(m_stat.st_mode); }
|
||||
bool is_directory() const { return S_ISDIR(m_stat.st_mode); }
|
||||
bool is_character() const { return S_ISCHR(m_stat.st_mode); }
|
||||
bool is_block() const { return S_ISBLK(m_stat.st_mode); }
|
||||
bool is_fifo() const { return S_ISFIFO(m_stat.st_mode); }
|
||||
bool is_link() const { return S_ISLNK(m_stat.st_mode); }
|
||||
bool is_socket() const { return S_ISSOCK(m_stat.st_mode); }
|
||||
|
||||
off_t size() const { return m_stat.st_size; }
|
||||
|
||||
time_t access_time() const { return m_stat.st_atime; }
|
||||
time_t change_time() const { return m_stat.st_ctime; }
|
||||
time_t modified_time() const { return m_stat.st_mtime; }
|
||||
|
||||
private:
|
||||
struct stat m_stat;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
-105
@@ -1,105 +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>
|
||||
|
||||
|
||||
// Various functions for manipulating file paths. Also consider making
|
||||
// a directory iterator.
|
||||
|
||||
#ifndef RAK_PATH_H
|
||||
#define RAK_PATH_H
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
namespace rak {
|
||||
|
||||
inline std::string
|
||||
path_expand(const std::string& path) {
|
||||
if (path.empty() || path[0] != '~')
|
||||
return path;
|
||||
|
||||
char* home = std::getenv("HOME");
|
||||
|
||||
if (home == NULL)
|
||||
return path;
|
||||
|
||||
return home + path.substr(1);
|
||||
}
|
||||
|
||||
// Don't inline this...
|
||||
//
|
||||
// Same strlcpy as found in *bsd.
|
||||
inline size_t
|
||||
strlcpy(char *dest, const char *src, size_t size) {
|
||||
size_t n = size;
|
||||
const char* first = src;
|
||||
|
||||
if (n != 0) {
|
||||
while (--n != 0)
|
||||
if ((*dest++ = *src++) == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
if (size != 0)
|
||||
*dest = '\0';
|
||||
|
||||
while (*src++)
|
||||
;
|
||||
}
|
||||
|
||||
return src - first - 1;
|
||||
}
|
||||
|
||||
inline char*
|
||||
path_expand(const char* src, char* first, char* last) {
|
||||
if (*src == '~') {
|
||||
char* home = std::getenv("HOME");
|
||||
|
||||
if (home == NULL)
|
||||
return first;
|
||||
|
||||
first += strlcpy(first, home, std::distance(first, last));
|
||||
|
||||
if (first > last)
|
||||
return last;
|
||||
|
||||
src++;
|
||||
}
|
||||
|
||||
return std::min(first + strlcpy(first, src, std::distance(first, last)), last);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -358,3 +358,20 @@ AC_DEFUN([TORRENT_DISABLE_PTHREAD_SETNAME_NP], [
|
||||
]
|
||||
)
|
||||
])
|
||||
|
||||
|
||||
AC_DEFUN([TORRENT_WITH_SYSTEMD], [
|
||||
AC_ARG_WITH(systemd,
|
||||
AS_HELP_STRING([--with-systemd],[enable systemd socket activation support [[default=no]]]),
|
||||
[
|
||||
if test "$withval" = "yes"; then
|
||||
PKG_CHECK_MODULES([SYSTEMD], [libsystemd],
|
||||
[
|
||||
CXXFLAGS="$CXXFLAGS $SYSTEMD_CFLAGS"
|
||||
LIBS="$LIBS $SYSTEMD_LIBS"
|
||||
AC_DEFINE(HAVE_SYSTEMD, 1, [Support for systemd socket activation.])
|
||||
],
|
||||
[AC_MSG_ERROR([libsystemd not found. Install libsystemd-dev (or the equivalent for your distribution).])])
|
||||
fi
|
||||
])
|
||||
])
|
||||
|
||||
+15
-7
@@ -13,8 +13,6 @@ libsub_root_a_SOURCES = \
|
||||
core/download_factory.h \
|
||||
core/download_list.cc \
|
||||
core/download_list.h \
|
||||
core/download_store.cc \
|
||||
core/download_store.h \
|
||||
core/http_queue.cc \
|
||||
core/http_queue.h \
|
||||
core/manager.cc \
|
||||
@@ -125,6 +123,16 @@ libsub_root_a_SOURCES = \
|
||||
rpc/tinyxml2/tinyxml2.cc \
|
||||
rpc/nlohmann/json.h \
|
||||
\
|
||||
scgi/thread_scgi.cc \
|
||||
scgi/thread_scgi.h \
|
||||
\
|
||||
session/download_storer.cc \
|
||||
session/download_storer.h \
|
||||
session/session_manager.cc \
|
||||
session/session_manager.h \
|
||||
session/thread_session.cc \
|
||||
session/thread_session.h \
|
||||
\
|
||||
ui/download.cc \
|
||||
ui/download.h \
|
||||
ui/download_list.cc \
|
||||
@@ -161,11 +169,11 @@ 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/socket_fd.cc \
|
||||
utils/socket_fd.h \
|
||||
\
|
||||
command_download.cc \
|
||||
command_dynamic.cc \
|
||||
@@ -189,10 +197,10 @@ libsub_root_a_SOURCES = \
|
||||
globals.h \
|
||||
option_parser.cc \
|
||||
option_parser.h \
|
||||
setup.cc \
|
||||
setup.h \
|
||||
signal_handler.cc \
|
||||
signal_handler.h \
|
||||
thread_worker.cc \
|
||||
thread_worker.h
|
||||
signal_handler.h
|
||||
|
||||
|
||||
AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir) -DPACKAGE_DATADIR=\"$(pkgdatadir)\"
|
||||
|
||||
+139
-29
@@ -5,9 +5,6 @@
|
||||
#include <functional>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <rak/file_stat.h>
|
||||
#include <rak/path.h>
|
||||
#include <rak/string_manip.h>
|
||||
#include <rak/regex.h>
|
||||
#include <torrent/rate.h>
|
||||
#include <torrent/throttle.h>
|
||||
@@ -21,13 +18,15 @@
|
||||
#include <torrent/net/types.h>
|
||||
#include <torrent/peer/connection_list.h>
|
||||
#include <torrent/peer/peer_list.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/download_store.h"
|
||||
#include "core/manager.h"
|
||||
#include "rpc/parse.h"
|
||||
#include "session/session_manager.h"
|
||||
|
||||
#include "globals.h"
|
||||
#include "control.h"
|
||||
@@ -47,6 +46,8 @@ retrieve_d_base_filename(core::Download* download) {
|
||||
|
||||
if (download->file_list()->is_multi_file())
|
||||
base = &download->file_list()->frozen_root_dir();
|
||||
else if (download->file_list()->empty())
|
||||
return std::string();
|
||||
else
|
||||
base = &download->file_list()->at(0)->frozen_path();
|
||||
|
||||
@@ -77,23 +78,23 @@ apply_d_change_link(core::Download* download, const torrent::Object::list_type&
|
||||
|
||||
if (type == "base_path") {
|
||||
target = rpc::call_command_string("d.base_path", rpc::make_target(download));
|
||||
link = rak::path_expand(prefix + rpc::call_command_string("d.base_path", rpc::make_target(download)) + postfix);
|
||||
link = expand_path(prefix + rpc::call_command_string("d.base_path", rpc::make_target(download)) + postfix);
|
||||
|
||||
} else if (type == "base_filename") {
|
||||
target = rpc::call_command_string("d.base_path", rpc::make_target(download));
|
||||
link = rak::path_expand(prefix + rpc::call_command_string("d.base_filename", rpc::make_target(download)) + postfix);
|
||||
link = expand_path(prefix + rpc::call_command_string("d.base_filename", rpc::make_target(download)) + postfix);
|
||||
|
||||
// } 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 = rak::path_expand(rpc::call_command_string("d.tied_to_file", rpc::make_target(download)));
|
||||
link = expand_path(rpc::call_command_string("d.tied_to_file", rpc::make_target(download)));
|
||||
|
||||
if (link.empty())
|
||||
return torrent::Object();
|
||||
|
||||
link = rak::path_expand(prefix + link + postfix);
|
||||
link = expand_path(prefix + link + postfix);
|
||||
target = rpc::call_command_string("d.base_path", rpc::make_target(download));
|
||||
|
||||
} else {
|
||||
@@ -109,7 +110,7 @@ apply_d_change_link(core::Download* download, const torrent::Object::list_type&
|
||||
|
||||
case 1:
|
||||
{
|
||||
rak::file_stat fileStat;
|
||||
torrent::utils::FileStat fileStat;
|
||||
errno = 0;
|
||||
|
||||
if (!fileStat.update_link(link) || !fileStat.is_link() || unlink(link.c_str()) == -1)
|
||||
@@ -131,7 +132,7 @@ apply_d_delete_tied(core::Download* download) {
|
||||
if (tie.empty())
|
||||
return torrent::Object();
|
||||
|
||||
if (::unlink(rak::path_expand(tie).c_str()) == -1)
|
||||
if (::unlink(expand_path(tie).c_str()) == -1)
|
||||
control->core()->push_log_std("Could not unlink tied file: " + std::string(std::strerror(errno)));
|
||||
|
||||
rpc::call_command("d.tied_to_file.set", std::string(), rpc::make_target(download));
|
||||
@@ -277,7 +278,7 @@ retrieve_d_bitfield(core::Download* download) {
|
||||
if (bitField->empty())
|
||||
return torrent::Object("");
|
||||
|
||||
return torrent::Object(rak::transform_hex(bitField->begin(), bitField->end()));
|
||||
return torrent::Object(torrent::utils::transform_to_hex_str(*bitField));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -302,7 +303,7 @@ apply_d_add_peer(core::Download* download, const std::string& arg) {
|
||||
if (port < 1 || port > 65535)
|
||||
throw torrent::input_error("Invalid port number.");
|
||||
|
||||
assert(std::this_thread::get_id() == torrent::main_thread::thread()->thread_id());
|
||||
assert(std::this_thread::get_id() == torrent::main_thread::thread_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) {
|
||||
@@ -327,7 +328,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;
|
||||
}
|
||||
|
||||
@@ -445,9 +447,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());
|
||||
|
||||
@@ -468,12 +472,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.");
|
||||
@@ -495,6 +501,19 @@ download_get_variable(core::Download* download, const char* first_key, const cha
|
||||
return download->bencode()->get_key(first_key).get_key(second_key);
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
download_get_value_or_zero(core::Download* download, const char* first_key, const char* second_key = NULL) {
|
||||
auto object = download_get_variable(download, first_key, second_key);
|
||||
|
||||
if (object.is_empty())
|
||||
return int64_t(0);
|
||||
|
||||
if (!object.is_value())
|
||||
throw torrent::bencode_error("Download variable is not a value.");
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
download_set_variable(core::Download* download, const torrent::Object& rawArgs, const char* first_key, const char* second_key = NULL) {
|
||||
if (second_key == NULL)
|
||||
@@ -600,12 +619,16 @@ d_list_remove(core::Download* download, const torrent::Object& rawArgs, const ch
|
||||
|
||||
#define CMD2_DL_TIMESTAMP(key, first_key, second_key) \
|
||||
CMD2_DL(key, std::bind(&download_get_variable, std::placeholders::_1, first_key, second_key)); \
|
||||
CMD2_DL_VALUE_P(key ".set", std::bind(&download_set_variable_value, \
|
||||
std::placeholders::_1, std::placeholders::_2, \
|
||||
first_key, second_key)); \
|
||||
CMD2_DL_VALUE_P(key ".set", std::bind(&download_set_variable_value, \
|
||||
std::placeholders::_1, std::placeholders::_2, \
|
||||
first_key, second_key)); \
|
||||
CMD2_DL_VALUE_P(key ".set_if_z", std::bind(&download_set_variable_value_ifz, \
|
||||
std::placeholders::_1, std::placeholders::_2, \
|
||||
first_key, second_key)); \
|
||||
CMD2_DL(key ".or_zero", [](core::Download* download, auto) { \
|
||||
return download_get_value_or_zero(download, first_key, second_key); }); \
|
||||
CMD2_DL_VALUE(key ".elapsed", [](core::Download* download, auto value) { \
|
||||
return download_get_value_or_zero(download, first_key, second_key).as_value() > value; });
|
||||
|
||||
#define CMD2_DL_VAR_STRING(key, first_key, second_key) \
|
||||
CMD2_DL(key, std::bind(&download_get_variable, std::placeholders::_1, first_key, second_key)); \
|
||||
@@ -625,9 +648,9 @@ 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.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", 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));
|
||||
@@ -683,8 +706,8 @@ initialize_command_download() {
|
||||
CMD2_DL_V ("d.erase", std::bind(&core::DownloadList::erase_ptr, control->core()->download_list(), std::placeholders::_1));
|
||||
CMD2_DL_V ("d.check_hash", std::bind(&core::DownloadList::check_hash, control->core()->download_list(), std::placeholders::_1));
|
||||
|
||||
CMD2_DL ("d.save_resume", std::bind(&core::DownloadStore::save_resume, control->core()->download_store(), std::placeholders::_1));
|
||||
CMD2_DL ("d.save_full_session", std::bind(&core::DownloadStore::save_full, control->core()->download_store(), std::placeholders::_1));
|
||||
CMD2_DL_V ("d.save_resume", [](core::Download* download, auto) { session_thread::manager()->save_resume_download(download); });
|
||||
CMD2_DL_V ("d.save_full_session", [](core::Download* download, auto) { session_thread::manager()->save_full_download(download); });
|
||||
|
||||
CMD2_DL_V ("d.update_priorities", CMD2_ON_DL(update_priorities));
|
||||
|
||||
@@ -868,4 +891,91 @@ initialize_command_download() {
|
||||
CMD2_DL_LIST ("t.multicall", std::bind(&t_multicall, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY_LIST ("p.call_target", std::bind(&p_call_target, std::placeholders::_2));
|
||||
|
||||
rpc::rpc.mark_safe("add_peer");
|
||||
rpc::rpc.mark_safe("d.hash");
|
||||
rpc::rpc.mark_safe("d.local_id");
|
||||
rpc::rpc.mark_safe("d.local_id_html");
|
||||
rpc::rpc.mark_safe("d.bitfield");
|
||||
rpc::rpc.mark_safe("d.base_path");
|
||||
rpc::rpc.mark_safe("d.base_filename");
|
||||
rpc::rpc.mark_safe("d.name");
|
||||
rpc::rpc.mark_safe("d.directory");
|
||||
rpc::rpc.mark_safe("d.directory_base");
|
||||
rpc::rpc.mark_safe("d.creation_date");
|
||||
rpc::rpc.mark_safe("d.load_date");
|
||||
rpc::rpc.mark_safe("d.up.rate");
|
||||
rpc::rpc.mark_safe("d.up.total");
|
||||
rpc::rpc.mark_safe("d.down.rate");
|
||||
rpc::rpc.mark_safe("d.down.total");
|
||||
rpc::rpc.mark_safe("d.skip.rate");
|
||||
rpc::rpc.mark_safe("d.skip.total");
|
||||
rpc::rpc.mark_safe("d.is_open");
|
||||
rpc::rpc.mark_safe("d.is_active");
|
||||
rpc::rpc.mark_safe("d.is_hash_checked");
|
||||
rpc::rpc.mark_safe("d.is_hash_checking");
|
||||
rpc::rpc.mark_safe("d.is_multi_file");
|
||||
rpc::rpc.mark_safe("d.is_private");
|
||||
rpc::rpc.mark_safe("d.is_pex_active");
|
||||
rpc::rpc.mark_safe("d.is_partially_done");
|
||||
rpc::rpc.mark_safe("d.is_not_partially_done");
|
||||
rpc::rpc.mark_safe("d.is_meta");
|
||||
rpc::rpc.mark_safe("d.peer_exchange");
|
||||
rpc::rpc.mark_safe("d.resume");
|
||||
rpc::rpc.mark_safe("d.pause");
|
||||
rpc::rpc.mark_safe("d.open");
|
||||
rpc::rpc.mark_safe("d.close");
|
||||
rpc::rpc.mark_safe("d.close.directly");
|
||||
rpc::rpc.mark_safe("d.erase");
|
||||
rpc::rpc.mark_safe("d.check_hash");
|
||||
rpc::rpc.mark_safe("d.save_resume");
|
||||
rpc::rpc.mark_safe("d.save_full_session");
|
||||
rpc::rpc.mark_safe("d.update_priorities");
|
||||
rpc::rpc.mark_safe("d.custom");
|
||||
rpc::rpc.mark_safe("d.custom1");
|
||||
rpc::rpc.mark_safe("d.custom2");
|
||||
rpc::rpc.mark_safe("d.custom3");
|
||||
rpc::rpc.mark_safe("d.custom4");
|
||||
rpc::rpc.mark_safe("d.custom5");
|
||||
rpc::rpc.mark_safe("d.size_bytes");
|
||||
rpc::rpc.mark_safe("d.size_chunks");
|
||||
rpc::rpc.mark_safe("d.size_pex");
|
||||
rpc::rpc.mark_safe("d.completed_bytes");
|
||||
rpc::rpc.mark_safe("d.bytes_done");
|
||||
rpc::rpc.mark_safe("d.peers_accounted");
|
||||
rpc::rpc.mark_safe("d.chunks_hashed");
|
||||
rpc::rpc.mark_safe("d.tracker_size");
|
||||
rpc::rpc.mark_safe("d.completed_chunks");
|
||||
rpc::rpc.mark_safe("d.left_bytes");
|
||||
rpc::rpc.mark_safe("d.chunk_size");
|
||||
rpc::rpc.mark_safe("d.priority");
|
||||
rpc::rpc.mark_safe("d.priority_str");
|
||||
rpc::rpc.mark_safe("d.state");
|
||||
rpc::rpc.mark_safe("d.state_changed");
|
||||
rpc::rpc.mark_safe("d.state_counter");
|
||||
rpc::rpc.mark_safe("d.connection_current");
|
||||
rpc::rpc.mark_safe("d.connection_leech");
|
||||
rpc::rpc.mark_safe("d.connection_seed");
|
||||
rpc::rpc.mark_safe("d.throttle_name");
|
||||
rpc::rpc.mark_safe("d.uploads_max");
|
||||
rpc::rpc.mark_safe("d.downloads_max");
|
||||
rpc::rpc.mark_safe("d.peers_min");
|
||||
rpc::rpc.mark_safe("d.peers_max");
|
||||
rpc::rpc.mark_safe("d.peers_connected");
|
||||
rpc::rpc.mark_safe("d.peers_not_connected");
|
||||
rpc::rpc.mark_safe("d.peers_complete");
|
||||
rpc::rpc.mark_safe("d.tracker_numwant");
|
||||
rpc::rpc.mark_safe("d.tracker_focus");
|
||||
rpc::rpc.mark_safe("d.message");
|
||||
rpc::rpc.mark_safe("d.hashing");
|
||||
rpc::rpc.mark_safe("d.hashing_failed");
|
||||
rpc::rpc.mark_safe("d.free_diskspace");
|
||||
rpc::rpc.mark_safe("d.views");
|
||||
rpc::rpc.mark_safe("d.views.remove");
|
||||
rpc::rpc.mark_safe("d.views.push_back_unique");
|
||||
rpc::rpc.mark_safe("d.ratio");
|
||||
rpc::rpc.mark_safe("f.multicall");
|
||||
rpc::rpc.mark_safe("p.multicall");
|
||||
rpc::rpc.mark_safe("p.call_target");
|
||||
rpc::rpc.mark_safe("t.multicall");
|
||||
}
|
||||
|
||||
@@ -455,4 +455,27 @@ initialize_command_dynamic() {
|
||||
CMD2_ANY ("strings.tracker_event", std::bind(&torrent::option_list_strings, torrent::OPTION_TRACKER_EVENT));
|
||||
CMD2_ANY ("strings.tracker_mode", std::bind(&torrent::option_list_strings, torrent::OPTION_TRACKER_MODE));
|
||||
// clang-format on
|
||||
|
||||
#ifdef HAVE_XMLRPC_TINYXML2
|
||||
rpc::rpc.mark_safe("system.listMethods");
|
||||
#endif
|
||||
|
||||
rpc::rpc.mark_safe("method.use_deprecated");
|
||||
rpc::rpc.mark_safe("method.const");
|
||||
rpc::rpc.mark_safe("method.has_key");
|
||||
rpc::rpc.mark_safe("method.list_keys");
|
||||
rpc::rpc.mark_safe("method.get");
|
||||
rpc::rpc.mark_safe("method.rlookup");
|
||||
rpc::rpc.mark_safe("catch");
|
||||
|
||||
rpc::rpc.mark_safe("strings.choke_heuristics");
|
||||
rpc::rpc.mark_safe("strings.choke_heuristics.upload");
|
||||
rpc::rpc.mark_safe("strings.choke_heuristics.download");
|
||||
rpc::rpc.mark_safe("strings.connection_type");
|
||||
rpc::rpc.mark_safe("strings.encryption");
|
||||
rpc::rpc.mark_safe("strings.ip_filter");
|
||||
rpc::rpc.mark_safe("strings.ip_tos");
|
||||
rpc::rpc.mark_safe("strings.log_group");
|
||||
rpc::rpc.mark_safe("strings.tracker_event");
|
||||
rpc::rpc.mark_safe("strings.tracker_mode");
|
||||
}
|
||||
|
||||
+23
-14
@@ -2,14 +2,12 @@
|
||||
|
||||
#include <functional>
|
||||
#include <cstdio>
|
||||
#include <rak/error_number.h>
|
||||
#include <rak/file_stat.h>
|
||||
#include <rak/path.h>
|
||||
#include <rak/string_manip.h>
|
||||
#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"
|
||||
@@ -67,10 +65,10 @@ apply_start_tied() {
|
||||
if (rpc::call_command_value("d.state", rpc::make_target(download)) == 1)
|
||||
continue;
|
||||
|
||||
rak::file_stat fs;
|
||||
torrent::utils::FileStat fs;
|
||||
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download));
|
||||
|
||||
if (!tied_to_file.empty() && fs.update(rak::path_expand(tied_to_file)))
|
||||
if (!tied_to_file.empty() && fs.update(expand_path(tied_to_file)))
|
||||
rpc::parse_command_single(rpc::make_target(download), "d.try_start=");
|
||||
}
|
||||
|
||||
@@ -83,10 +81,10 @@ apply_stop_untied() {
|
||||
if (rpc::call_command_value("d.state", rpc::make_target(download)) == 0)
|
||||
continue;
|
||||
|
||||
rak::file_stat fs;
|
||||
torrent::utils::FileStat fs;
|
||||
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download));
|
||||
|
||||
if (!tied_to_file.empty() && !fs.update(rak::path_expand(tied_to_file)))
|
||||
if (!tied_to_file.empty() && !fs.update(expand_path(tied_to_file)))
|
||||
rpc::parse_command_single(rpc::make_target(download), "d.try_stop=");
|
||||
}
|
||||
|
||||
@@ -96,10 +94,10 @@ apply_stop_untied() {
|
||||
torrent::Object
|
||||
apply_close_untied() {
|
||||
for (const auto& download : *control->core()->download_list()) {
|
||||
rak::file_stat fs;
|
||||
torrent::utils::FileStat fs;
|
||||
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download));
|
||||
|
||||
if (rpc::call_command_value("d.ignore_commands", rpc::make_target(download)) == 0 && !tied_to_file.empty() && !fs.update(rak::path_expand(tied_to_file)))
|
||||
if (rpc::call_command_value("d.ignore_commands", rpc::make_target(download)) == 0 && !tied_to_file.empty() && !fs.update(expand_path(tied_to_file)))
|
||||
rpc::parse_command_single(rpc::make_target(download), "d.try_close=");
|
||||
}
|
||||
|
||||
@@ -109,10 +107,10 @@ apply_close_untied() {
|
||||
torrent::Object
|
||||
apply_remove_untied() {
|
||||
for (auto itr = control->core()->download_list()->begin(); itr != control->core()->download_list()->end(); ) {
|
||||
rak::file_stat fs;
|
||||
torrent::utils::FileStat fs;
|
||||
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(*itr));
|
||||
|
||||
if (!tied_to_file.empty() && !fs.update(rak::path_expand(tied_to_file))) {
|
||||
if (!tied_to_file.empty() && !fs.update(expand_path(tied_to_file))) {
|
||||
// Need to clear tied_to_file so it doesn't try to delete it.
|
||||
rpc::call_command("d.tied_to_file.set", std::string(), rpc::make_target(*itr));
|
||||
|
||||
@@ -210,7 +208,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;
|
||||
@@ -301,7 +299,7 @@ directory_watch_added(const torrent::Object::list_type& args) {
|
||||
auto& command = args.back().as_string();
|
||||
|
||||
if (!control->directory_events()->open())
|
||||
throw torrent::input_error("Could not open inotify:" + std::string(rak::error_number::current().c_str()));
|
||||
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,
|
||||
@@ -346,4 +344,15 @@ 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));
|
||||
|
||||
rpc::rpc.mark_safe("start_tied");
|
||||
rpc::rpc.mark_safe("stop_untied");
|
||||
rpc::rpc.mark_safe("close_untied");
|
||||
rpc::rpc.mark_safe("remove_untied");
|
||||
|
||||
rpc::rpc.mark_safe("close_low_diskspace");
|
||||
rpc::rpc.mark_safe("close_low_diskspace.normal");
|
||||
rpc::rpc.mark_safe("download_list");
|
||||
rpc::rpc.mark_safe("d.multicall2");
|
||||
rpc::rpc.mark_safe("d.multicall.filtered");
|
||||
}
|
||||
|
||||
+28
-36
@@ -1,41 +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/error_number.h>
|
||||
#include <rak/path.h>
|
||||
#include <torrent/data/file.h>
|
||||
#include <torrent/data/file_list.h>
|
||||
#include <torrent/data/file_list_iterator.h>
|
||||
@@ -139,4 +103,32 @@ initialize_command_file() {
|
||||
|
||||
CMD2_FILEITR("fi.filename_last", std::bind(&apply_fi_filename_last, std::placeholders::_1));
|
||||
CMD2_FILEITR("fi.is_file", std::bind(&torrent::FileListIterator::is_file, std::placeholders::_1));
|
||||
|
||||
rpc::rpc.mark_safe("f.path");
|
||||
rpc::rpc.mark_safe("f.path_components");
|
||||
rpc::rpc.mark_safe("f.path_depth");
|
||||
rpc::rpc.mark_safe("f.frozen_path");
|
||||
rpc::rpc.mark_safe("f.offset");
|
||||
rpc::rpc.mark_safe("f.size_bytes");
|
||||
rpc::rpc.mark_safe("f.size_chunks");
|
||||
rpc::rpc.mark_safe("f.completed_chunks");
|
||||
rpc::rpc.mark_safe("f.range_first");
|
||||
rpc::rpc.mark_safe("f.range_second");
|
||||
rpc::rpc.mark_safe("f.priority");
|
||||
rpc::rpc.mark_safe("f.priority.set");
|
||||
rpc::rpc.mark_safe("f.is_created");
|
||||
rpc::rpc.mark_safe("f.is_open");
|
||||
rpc::rpc.mark_safe("f.is_create_queued");
|
||||
rpc::rpc.mark_safe("f.is_resize_queued");
|
||||
rpc::rpc.mark_safe("f.prioritize_first");
|
||||
rpc::rpc.mark_safe("f.prioritize_first.enable");
|
||||
rpc::rpc.mark_safe("f.prioritize_first.disable");
|
||||
rpc::rpc.mark_safe("f.prioritize_last");
|
||||
rpc::rpc.mark_safe("f.prioritize_last.enable");
|
||||
rpc::rpc.mark_safe("f.prioritize_last.disable");
|
||||
rpc::rpc.mark_safe("f.last_touched");
|
||||
rpc::rpc.mark_safe("f.match_depth_prev");
|
||||
rpc::rpc.mark_safe("f.match_depth_next");
|
||||
rpc::rpc.mark_safe("fi.filename_last");
|
||||
rpc::rpc.mark_safe("fi.is_file");
|
||||
}
|
||||
|
||||
@@ -385,4 +385,24 @@ initialize_command_groups() {
|
||||
CMD2_ANY ("choke_group.down.heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS,
|
||||
std::bind(&torrent::choke_queue::heuristics, CHOKE_GROUP(&torrent::choke_group::down_queue))));
|
||||
CMD2_ANY_LIST ("choke_group.down.heuristics.set", std::bind(&apply_cg_heuristics_set, std::placeholders::_2, false));
|
||||
|
||||
rpc::rpc.mark_safe("choke_group.list");
|
||||
rpc::rpc.mark_safe("choke_group.size");
|
||||
rpc::rpc.mark_safe("choke_group.index_of");
|
||||
rpc::rpc.mark_safe("choke_group.general.size");
|
||||
rpc::rpc.mark_safe("choke_group.tracker.mode");
|
||||
rpc::rpc.mark_safe("choke_group.up.rate");
|
||||
rpc::rpc.mark_safe("choke_group.down.rate");
|
||||
rpc::rpc.mark_safe("choke_group.up.max");
|
||||
rpc::rpc.mark_safe("choke_group.up.max.unlimited");
|
||||
rpc::rpc.mark_safe("choke_group.up.total");
|
||||
rpc::rpc.mark_safe("choke_group.up.queued");
|
||||
rpc::rpc.mark_safe("choke_group.up.unchoked");
|
||||
rpc::rpc.mark_safe("choke_group.up.heuristics");
|
||||
rpc::rpc.mark_safe("choke_group.down.max");
|
||||
rpc::rpc.mark_safe("choke_group.down.max.unlimited");
|
||||
rpc::rpc.mark_safe("choke_group.down.total");
|
||||
rpc::rpc.mark_safe("choke_group.down.queued");
|
||||
rpc::rpc.mark_safe("choke_group.down.unchoked");
|
||||
rpc::rpc.mark_safe("choke_group.down.heuristics");
|
||||
}
|
||||
|
||||
+18
-49
@@ -1,54 +1,18 @@
|
||||
// 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 <fstream>
|
||||
#include <rak/path.h>
|
||||
#include <torrent/peer/peer_list.h>
|
||||
#include <torrent/utils/log.h>
|
||||
#include <torrent/utils/option_strings.h>
|
||||
|
||||
#include "globals.h"
|
||||
#include "command_helpers.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <torrent/peer/peer_list.h>
|
||||
#include <torrent/utils/log.h>
|
||||
#include <torrent/utils/option_strings.h>
|
||||
|
||||
#include "globals.h"
|
||||
#include "command_helpers.h"
|
||||
|
||||
bool ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address_end);
|
||||
|
||||
@@ -65,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();
|
||||
@@ -89,7 +53,7 @@ apply_ip_tables_get(const torrent::Object::list_type& args) {
|
||||
if (table_itr == ip_tables.end())
|
||||
throw torrent::input_error("Could not find ip table.");
|
||||
|
||||
if (!ipv4_range_parse(address.c_str(), &address_start, &address_end))
|
||||
if (!ipv4_range_parse(address.c_str(), &address_start, &address_end))
|
||||
throw torrent::input_error("Invalid address format.");
|
||||
|
||||
if(!table_itr->table.defined(address_start, address_end))
|
||||
@@ -239,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;
|
||||
@@ -298,7 +267,7 @@ apply_ipv4_filter_get(const std::string& args) {
|
||||
uint32_t address_start;
|
||||
uint32_t address_end;
|
||||
|
||||
if (!ipv4_range_parse(args.c_str(), &address_start, &address_end))
|
||||
if (!ipv4_range_parse(args.c_str(), &address_start, &address_end))
|
||||
throw torrent::input_error("Invalid address format.");
|
||||
|
||||
if(!torrent::PeerList::ipv4_filter()->defined(address_start, address_end))
|
||||
@@ -326,8 +295,8 @@ apply_ipv4_filter_load(const torrent::Object::list_type& args) {
|
||||
std::string value_name = args.back().as_string();
|
||||
int value = torrent::option_find_string(torrent::OPTION_IP_FILTER, value_name.c_str());
|
||||
|
||||
std::fstream file(rak::path_expand(filename).c_str(), std::ios::in);
|
||||
|
||||
std::fstream file(expand_path(filename).c_str(), std::ios::in);
|
||||
|
||||
if (!file.is_open())
|
||||
throw torrent::input_error("Could not open ip filter file: " + filename);
|
||||
|
||||
|
||||
+63
-23
@@ -1,27 +1,26 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <fcntl.h>
|
||||
#include <functional>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <rak/path.h>
|
||||
#include <rak/error_number.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <torrent/torrent.h>
|
||||
#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/download_store.h"
|
||||
#include "core/manager.h"
|
||||
#include "rak/string_manip.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
#include "rpc/scgi.h"
|
||||
#include "session/session_manager.h"
|
||||
#include "utils/file_status_cache.h"
|
||||
|
||||
#include "globals.h"
|
||||
@@ -96,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;
|
||||
}
|
||||
@@ -121,6 +124,15 @@ group_insert(const torrent::Object::list_type& args) {
|
||||
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.max", "value", (int64_t)300));
|
||||
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.upload", "value", (int64_t)20 << 20));
|
||||
|
||||
rpc::rpc.mark_safe("group." + name + ".view");
|
||||
rpc::rpc.mark_safe("group." + name + ".view.set");
|
||||
rpc::rpc.mark_safe("group." + name + ".ratio.min");
|
||||
rpc::rpc.mark_safe("group." + name + ".ratio.min.set");
|
||||
rpc::rpc.mark_safe("group." + name + ".ratio.max");
|
||||
rpc::rpc.mark_safe("group." + name + ".ratio.max.set");
|
||||
rpc::rpc.mark_safe("group." + name + ".ratio.upload");
|
||||
rpc::rpc.mark_safe("group." + name + ".ratio.upload.set");
|
||||
|
||||
if (rpc::call_command_value("method.use_intermediate") == 3) {
|
||||
// Cleaned up in 0.16.1:
|
||||
|
||||
@@ -171,12 +183,16 @@ cmd_file_append(const torrent::Object::list_type& args) {
|
||||
|
||||
FILE* output = fopen(args.front().as_string().c_str(), "a");
|
||||
|
||||
if (output == NULL)
|
||||
throw torrent::input_error("Could not append to file '" + args.front().as_string() + "': " + rak::error_number::current().c_str());
|
||||
if (output == nullptr)
|
||||
throw torrent::input_error("Could not append to file '" + args.front().as_string() + "': " + std::strerror(errno));
|
||||
|
||||
file_print_list(++args.begin(), args.end(), output, file_print_delim_space);
|
||||
|
||||
fprintf(output, "\n");
|
||||
try {
|
||||
file_print_list(++args.begin(), args.end(), output, file_print_delim_space);
|
||||
fprintf(output, "\n");
|
||||
} catch (...) {
|
||||
fclose(output);
|
||||
throw;
|
||||
}
|
||||
fclose(output);
|
||||
return torrent::Object();
|
||||
}
|
||||
@@ -184,7 +200,6 @@ cmd_file_append(const torrent::Object::list_type& args) {
|
||||
void
|
||||
initialize_command_local() {
|
||||
core::DownloadList* dList = control->core()->download_list();
|
||||
core::DownloadStore* dStore = control->core()->download_store();
|
||||
torrent::ChunkManager* chunkManager = torrent::chunk_manager();
|
||||
torrent::FileManager* fileManager = torrent::file_manager();
|
||||
|
||||
@@ -193,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);
|
||||
@@ -212,7 +227,8 @@ initialize_command_local() {
|
||||
CMD2_ANY_VALUE_V ("system.files.advise_random.set", std::bind(&FM_t::set_advise_random, fileManager, std::placeholders::_2));
|
||||
CMD2_ANY ("system.files.advise_random.hashing", std::bind(&FM_t::advise_random_hashing, fileManager));
|
||||
CMD2_ANY_VALUE_V ("system.files.advise_random.hashing.set", std::bind(&FM_t::set_advise_random_hashing, fileManager, std::placeholders::_2));
|
||||
CMD2_VAR_BOOL ("system.files.session.fdatasync", true);
|
||||
CMD2_ANY ("system.files.session.fdatasync", [](auto, auto) { return session_thread::manager()->use_fsyncdisk(); });
|
||||
CMD2_ANY_VALUE_V ("system.files.session.fdatasync.set", [](auto, auto& value) { return session_thread::manager()->set_use_fsyncdisk(value); });
|
||||
|
||||
CMD2_ANY ("system.files.opened_counter", std::bind(&FM_t::files_opened_counter, fileManager));
|
||||
CMD2_ANY ("system.files.closed_counter", std::bind(&FM_t::files_closed_counter, fileManager));
|
||||
@@ -270,22 +286,25 @@ initialize_command_local() {
|
||||
CMD2_ANY ("pieces.hash.queue_size", std::bind(&torrent::main_thread::hash_queue_size));
|
||||
CMD2_VAR_BOOL ("pieces.hash.on_completion", true);
|
||||
|
||||
CMD2_VAR_STRING ("directory.default", "./");
|
||||
CMD2_VAR_STRING ("directory.default", "./");
|
||||
|
||||
CMD2_VAR_STRING ("session.name", "");
|
||||
CMD2_VAR_BOOL ("session.use_lock", true);
|
||||
CMD2_VAR_BOOL ("session.on_completion", true);
|
||||
CMD2_VAR_STRING ("session.name", "");
|
||||
CMD2_ANY ("session.path", [](auto, auto) { return session_thread::manager()->path(); });
|
||||
CMD2_ANY_STRING_V("session.path.set", [](auto, auto& str) { return session_thread::manager()->set_path(str); });
|
||||
CMD2_ANY ("session.use_lock", [](auto, auto) { return session_thread::manager()->use_lock(); });
|
||||
CMD2_ANY_VALUE_V ("session.use_lock.set", [](auto, auto& value) { return session_thread::manager()->set_use_lock(value); });
|
||||
CMD2_VAR_BOOL ("session.on_completion", true);
|
||||
|
||||
CMD2_ANY ("session.path", std::bind(&core::DownloadStore::path, dStore));
|
||||
CMD2_ANY_STRING_V("session.path.set", std::bind(&core::DownloadStore::set_path, dStore, std::placeholders::_2));
|
||||
CMD2_ANY_V ("session.save", [dList](auto, auto) { return dList->session_save(); });
|
||||
|
||||
CMD2_ANY_V ("session.save", std::bind(&core::DownloadList::session_save, dList));
|
||||
CMD2_ANY ("magnet.path", [](auto, auto) { return control->core()->magnet_path(); });
|
||||
CMD2_ANY_STRING_V("magnet.path.set", [](auto, auto& str) { return control->core()->set_magnet_path(str); });
|
||||
|
||||
#ifdef HAVE_LUA
|
||||
rpc::LuaEngine* lua_engine = control->lua_engine();
|
||||
|
||||
CMD2_ANY ("lua.execute", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, 0));
|
||||
CMD2_ANY ("lua.execute.str", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, rpc::LuaEngine::flag_string));
|
||||
CMD2_ANY ("lua.execute", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, 0));
|
||||
CMD2_ANY ("lua.execute.str", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, rpc::LuaEngine::flag_string));
|
||||
#endif
|
||||
|
||||
#define CMD2_EXECUTE(key, flags) \
|
||||
@@ -317,4 +336,25 @@ initialize_command_local() {
|
||||
CMD2_ANY_P("argument.3", std::bind(&rpc::command_base::argument_ref, 3));
|
||||
|
||||
CMD2_ANY_LIST ("group.insert", std::bind(&group_insert, std::placeholders::_2));
|
||||
|
||||
rpc::rpc.mark_safe("system.api_version");
|
||||
rpc::rpc.mark_safe("system.client_version");
|
||||
rpc::rpc.mark_safe("system.library_version");
|
||||
rpc::rpc.mark_safe("system.file.max_size");
|
||||
rpc::rpc.mark_safe("system.file.split_size");
|
||||
rpc::rpc.mark_safe("system.file.split_suffix");
|
||||
|
||||
rpc::rpc.mark_safe("directory.default");
|
||||
rpc::rpc.mark_safe("session.path");
|
||||
rpc::rpc.mark_safe("session.use_lock");
|
||||
rpc::rpc.mark_safe("session.on_completion");
|
||||
|
||||
rpc::rpc.mark_safe("pieces.sync.always_safe");
|
||||
rpc::rpc.mark_safe("pieces.sync.timeout");
|
||||
rpc::rpc.mark_safe("pieces.sync.timeout_safe");
|
||||
rpc::rpc.mark_safe("pieces.preload.type");
|
||||
rpc::rpc.mark_safe("pieces.preload.min_size");
|
||||
rpc::rpc.mark_safe("pieces.preload.min_rate");
|
||||
rpc::rpc.mark_safe("pieces.memory.max");
|
||||
rpc::rpc.mark_safe("pieces.hash.on_completion");
|
||||
}
|
||||
|
||||
+30
-55
@@ -8,53 +8,23 @@
|
||||
#include <torrent/utils/log.h>
|
||||
#include <torrent/utils/option_strings.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 "rak/path.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
|
||||
#include "globals.h"
|
||||
#include "control.h"
|
||||
#include "command_helpers.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;
|
||||
|
||||
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();
|
||||
|
||||
std::string output_id = (itr++)->as_string();
|
||||
std::string file_name = rak::path_expand((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);
|
||||
|
||||
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);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -62,9 +32,8 @@ torrent::Object
|
||||
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();
|
||||
}
|
||||
@@ -86,7 +55,7 @@ apply_log(const torrent::Object::string_type& arg, int logType) {
|
||||
}
|
||||
|
||||
if (!arg.empty()) {
|
||||
int logFd = open(rak::path_expand(arg).c_str(), O_WRONLY | O_APPEND | O_CREAT, 0644);
|
||||
int logFd = open(expand_path(arg).c_str(), O_WRONLY | O_APPEND | O_CREAT, 0644);
|
||||
|
||||
if (logFd < 0)
|
||||
throw torrent::input_error("Could not open execute log file.");
|
||||
@@ -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));
|
||||
}
|
||||
@@ -128,19 +100,22 @@ log_vmmap_dump(const std::string& str) {
|
||||
|
||||
void
|
||||
initialize_command_logging() {
|
||||
CMD2_ANY_LIST ("log.open_file", std::bind(&apply_log_open, 0, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.open_gz_file", std::bind(&apply_log_open, log_flag_use_gz, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.open_file_pid", std::bind(&apply_log_open, log_flag_append_pid, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.open_gz_file_pid", std::bind(&apply_log_open, log_flag_append_pid | log_flag_use_gz, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.append_file", std::bind(&apply_log_open, log_flag_append_file, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.append_gz_file", std::bind(&apply_log_open, log_flag_append_file, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.open_file", std::bind(&apply_log_open, 0, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.open_file.flush", std::bind(&apply_log_open, log_flag_flush, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.open_gz_file", std::bind(&apply_log_open, log_flag_use_gz, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.open_file_pid", std::bind(&apply_log_open, log_flag_append_pid, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.open_gz_file_pid", std::bind(&apply_log_open, log_flag_append_pid | log_flag_use_gz, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.append_file", std::bind(&apply_log_open, log_flag_append_file, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.append_file.flush", std::bind(&apply_log_open, log_flag_append_file | log_flag_flush, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.append_gz_file", std::bind(&apply_log_open, log_flag_append_file, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY_STRING_V("log.close", std::bind(&torrent::log_close_output_str, std::placeholders::_2));
|
||||
CMD2_ANY_STRING_V("log.close", std::bind(&torrent::log_close_output_str, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY_LIST ("log.add_output", std::bind(&apply_log_add_output, std::placeholders::_2));
|
||||
CMD2_ANY_LIST ("log.add_output", std::bind(&apply_log_add_output, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY_STRING ("log.execute", std::bind(&apply_log, std::placeholders::_2, 0));
|
||||
CMD2_ANY_STRING ("log.vmmap.dump", std::bind(&log_vmmap_dump, std::placeholders::_2));
|
||||
CMD2_ANY_STRING_V("log.rpc", std::bind(&ThreadWorker::set_rpc_log, worker_thread, std::placeholders::_2));
|
||||
CMD2_REDIRECT ("log.xmlrpc", "log.rpc"); // For backwards compatibility
|
||||
CMD2_ANY_STRING ("log.execute", std::bind(&apply_log, std::placeholders::_2, 0));
|
||||
CMD2_ANY_STRING ("log.vmmap.dump", std::bind(&log_vmmap_dump, std::placeholders::_2));
|
||||
CMD2_ANY_STRING_V("log.rpc", [](const auto&, const auto& str) { scgi_thread::set_rpc_log(str); });
|
||||
|
||||
CMD2_REDIRECT ("log.xmlrpc", "log.rpc"); // For backwards compatibility
|
||||
}
|
||||
|
||||
+131
-43
@@ -3,20 +3,22 @@
|
||||
#include <functional>
|
||||
#include <cstdio>
|
||||
#include <unistd.h>
|
||||
#include <rak/address_info.h>
|
||||
#include <rak/path.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/network_manager.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>
|
||||
|
||||
#include "globals.h"
|
||||
#include "control.h"
|
||||
#include "command_helpers.h"
|
||||
#include "core/download.h"
|
||||
#include "core/manager.h"
|
||||
#include "rpc/scgi.h"
|
||||
@@ -24,24 +26,25 @@
|
||||
#include "rpc/parse.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
|
||||
#include "globals.h"
|
||||
#include "control.h"
|
||||
#include "command_helpers.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();
|
||||
}
|
||||
@@ -53,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();
|
||||
}
|
||||
@@ -79,19 +82,18 @@ initialize_rpc_handlers() {
|
||||
|
||||
torrent::Object
|
||||
apply_scgi(const std::string& arg, int type) {
|
||||
if (worker_thread->scgi() != NULL)
|
||||
if (scgi_thread::scgi() != nullptr)
|
||||
throw torrent::input_error("SCGI already enabled.");
|
||||
|
||||
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;
|
||||
|
||||
@@ -105,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");
|
||||
|
||||
@@ -126,26 +129,75 @@ apply_scgi(const std::string& arg, int type) {
|
||||
|
||||
case 2:
|
||||
default:
|
||||
path = rak::path_expand(arg);
|
||||
path = expand_path(arg);
|
||||
|
||||
unlink(path.c_str());
|
||||
scgi->open_named(path);
|
||||
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());
|
||||
}
|
||||
|
||||
worker_thread->set_scgi(scgi);
|
||||
scgi_thread::set_scgi(scgi.release());
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_scgi_systemd() {
|
||||
#ifdef HAVE_SYSTEMD
|
||||
if (scgi_thread::scgi() != nullptr)
|
||||
throw torrent::input_error("SCGI already enabled.");
|
||||
|
||||
int n = sd_listen_fds(0);
|
||||
if (n < 1)
|
||||
throw torrent::input_error("No systemd socket(s) provided (sd_listen_fds returned " +
|
||||
std::to_string(n) + ").");
|
||||
|
||||
// Iterate over all provided fds. Use the first listening stream socket;
|
||||
// close the rest. The systemd docs say unused fds should be closed.
|
||||
int selected_fd = -1;
|
||||
for (int i = 0; i < n; i++) {
|
||||
int fd = SD_LISTEN_FDS_START + i;
|
||||
|
||||
if (selected_fd != -1) {
|
||||
::close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto err = sd_is_socket(fd, AF_UNSPEC, SOCK_STREAM, 1);
|
||||
|
||||
if (err < 0) {
|
||||
// Safe to ignore errors here - we just skip it and move on.
|
||||
::close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (err == 0) {
|
||||
// Not the socket we're looking for.
|
||||
::close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
selected_fd = fd;
|
||||
}
|
||||
|
||||
if (selected_fd == -1)
|
||||
throw torrent::input_error("No listening stream socket found among systemd-provided fds.");
|
||||
|
||||
initialize_rpc_handlers();
|
||||
|
||||
rpc::SCgi* scgi = new rpc::SCgi;
|
||||
scgi->open_fd(selected_fd);
|
||||
|
||||
scgi_thread::set_scgi(scgi);
|
||||
return torrent::Object();
|
||||
#else
|
||||
throw torrent::input_error("Systemd SCGI endpoint is not supported.");
|
||||
#endif
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
apply_xmlrpc_dialect(const std::string& arg) {
|
||||
int value;
|
||||
@@ -165,11 +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 nw_manager = torrent::runtime::network_manager();
|
||||
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));
|
||||
|
||||
@@ -178,12 +228,12 @@ initialize_command_network() {
|
||||
CMD2_VAR_BOOL ("network.port_random", true);
|
||||
CMD2_VAR_STRING ("network.port_range", "6881-6999");
|
||||
|
||||
CMD2_ANY ("network.listen.port", [nw_manager](auto, auto) { return nw_manager->listen_port(); });
|
||||
CMD2_ANY ("network.listen.port", [](auto, auto) { return torrent::runtime::listen_port(); });
|
||||
CMD2_ANY ("network.listen.backlog", [nw_config](auto, auto) { return nw_config->listen_backlog(); });
|
||||
CMD2_ANY_VALUE_V ("network.listen.backlog.set", [nw_config](auto, auto& value) { return nw_config->set_listen_backlog(value); });
|
||||
|
||||
CMD2_VAR_BOOL ("protocol.pex", true);
|
||||
CMD2_ANY_LIST ("protocol.encryption.set", [](auto, auto& args) { return apply_encryption(args); });
|
||||
CMD2_ANY_LIST ("protocol.encryption.set", [](auto, auto& args) { return apply_encryption(args); });
|
||||
|
||||
CMD2_VAR_STRING ("protocol.connection.leech", "leech");
|
||||
CMD2_VAR_STRING ("protocol.connection.seed", "seed");
|
||||
@@ -239,18 +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(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);
|
||||
@@ -265,4 +321,36 @@ initialize_command_network() {
|
||||
CMD2_ANY_VALUE_V ("network.block.outgoing.set", [nw_config](auto, auto& value) { return nw_config->set_block_outgoing(value); });
|
||||
CMD2_ANY ("network.prefer.ipv6", [nw_config](auto, auto) { return nw_config->is_prefer_ipv6(); });
|
||||
CMD2_ANY_VALUE_V ("network.prefer.ipv6.set", [nw_config](auto, auto& value) { return nw_config->set_prefer_ipv6(value); });
|
||||
|
||||
rpc::rpc.mark_safe("network.port_open");
|
||||
rpc::rpc.mark_safe("network.port_random");
|
||||
rpc::rpc.mark_safe("network.port_range");
|
||||
rpc::rpc.mark_safe("network.listen.port");
|
||||
rpc::rpc.mark_safe("network.listen.backlog");
|
||||
|
||||
rpc::rpc.mark_safe("network.http.current_open");
|
||||
rpc::rpc.mark_safe("network.http.max_cache_connections");
|
||||
rpc::rpc.mark_safe("network.http.max_host_connections");
|
||||
rpc::rpc.mark_safe("network.http.max_total_connections");
|
||||
|
||||
rpc::rpc.mark_safe("network.open_files");
|
||||
rpc::rpc.mark_safe("network.max_open_files");
|
||||
rpc::rpc.mark_safe("network.max_open_sockets");
|
||||
rpc::rpc.mark_safe("network.total_handshakes");
|
||||
|
||||
rpc::rpc.mark_safe("network.send_buffer.size");
|
||||
rpc::rpc.mark_safe("network.receive_buffer.size");
|
||||
rpc::rpc.mark_safe("network.bind_address");
|
||||
rpc::rpc.mark_safe("network.local_address");
|
||||
rpc::rpc.mark_safe("network.xmlrpc.size_limit");
|
||||
rpc::rpc.mark_safe("network.open_sockets");
|
||||
rpc::rpc.mark_safe("network.http.cacert");
|
||||
rpc::rpc.mark_safe("network.http.capath");
|
||||
rpc::rpc.mark_safe("network.http.proxy_address");
|
||||
rpc::rpc.mark_safe("network.proxy_address");
|
||||
rpc::rpc.mark_safe("network.scgi.dont_route");
|
||||
rpc::rpc.mark_safe("protocol.pex");
|
||||
|
||||
rpc::rpc.mark_safe("network.rpc.use_xmlrpc");
|
||||
rpc::rpc.mark_safe("network.rpc.use_jsonrpc");
|
||||
}
|
||||
|
||||
+36
-28
@@ -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,18 +35,20 @@ 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
|
||||
retrieve_p_completed_percent(torrent::Peer* peer) {
|
||||
if (peer->bitfield()->size_bits() == 0)
|
||||
return int64_t(0);
|
||||
return (100 * peer->bitfield()->size_set()) / peer->bitfield()->size_bits();
|
||||
}
|
||||
|
||||
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));
|
||||
@@ -80,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));
|
||||
|
||||
@@ -98,4 +80,30 @@ initialize_command_peer() {
|
||||
|
||||
CMD2_PEER_V("p.disconnect", std::bind(&torrent::Peer::disconnect, std::placeholders::_1, 0));
|
||||
CMD2_PEER_V("p.disconnect_delayed", std::bind(&torrent::Peer::disconnect, std::placeholders::_1, torrent::ConnectionList::disconnect_delayed));
|
||||
|
||||
rpc::rpc.mark_safe("p.address");
|
||||
rpc::rpc.mark_safe("p.port");
|
||||
rpc::rpc.mark_safe("p.client_version");
|
||||
rpc::rpc.mark_safe("p.options_str");
|
||||
rpc::rpc.mark_safe("p.id");
|
||||
rpc::rpc.mark_safe("p.id_html");
|
||||
rpc::rpc.mark_safe("p.up_rate");
|
||||
rpc::rpc.mark_safe("p.up_total");
|
||||
rpc::rpc.mark_safe("p.down_rate");
|
||||
rpc::rpc.mark_safe("p.down_total");
|
||||
rpc::rpc.mark_safe("p.peer_rate");
|
||||
rpc::rpc.mark_safe("p.peer_total");
|
||||
rpc::rpc.mark_safe("p.is_encrypted");
|
||||
rpc::rpc.mark_safe("p.is_incoming");
|
||||
rpc::rpc.mark_safe("p.is_obfuscated");
|
||||
rpc::rpc.mark_safe("p.is_snubbed");
|
||||
rpc::rpc.mark_safe("p.is_unwanted");
|
||||
rpc::rpc.mark_safe("p.is_preferred");
|
||||
rpc::rpc.mark_safe("p.snubbed");
|
||||
rpc::rpc.mark_safe("p.snubbed.set");
|
||||
rpc::rpc.mark_safe("p.banned");
|
||||
rpc::rpc.mark_safe("p.banned.set");
|
||||
rpc::rpc.mark_safe("p.completed_percent");
|
||||
rpc::rpc.mark_safe("p.disconnect");
|
||||
rpc::rpc.mark_safe("p.disconnect_delayed");
|
||||
}
|
||||
|
||||
+73
-12
@@ -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;
|
||||
}
|
||||
|
||||
@@ -199,4 +204,60 @@ initialize_command_throttle() {
|
||||
CMD2_ANY_STRING ("throttle.up.rate", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_up | throttle_info_rate));
|
||||
CMD2_ANY_STRING ("throttle.down.max", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_down | throttle_info_max));
|
||||
CMD2_ANY_STRING ("throttle.down.rate", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_down | throttle_info_rate));
|
||||
|
||||
rpc::rpc.mark_safe("throttle.unchoked_uploads");
|
||||
rpc::rpc.mark_safe("throttle.max_unchoked_uploads");
|
||||
rpc::rpc.mark_safe("throttle.unchoked_downloads");
|
||||
rpc::rpc.mark_safe("throttle.max_unchoked_downloads");
|
||||
|
||||
rpc::rpc.mark_safe("throttle.min_peers.normal");
|
||||
rpc::rpc.mark_safe("throttle.min_peers.normal.set");
|
||||
rpc::rpc.mark_safe("throttle.max_peers.normal");
|
||||
rpc::rpc.mark_safe("throttle.max_peers.normal.set");
|
||||
rpc::rpc.mark_safe("throttle.min_peers.seed");
|
||||
rpc::rpc.mark_safe("throttle.min_peers.seed.set");
|
||||
rpc::rpc.mark_safe("throttle.max_peers.seed");
|
||||
rpc::rpc.mark_safe("throttle.max_peers.seed.set");
|
||||
|
||||
rpc::rpc.mark_safe("throttle.min_uploads");
|
||||
rpc::rpc.mark_safe("throttle.min_uploads.set");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.set");
|
||||
rpc::rpc.mark_safe("throttle.min_downloads");
|
||||
rpc::rpc.mark_safe("throttle.min_downloads.set");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.set");
|
||||
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.div");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.div.set");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.div._val");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.div._val.set");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.global");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.global.set");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.global._val");
|
||||
rpc::rpc.mark_safe("throttle.max_uploads.global._val.set");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.div");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.div.set");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.div._val");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.div._val.set");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.global");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.global.set");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.global._val");
|
||||
rpc::rpc.mark_safe("throttle.max_downloads.global._val.set");
|
||||
|
||||
rpc::rpc.mark_safe("throttle.global_up.rate");
|
||||
rpc::rpc.mark_safe("throttle.global_up.total");
|
||||
rpc::rpc.mark_safe("throttle.global_up.max_rate");
|
||||
rpc::rpc.mark_safe("throttle.global_up.max_rate.set");
|
||||
rpc::rpc.mark_safe("throttle.global_up.max_rate.set_kb");
|
||||
rpc::rpc.mark_safe("throttle.global_down.rate");
|
||||
rpc::rpc.mark_safe("throttle.global_down.total");
|
||||
rpc::rpc.mark_safe("throttle.global_down.max_rate");
|
||||
rpc::rpc.mark_safe("throttle.global_down.max_rate.set");
|
||||
rpc::rpc.mark_safe("throttle.global_down.max_rate.set_kb");
|
||||
|
||||
rpc::rpc.mark_safe("throttle.up.max");
|
||||
rpc::rpc.mark_safe("throttle.up.rate");
|
||||
rpc::rpc.mark_safe("throttle.down.max");
|
||||
rpc::rpc.mark_safe("throttle.down.rate");
|
||||
}
|
||||
|
||||
+60
-38
@@ -3,9 +3,9 @@
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <netdb.h>
|
||||
#include <torrent/net/network_config.h>
|
||||
#include <torrent/net/network_manager.h>
|
||||
#include <torrent/net/resolver.h>
|
||||
#include <torrent/runtime/network_config.h>
|
||||
#include <torrent/runtime/network_manager.h>
|
||||
#include <torrent/tracker/dht_controller.h>
|
||||
#include <torrent/tracker/tracker.h>
|
||||
#include <torrent/utils/log.h>
|
||||
@@ -28,9 +28,6 @@ tracker_set_enabled(torrent::tracker::Tracker* tracker, bool state) {
|
||||
|
||||
torrent::Object
|
||||
apply_dht_add_node(const std::string& arg) {
|
||||
if (!torrent::runtime::network_manager()->is_dht_valid())
|
||||
throw torrent::input_error("DHT not enabled.");
|
||||
|
||||
int port;
|
||||
char dummy;
|
||||
char host[1024];
|
||||
@@ -45,7 +42,7 @@ apply_dht_add_node(const std::string& arg) {
|
||||
if (port < 1 || port > 65535)
|
||||
throw torrent::input_error("Invalid port number.");
|
||||
|
||||
assert(std::this_thread::get_id() == torrent::main_thread::thread()->thread_id());
|
||||
assert(std::this_thread::get_id() == torrent::main_thread::thread_id());
|
||||
|
||||
auto host_str = std::string(host);
|
||||
|
||||
@@ -59,7 +56,7 @@ apply_dht_add_node(const std::string& arg) {
|
||||
}
|
||||
|
||||
lt_log_print(torrent::LOG_DHT_CONTROLLER, "dht.add_node : %s", host_str.c_str());
|
||||
torrent::runtime::network_manager()->dht_add_peer_node(sa.get(), port);
|
||||
torrent::runtime::network_manager()->dht_add_bootstrap_node(host_str.c_str(), port);
|
||||
});
|
||||
|
||||
return torrent::Object();
|
||||
@@ -71,16 +68,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(); });
|
||||
@@ -111,36 +98,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();
|
||||
|
||||
@@ -149,9 +140,40 @@ 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));
|
||||
|
||||
rpc::rpc.mark_safe("t.url");
|
||||
rpc::rpc.mark_safe("t.group");
|
||||
rpc::rpc.mark_safe("t.id");
|
||||
rpc::rpc.mark_safe("t.type");
|
||||
rpc::rpc.mark_safe("t.is_usable");
|
||||
rpc::rpc.mark_safe("t.is_busy");
|
||||
rpc::rpc.mark_safe("t.is_enabled");
|
||||
rpc::rpc.mark_safe("t.is_enabled.set");
|
||||
rpc::rpc.mark_safe("t.is_extra_tracker");
|
||||
rpc::rpc.mark_safe("t.is_open");
|
||||
rpc::rpc.mark_safe("t.normal_interval");
|
||||
rpc::rpc.mark_safe("t.scrape_time_last");
|
||||
rpc::rpc.mark_safe("t.scrape_counter");
|
||||
rpc::rpc.mark_safe("t.success_time_last");
|
||||
rpc::rpc.mark_safe("t.success_counter");
|
||||
rpc::rpc.mark_safe("t.failed_time_last");
|
||||
rpc::rpc.mark_safe("t.failed_counter");
|
||||
rpc::rpc.mark_safe("t.activity_time_last");
|
||||
rpc::rpc.mark_safe("t.activity_time_next");
|
||||
rpc::rpc.mark_safe("t.scrape_complete");
|
||||
rpc::rpc.mark_safe("t.scrape_incomplete");
|
||||
rpc::rpc.mark_safe("t.scrape_downloaded");
|
||||
|
||||
rpc::rpc.mark_safe("dht.mode.set");
|
||||
rpc::rpc.mark_safe("dht.port");
|
||||
rpc::rpc.mark_safe("dht.override_port");
|
||||
rpc::rpc.mark_safe("dht.add_node");
|
||||
rpc::rpc.mark_safe("dht.statistics");
|
||||
rpc::rpc.mark_safe("trackers.numwant");
|
||||
rpc::rpc.mark_safe("trackers.use_udp");
|
||||
}
|
||||
|
||||
+46
-2
@@ -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.");
|
||||
}
|
||||
@@ -888,4 +908,28 @@ initialize_command_ui() {
|
||||
return control->object_storage()->get_str(display::color_vars[color_id]);
|
||||
});
|
||||
}
|
||||
|
||||
rpc::rpc.mark_safe("view.set_visible");
|
||||
rpc::rpc.mark_safe("view.set_not_visible");
|
||||
|
||||
rpc::rpc.mark_safe("cat");
|
||||
rpc::rpc.mark_safe("if");
|
||||
rpc::rpc.mark_safe("branch");
|
||||
rpc::rpc.mark_safe("and");
|
||||
rpc::rpc.mark_safe("or");
|
||||
rpc::rpc.mark_safe("not");
|
||||
rpc::rpc.mark_safe("value");
|
||||
rpc::rpc.mark_safe("compare");
|
||||
rpc::rpc.mark_safe("elapsed.less");
|
||||
rpc::rpc.mark_safe("elapsed.greater");
|
||||
|
||||
rpc::rpc.mark_safe("convert.gm_time");
|
||||
rpc::rpc.mark_safe("convert.gm_date");
|
||||
rpc::rpc.mark_safe("convert.time");
|
||||
rpc::rpc.mark_safe("convert.date");
|
||||
rpc::rpc.mark_safe("convert.elapsed_time");
|
||||
rpc::rpc.mark_safe("convert.kb");
|
||||
rpc::rpc.mark_safe("convert.mb");
|
||||
rpc::rpc.mark_safe("convert.xb");
|
||||
rpc::rpc.mark_safe("convert.throttle");
|
||||
}
|
||||
|
||||
+15
-13
@@ -5,11 +5,10 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <torrent/net/http_stack.h>
|
||||
#include <torrent/net/network_manager.h>
|
||||
#include <torrent/runtime/network_manager.h>
|
||||
#include <torrent/utils/directory_events.h>
|
||||
|
||||
#include "core/dht_manager.h"
|
||||
#include "core/download_store.h"
|
||||
#include "core/http_queue.h"
|
||||
#include "core/manager.h"
|
||||
#include "core/view_manager.h"
|
||||
@@ -26,6 +25,7 @@
|
||||
#include "rpc/lua.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
#include "rpc/object_storage.h"
|
||||
#include "session/session_manager.h"
|
||||
#include "ui/root.h"
|
||||
|
||||
Control::Control()
|
||||
@@ -58,7 +58,8 @@ Control::~Control() {
|
||||
|
||||
void
|
||||
Control::initialize() {
|
||||
worker_thread->start_thread();
|
||||
session_thread::thread()->start_thread();
|
||||
scgi_thread::thread()->start_thread();
|
||||
|
||||
display::Canvas::initialize();
|
||||
display::Window::slot_schedule([this](display::Window* w, std::chrono::microseconds t) { m_display->schedule(w, t); });
|
||||
@@ -68,7 +69,6 @@ Control::initialize() {
|
||||
torrent::net_thread::http_stack()->set_user_agent(USER_AGENT);
|
||||
|
||||
m_core->listen_open();
|
||||
m_core->download_store()->enable(rpc::call_command_value("session.use_lock"));
|
||||
m_core->set_hashing_view(*m_view_manager->find_throw("hashing"));
|
||||
|
||||
m_ui->init(this);
|
||||
@@ -86,7 +86,12 @@ Control::cleanup() {
|
||||
if(!display::Canvas::daemon())
|
||||
m_inputStdin->remove(torrent::this_thread::poll());
|
||||
|
||||
m_core->download_store()->disable();
|
||||
if (scgi_thread::thread()->is_active())
|
||||
scgi_thread::thread()->stop_thread_wait();
|
||||
|
||||
// Wait for all session files to be written.
|
||||
session_thread::manager()->flush_all_pending_builds();
|
||||
session_thread::thread()->stop_thread_wait();
|
||||
|
||||
m_ui->cleanup();
|
||||
m_core->cleanup();
|
||||
@@ -104,7 +109,7 @@ Control::cleanup_exception() {
|
||||
|
||||
bool
|
||||
Control::is_shutdown_completed() {
|
||||
if (!m_shutdownQuick || worker_thread->is_active())
|
||||
if (!m_shutdownQuick)
|
||||
return false;
|
||||
|
||||
// Tracker requests can be disowned, so wait for these to
|
||||
@@ -116,17 +121,17 @@ 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() {
|
||||
rpc::commands.call_catch("event.system.shutdown", rpc::make_target(), "shutdown", "System shutdown event action failed: ");
|
||||
|
||||
if (!m_shutdownQuick) {
|
||||
if (worker_thread->is_active())
|
||||
worker_thread->stop_thread_wait();
|
||||
if (scgi_thread::thread()->is_active())
|
||||
scgi_thread::thread()->stop_thread_wait();
|
||||
|
||||
if (!m_shutdownQuick) {
|
||||
torrent::runtime::network_manager()->listen_close();
|
||||
|
||||
m_directory_events->close();
|
||||
@@ -136,9 +141,6 @@ Control::handle_shutdown() {
|
||||
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown, 5s);
|
||||
|
||||
} else {
|
||||
if (worker_thread->is_active())
|
||||
worker_thread->stop_thread_wait();
|
||||
|
||||
m_core->shutdown(true);
|
||||
}
|
||||
|
||||
|
||||
+10
-9
@@ -6,17 +6,17 @@
|
||||
#include <sstream>
|
||||
#include <torrent/object.h>
|
||||
#include <torrent/object_stream.h>
|
||||
#include <torrent/net/network_manager.h>
|
||||
#include <torrent/rate.h>
|
||||
#include <torrent/runtime/network_manager.h>
|
||||
#include <torrent/tracker/dht_controller.h>
|
||||
#include <torrent/utils/log.h>
|
||||
|
||||
#include "control.h"
|
||||
#include "download.h"
|
||||
#include "download_store.h"
|
||||
#include "globals.h"
|
||||
#include "manager.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
#include "session/session_manager.h"
|
||||
|
||||
#define LT_LOG(log_fmt, ...) \
|
||||
lt_log_print_subsystem(torrent::LOG_DHT_CONTROLLER, "dht_manager", log_fmt, __VA_ARGS__);
|
||||
@@ -35,13 +35,13 @@ DhtManager::~DhtManager() {
|
||||
|
||||
void
|
||||
DhtManager::load_dht_cache() {
|
||||
if (m_start == dht_disable || !control->core()->download_store()->is_enabled()) {
|
||||
if (m_start == dht_disable || !session_thread::manager()->is_used()) {
|
||||
LT_LOG("ignoring cache file", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string cache_filename = control->core()->download_store()->path() + "rtorrent.dht_cache";
|
||||
std::fstream cache_stream(cache_filename.c_str(), std::ios::in | std::ios::binary);
|
||||
auto cache_filename = session_thread::manager()->path() + "rtorrent.dht_cache";
|
||||
auto cache_stream = std::fstream(cache_filename.c_str(), std::ios::in | std::ios::binary);
|
||||
|
||||
torrent::Object cache = torrent::Object::create_map();
|
||||
|
||||
@@ -115,14 +115,15 @@ DhtManager::stop_dht() {
|
||||
|
||||
void
|
||||
DhtManager::save_dht_cache() {
|
||||
if (!control->core()->download_store()->is_enabled())
|
||||
if (!session_thread::manager()->is_used())
|
||||
return;
|
||||
|
||||
if (!torrent::runtime::network_manager()->is_dht_valid())
|
||||
return;
|
||||
|
||||
std::string filename = control->core()->download_store()->path() + "rtorrent.dht_cache";
|
||||
std::string filename_tmp = filename + ".new";
|
||||
std::fstream cache_file(filename_tmp.c_str(), std::ios::out | std::ios::trunc);
|
||||
auto filename = session_thread::manager()->path() + "rtorrent.dht_cache";
|
||||
auto filename_tmp = filename + ".new";
|
||||
auto cache_file = std::fstream(filename_tmp.c_str(), std::ios::out | std::ios::trunc);
|
||||
|
||||
if (!cache_file.is_open())
|
||||
return;
|
||||
|
||||
+12
-26
@@ -1,25 +1,25 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "core/download.h"
|
||||
|
||||
#include <list>
|
||||
#include <rak/file_stat.h>
|
||||
#include <rak/path.h>
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/rate.h>
|
||||
#include <torrent/torrent.h>
|
||||
#include <torrent/tracker/tracker.h>
|
||||
#include <torrent/data/file.h>
|
||||
#include <torrent/data/file_list.h>
|
||||
#include <torrent/utils/file_stat.h>
|
||||
|
||||
#include "rpc/parse_commands.h"
|
||||
|
||||
#include "control.h"
|
||||
#include "download.h"
|
||||
#include "manager.h"
|
||||
#include "core/manager.h"
|
||||
|
||||
namespace core {
|
||||
|
||||
Download::Download(download_type d) :
|
||||
m_download(d) {
|
||||
Download::Download(download_type d)
|
||||
: m_download(d) {
|
||||
|
||||
m_download.info()->signal_tracker_success().push_back(std::bind(&Download::receive_tracker_msg, this, ""));
|
||||
m_download.info()->signal_tracker_failed().push_back(std::bind(&Download::receive_tracker_msg, this, std::placeholders::_1));
|
||||
@@ -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");
|
||||
@@ -118,13 +103,14 @@ void
|
||||
Download::set_root_directory(const std::string& path) {
|
||||
// If the download is open, hashed and has completed chunks make
|
||||
// sure to verify that the download files are still present.
|
||||
//
|
||||
//
|
||||
// This should ensure that no one tries to set the destination
|
||||
// directory 'after' moving files. In cases where the user wants to
|
||||
// override this behavior the download must first be closed or
|
||||
// 'd.directory_base.set' may be used.
|
||||
rak::file_stat file_stat;
|
||||
torrent::FileList* file_list = m_download.file_list();
|
||||
|
||||
torrent::utils::FileStat file_stat;
|
||||
torrent::FileList* file_list = m_download.file_list();
|
||||
|
||||
if (is_hash_checked() && file_list->completed_chunks() != 0 &&
|
||||
|
||||
@@ -136,11 +122,11 @@ Download::set_root_directory(const std::string& path) {
|
||||
rpc::call_command("d.state.set", (int64_t)0, rpc::make_target(this));
|
||||
control->core()->download_list()->close_directly(this);
|
||||
|
||||
throw torrent::input_error("Cannot change the directory of an open download atter the files have been moved.");
|
||||
throw torrent::input_error("Cannot change the directory of an open download after the files have been moved.");
|
||||
}
|
||||
|
||||
control->core()->download_list()->close_directly(this);
|
||||
file_list->set_root_dir(rak::path_expand(path));
|
||||
file_list->set_root_dir(expand_path(path));
|
||||
|
||||
bencode()->get_key("rtorrent").insert_key("directory", path);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <rak/path.h>
|
||||
#include <torrent/utils/log.h>
|
||||
#include <torrent/utils/resume.h>
|
||||
#include <torrent/object.h>
|
||||
@@ -16,11 +15,11 @@
|
||||
#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"
|
||||
#include "core/download.h"
|
||||
#include "core/download_store.h"
|
||||
#include "core/http_queue.h"
|
||||
#include "core/manager.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
@@ -124,7 +123,7 @@ DownloadFactory::receive_load() {
|
||||
receive_loaded();
|
||||
|
||||
} else {
|
||||
std::fstream stream(rak::path_expand(m_uri).c_str(), std::ios::in | std::ios::binary);
|
||||
std::fstream stream(expand_path(m_uri).c_str(), std::ios::in | std::ios::binary);
|
||||
|
||||
if (!stream.is_open())
|
||||
return receive_failed("Could not open file");
|
||||
@@ -159,8 +158,8 @@ DownloadFactory::receive_commit() {
|
||||
|
||||
void
|
||||
DownloadFactory::receive_success() {
|
||||
auto rtorrent_object = download_factory_load_stream((rak::path_expand(m_uri) + ".rtorrent").c_str());
|
||||
auto libtorrent_resume_object = download_factory_load_stream((rak::path_expand(m_uri) + ".libtorrent_resume").c_str());
|
||||
auto rtorrent_object = download_factory_load_stream((expand_path(m_uri) + ".rtorrent").c_str());
|
||||
auto libtorrent_resume_object = download_factory_load_stream((expand_path(m_uri) + ".libtorrent_resume").c_str());
|
||||
|
||||
uint32_t tracker_key;
|
||||
|
||||
@@ -239,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);
|
||||
@@ -255,11 +251,21 @@ DownloadFactory::receive_success() {
|
||||
rpc::call_command_value("system.file.split_size"),
|
||||
rpc::call_command_string("system.file.split_suffix"));
|
||||
|
||||
if (!rtorrent->has_key_string("directory"))
|
||||
rpc::call_command("d.directory.set", m_variables["directory"], rpc::make_target(download));
|
||||
else
|
||||
if (rtorrent->has_key_string("directory")) {
|
||||
rpc::call_command("d.directory_base.set", rtorrent->get_key("directory"), rpc::make_target(download));
|
||||
|
||||
} else if (download->download()->info()->is_meta_download()) {
|
||||
auto& metadata_path = control->core()->magnet_path();
|
||||
|
||||
if (!metadata_path.empty())
|
||||
rpc::call_command("d.directory.set", metadata_path, rpc::make_target(download));
|
||||
else
|
||||
rpc::call_command("d.directory.set", session_thread::session_path(), rpc::make_target(download));
|
||||
|
||||
} else {
|
||||
rpc::call_command("d.directory.set", m_variables["directory"], rpc::make_target(download));
|
||||
}
|
||||
|
||||
if (!m_session && m_variables["tied_to_file"].as_value())
|
||||
rpc::call_command("d.tied_to_file.set", m_uri.empty() ? m_variables["tied_file"] : m_uri, rpc::make_target(download));
|
||||
|
||||
@@ -319,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())
|
||||
|
||||
+18
-18
@@ -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"
|
||||
|
||||
@@ -22,10 +23,10 @@
|
||||
#include "view.h"
|
||||
#include "view_manager.h"
|
||||
|
||||
#include "dht_manager.h"
|
||||
#include "download.h"
|
||||
#include "download_list.h"
|
||||
#include "download_store.h"
|
||||
#include "core/dht_manager.h"
|
||||
#include "core/download.h"
|
||||
#include "core/download_list.h"
|
||||
#include "session/session_manager.h"
|
||||
#include "ui/root.h"
|
||||
|
||||
#define DL_TRIGGER_EVENT(download, event_name) \
|
||||
@@ -68,10 +69,8 @@ DownloadList::clear() {
|
||||
|
||||
void
|
||||
DownloadList::session_save() {
|
||||
unsigned int c = std::count_if(begin(), end(), [&](Download* d) { return control->core()->download_store()->save_resume(d); });
|
||||
|
||||
if (c != size())
|
||||
lt_log_print(torrent::LOG_ERROR, "Failed to save session torrents.");
|
||||
for (auto& download : *this)
|
||||
session_thread::manager()->save_resume_download(download);
|
||||
|
||||
control->dht_manager()->save_dht_cache();
|
||||
control->ui()->save_input_history();
|
||||
@@ -84,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(); });
|
||||
}
|
||||
@@ -199,10 +201,10 @@ DownloadList::erase(iterator itr) {
|
||||
(*itr)->set_hash_failed(true);
|
||||
|
||||
close(*itr);
|
||||
|
||||
control->core()->download_store()->remove(*itr);
|
||||
session_thread::manager()->remove_download(*itr);
|
||||
|
||||
DL_TRIGGER_EVENT(*itr, "event.download.erased");
|
||||
|
||||
for (auto v : *control->view_manager())
|
||||
v->erase(*itr);
|
||||
|
||||
@@ -639,10 +641,8 @@ DownloadList::confirm_finished(Download* download) {
|
||||
// the download.
|
||||
//
|
||||
// Obsolete.
|
||||
if (!download->is_active() && rpc::call_command_value("session.on_completion") != 0) {
|
||||
// torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"));
|
||||
control->core()->download_store()->save_resume(download);
|
||||
}
|
||||
if (!download->is_active() && rpc::call_command_value("session.on_completion") != 0)
|
||||
session_thread::manager()->save_resume_download(download);
|
||||
|
||||
// Send the completed request before resuming so we don't reset the
|
||||
// up/downloaded baseline.
|
||||
@@ -655,7 +655,7 @@ DownloadList::confirm_finished(Download* download) {
|
||||
|
||||
if (find(infohash) == end())
|
||||
return;
|
||||
|
||||
|
||||
// if (download->resume_flags() != ~uint32_t())
|
||||
// throw torrent::internal_error("DownloadList::confirm_finished(...) download->resume_flags() != ~uint32_t().");
|
||||
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
// DownloadStore handles the saving and listing of session torrents.
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <rak/error_number.h>
|
||||
#include <rak/path.h>
|
||||
#include <rak/string_manip.h>
|
||||
#include <torrent/utils/resume.h>
|
||||
#include <torrent/object.h>
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/torrent.h>
|
||||
#include <torrent/rate.h>
|
||||
#include <torrent/object_stream.h>
|
||||
|
||||
#include "utils/directory.h"
|
||||
|
||||
#include "download.h"
|
||||
#include "download_store.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
|
||||
namespace core {
|
||||
|
||||
void
|
||||
DownloadStore::enable(bool lock) {
|
||||
if (is_enabled())
|
||||
throw torrent::input_error("Session directory already enabled.");
|
||||
|
||||
if (m_path.empty())
|
||||
return;
|
||||
|
||||
if (lock)
|
||||
m_lockfile.set_path(m_path + "rtorrent.lock");
|
||||
else
|
||||
m_lockfile.set_path(std::string());
|
||||
|
||||
if (!m_lockfile.try_lock()) {
|
||||
if (rak::error_number::current().is_bad_path())
|
||||
throw torrent::input_error("Could not lock session directory: \"" + m_path + "\", " + rak::error_number::current().c_str());
|
||||
else
|
||||
throw torrent::input_error("Could not lock session directory: \"" + m_path + "\", held by \"" + m_lockfile.locked_by_as_string() + "\".");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DownloadStore::disable() {
|
||||
if (!is_enabled())
|
||||
return;
|
||||
|
||||
m_lockfile.unlock();
|
||||
}
|
||||
|
||||
void
|
||||
DownloadStore::set_path(const std::string& path) {
|
||||
if (is_enabled())
|
||||
throw torrent::input_error("Tried to change session directory while it is enabled.");
|
||||
|
||||
if (!path.empty() && *path.rbegin() != '/')
|
||||
m_path = rak::path_expand(path + '/');
|
||||
else
|
||||
m_path = rak::path_expand(path);
|
||||
}
|
||||
|
||||
bool
|
||||
DownloadStore::write_bencode(const std::string& filename, const torrent::Object& obj, uint32_t skip_mask) {
|
||||
int fd;
|
||||
torrent::Object tmp;
|
||||
std::fstream output(filename.c_str(), std::ios::out | std::ios::trunc);
|
||||
|
||||
if (!output.is_open())
|
||||
goto download_store_save_error;
|
||||
|
||||
torrent::object_write_bencode(&output, &obj, skip_mask);
|
||||
|
||||
if (!output.good())
|
||||
goto download_store_save_error;
|
||||
|
||||
output.close();
|
||||
|
||||
// Test the new file, to ensure it is a valid bencode string.
|
||||
output.open(filename.c_str(), std::ios::in);
|
||||
output >> tmp;
|
||||
|
||||
if (!output.good())
|
||||
goto download_store_save_error;
|
||||
|
||||
output.close();
|
||||
|
||||
// Ensure that the new file is actually written to the disk
|
||||
fd = ::open(filename.c_str(), O_WRONLY);
|
||||
if (fd < 0)
|
||||
goto download_store_save_error;
|
||||
|
||||
if (rpc::call_command_value("system.files.session.fdatasync")) {
|
||||
#ifdef __APPLE__
|
||||
fsync(fd);
|
||||
#else
|
||||
fdatasync(fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
::close(fd);
|
||||
|
||||
return true;
|
||||
|
||||
download_store_save_error:
|
||||
output.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DownloadStore::save(Download* d, int flags) {
|
||||
if (!is_enabled())
|
||||
return true;
|
||||
|
||||
torrent::Object* resume_base = &d->download()->bencode()->get_key("libtorrent_resume");
|
||||
torrent::Object* rtorrent_base = &d->download()->bencode()->get_key("rtorrent");
|
||||
|
||||
// Move this somewhere else?
|
||||
rtorrent_base->insert_key("chunks_done", d->download()->file_list()->completed_chunks());
|
||||
rtorrent_base->insert_key("chunks_wanted", d->download()->data()->wanted_chunks());
|
||||
rtorrent_base->insert_key("total_uploaded", d->info()->up_rate()->total());
|
||||
rtorrent_base->insert_key("total_downloaded", d->info()->down_rate()->total());
|
||||
|
||||
// Don't save for completed torrents when we've cleared the uncertain_pieces.
|
||||
torrent::resume_save_progress(*d->download(), *resume_base);
|
||||
torrent::resume_save_uncertain_pieces(*d->download(), *resume_base);
|
||||
|
||||
torrent::resume_save_addresses(*d->download(), *resume_base);
|
||||
torrent::resume_save_file_priorities(*d->download(), *resume_base);
|
||||
torrent::resume_save_tracker_settings(*d->download(), *resume_base);
|
||||
|
||||
// Temp fixing of all flags, move to a better place:
|
||||
resume_base->set_flags(torrent::Object::flag_session_data);
|
||||
rtorrent_base->set_flags(torrent::Object::flag_session_data);
|
||||
|
||||
std::string base_filename = create_filename(d);
|
||||
|
||||
if (!write_bencode(base_filename + ".libtorrent_resume.new", *resume_base, 0) ||
|
||||
!write_bencode(base_filename + ".rtorrent.new", *rtorrent_base, 0))
|
||||
return false;
|
||||
|
||||
::rename((base_filename + ".libtorrent_resume.new").c_str(), (base_filename + ".libtorrent_resume").c_str());
|
||||
::rename((base_filename + ".rtorrent.new").c_str(), (base_filename + ".rtorrent").c_str());
|
||||
|
||||
if (!(flags & flag_skip_static) &&
|
||||
write_bencode(base_filename + ".new", *d->bencode(), torrent::Object::flag_session_data))
|
||||
::rename((base_filename + ".new").c_str(), base_filename.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DownloadStore::remove(Download* d) {
|
||||
if (!is_enabled())
|
||||
return;
|
||||
|
||||
::unlink((create_filename(d) + ".libtorrent_resume").c_str());
|
||||
::unlink((create_filename(d) + ".rtorrent").c_str());
|
||||
::unlink(create_filename(d).c_str());
|
||||
}
|
||||
|
||||
// This also needs to check that it isn't a directory.
|
||||
bool
|
||||
not_correct_format(const utils::directory_entry& entry) {
|
||||
return !DownloadStore::is_correct_format(entry.s_name);
|
||||
}
|
||||
|
||||
utils::Directory
|
||||
DownloadStore::get_formated_entries() {
|
||||
if (!is_enabled())
|
||||
return utils::Directory();
|
||||
|
||||
utils::Directory d(m_path);
|
||||
|
||||
if (!d.update(utils::Directory::update_hide_dot))
|
||||
throw torrent::storage_error("core::DownloadStore::update() could not open directory \"" + m_path + "\"");
|
||||
|
||||
d.erase(std::remove_if(d.begin(), d.end(), [&](const utils::directory_entry& entry) { return not_correct_format(entry); }), d.end());
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
bool
|
||||
DownloadStore::is_correct_format(const std::string& f) {
|
||||
if (f.size() != 48 || f.substr(40) != ".torrent")
|
||||
return false;
|
||||
|
||||
for (std::string::const_iterator itr = f.begin(); itr != f.end() - 8; ++itr)
|
||||
if (!(*itr >= '0' && *itr <= '9') &&
|
||||
!(*itr >= 'A' && *itr <= 'F'))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string
|
||||
DownloadStore::create_filename(Download* d) {
|
||||
return m_path + rak::transform_hex(d->info()->hash().begin(), d->info()->hash().end()) + ".torrent";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
#ifndef RTORRENT_CORE_DOWNLOAD_STORE_H
|
||||
#define RTORRENT_CORE_DOWNLOAD_STORE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "utils/lockfile.h"
|
||||
|
||||
namespace utils {
|
||||
class Directory;
|
||||
}
|
||||
|
||||
namespace core {
|
||||
|
||||
class Download;
|
||||
|
||||
class DownloadStore {
|
||||
public:
|
||||
static const int flag_skip_static = 0x1;
|
||||
|
||||
bool is_enabled() { return m_lockfile.is_locked(); }
|
||||
|
||||
void enable(bool lock);
|
||||
void disable();
|
||||
|
||||
const std::string& path() const { return m_path; }
|
||||
void set_path(const std::string& path);
|
||||
|
||||
bool save(Download* d, int flags);
|
||||
bool save_full(Download* d) { return save(d, 0); }
|
||||
bool save_resume(Download* d) { return save(d, flag_skip_static); }
|
||||
void remove(Download* d);
|
||||
|
||||
// Currently shows all entries in the correct format.
|
||||
utils::Directory get_formated_entries();
|
||||
|
||||
static bool is_correct_format(const std::string& f);
|
||||
|
||||
private:
|
||||
std::string create_filename(Download* d);
|
||||
|
||||
bool write_bencode(const std::string& filename, const torrent::Object& obj, uint32_t skip_mask);
|
||||
|
||||
std::string m_path;
|
||||
utils::Lockfile m_lockfile;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
+104
-29
@@ -1,27 +1,26 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "core/manager.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <unistd.h>
|
||||
#include <sys/select.h>
|
||||
#include <rak/address_info.h>
|
||||
#include <rak/regex.h>
|
||||
#include <rak/path.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/network_manager.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"
|
||||
@@ -32,9 +31,7 @@
|
||||
#include "control.h"
|
||||
#include "core/download.h"
|
||||
#include "core/download_factory.h"
|
||||
#include "core/download_store.h"
|
||||
#include "core/http_queue.h"
|
||||
#include "core/manager.h"
|
||||
#include "core/view.h"
|
||||
|
||||
namespace core {
|
||||
@@ -50,11 +47,10 @@ Manager::push_log(const char* msg) {
|
||||
m_log_complete->lock_and_push_log(msg, strlen(msg), 0);
|
||||
}
|
||||
|
||||
Manager::Manager() :
|
||||
m_log_important(torrent::log_open_log_buffer("important")),
|
||||
Manager::Manager()
|
||||
: m_log_important(torrent::log_open_log_buffer("important")),
|
||||
m_log_complete(torrent::log_open_log_buffer("complete")) {
|
||||
|
||||
m_download_store = std::make_unique<DownloadStore>();
|
||||
m_download_list = std::make_unique<DownloadList>();
|
||||
m_file_status_cache = std::make_unique<FileStatusCache>();
|
||||
m_http_queue = std::make_unique<HttpQueue>();
|
||||
@@ -68,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)
|
||||
@@ -134,6 +144,16 @@ Manager::retrieve_throttle_value(const torrent::Object::string_type& name, bool
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Manager::set_magnet_path(const std::string& path) {
|
||||
if (path.empty())
|
||||
m_magnet_path.clear();
|
||||
else if (path.back() == '/')
|
||||
m_magnet_path = path;
|
||||
else
|
||||
m_magnet_path = path + '/';
|
||||
}
|
||||
|
||||
void
|
||||
Manager::cleanup() {
|
||||
// Need to disconnect log signals? Not really since we won't receive
|
||||
@@ -191,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');
|
||||
|
||||
@@ -203,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;
|
||||
}
|
||||
}
|
||||
@@ -233,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));
|
||||
@@ -249,7 +268,7 @@ Manager::try_create_download(const std::string& uri, int flags, const command_li
|
||||
!is_network_uri(uri) &&
|
||||
!is_magnet_uri(uri) &&
|
||||
!is_data_uri(uri) &&
|
||||
!file_status_cache()->insert(uri, 0))
|
||||
!file_status_cache()->insert(uri))
|
||||
return;
|
||||
|
||||
// Adding download.
|
||||
@@ -306,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;
|
||||
@@ -322,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 {
|
||||
|
||||
+9
-3
@@ -21,7 +21,6 @@ class FileStatusCache;
|
||||
|
||||
namespace core {
|
||||
|
||||
class DownloadStore;
|
||||
class HttpQueue;
|
||||
|
||||
typedef std::map<std::string, torrent::ThrottlePair> ThrottleMap;
|
||||
@@ -36,8 +35,9 @@ public:
|
||||
Manager();
|
||||
~Manager();
|
||||
|
||||
bool is_download_shutdown_completed();
|
||||
|
||||
DownloadList* download_list() { return m_download_list.get(); }
|
||||
DownloadStore* download_store() { return m_download_store.get(); }
|
||||
FileStatusCache* file_status_cache() { return m_file_status_cache.get(); }
|
||||
|
||||
HttpQueue* http_queue() { return m_http_queue.get(); }
|
||||
@@ -63,6 +63,9 @@ public:
|
||||
|
||||
void set_proxy_address(const std::string& addr);
|
||||
|
||||
const std::string& magnet_path();
|
||||
void set_magnet_path(const std::string& path);
|
||||
|
||||
void shutdown(bool force);
|
||||
|
||||
void push_log(const char* msg);
|
||||
@@ -95,7 +98,6 @@ private:
|
||||
void receive_hashing_changed();
|
||||
|
||||
std::unique_ptr<DownloadList> m_download_list;
|
||||
std::unique_ptr<DownloadStore> m_download_store;
|
||||
std::unique_ptr<FileStatusCache> m_file_status_cache;
|
||||
std::unique_ptr<HttpQueue> m_http_queue;
|
||||
|
||||
@@ -106,11 +108,15 @@ private:
|
||||
|
||||
torrent::log_buffer_ptr m_log_important;
|
||||
torrent::log_buffer_ptr m_log_complete;
|
||||
|
||||
std::string m_magnet_path;
|
||||
};
|
||||
|
||||
// Meh, cleanup.
|
||||
extern void receive_tracker_dump(const std::string& url, const char* data, size_t size);
|
||||
|
||||
inline const std::string& Manager::magnet_path() { return m_magnet_path; }
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -361,7 +361,7 @@ Frame::balance_row(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
|
||||
(*itr)->balance(x, y, m_width, std::min((*itr)->m_height, height));
|
||||
|
||||
y += (*itr)->m_height;
|
||||
height -= (*itr)->m_height;
|
||||
height -= std::min(height, (*itr)->m_height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,7 +436,7 @@ Frame::balance_column(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
|
||||
(*itr)->balance(x, y, std::min((*itr)->m_width, width), m_height);
|
||||
|
||||
x += (*itr)->m_width;
|
||||
width -= (*itr)->m_width;
|
||||
width -= std::min(width, (*itr)->m_width);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ Manager::receive_update() {
|
||||
m_force_redraw = false;
|
||||
|
||||
display::Canvas::resize_term(display::Canvas::term_size());
|
||||
keypad(stdscr, TRUE);
|
||||
Canvas::redraw_std();
|
||||
|
||||
adjust_layout();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+13
-14
@@ -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,10 +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/network_manager.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"
|
||||
@@ -151,7 +151,7 @@ print_download_status(char* first, char* last, core::Download* d) {
|
||||
|
||||
if (d->is_hash_checking()) {
|
||||
first = print_buffer(first, last, "Checking hash [%2i%%]",
|
||||
(d->download()->chunks_hashed() * 100) / d->download()->file_list()->size_chunks());
|
||||
d->download()->file_list()->size_chunks() != 0 ? (d->download()->chunks_hashed() * 100) / d->download()->file_list()->size_chunks() : 0);
|
||||
|
||||
} else if (d->tracker_controller().has_active_trackers_not_scrape()) {
|
||||
auto tracker = d->tracker_controller().find_if([](const auto& t) {
|
||||
@@ -209,7 +209,7 @@ print_download_info_compact(char* first, char* last, core::Download* d) {
|
||||
if (d->is_done())
|
||||
first = print_buffer(first, last, " 100%% ");
|
||||
else if (d->is_open())
|
||||
first = print_buffer(first, last, " %2u%% ",(d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks());
|
||||
first = print_buffer(first, last, " %2u%% ", d->download()->file_list()->size_chunks() != 0 ? (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks() : 0);
|
||||
else
|
||||
first = print_buffer(first, last, " ");
|
||||
|
||||
@@ -261,7 +261,7 @@ print_download_percentage_done(char* first, char* last, core::Download* d) {
|
||||
//return print_buffer(first, last, "[--%%]");
|
||||
return print_buffer(first, last, " ");
|
||||
else
|
||||
return print_buffer(first, last, "[%2u%%]", (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks());
|
||||
return print_buffer(first, last, "[%2u%%]", d->download()->file_list()->size_chunks() != 0 ? (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks() : 0);
|
||||
}
|
||||
|
||||
char*
|
||||
@@ -375,10 +375,9 @@ print_status_info(char* first, char* last) {
|
||||
first = print_status_throttle_rate(first, last, false, throttle_down_names, global_downrate);
|
||||
|
||||
first = print_buffer(first, last, " KB]");
|
||||
first = print_buffer(first, last, " [Port: %i]", (unsigned int)torrent::runtime::listen_port());
|
||||
|
||||
first = print_buffer(first, last, " [Port: %i]", (unsigned int)torrent::runtime::network_manager()->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 ");
|
||||
@@ -389,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());
|
||||
|
||||
@@ -428,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
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ WindowHttpQueue::WindowHttpQueue(core::HttpQueue* q) :
|
||||
set_active(false);
|
||||
|
||||
m_conn_insert = m_queue->signal_insert().insert(m_queue->signal_insert().end(), [this](auto h) { receive_insert(h); });
|
||||
m_conn_erase = m_queue->signal_erase().insert(m_queue->signal_insert().end(), [this](auto h) { receive_erase(h); });
|
||||
m_conn_erase = m_queue->signal_erase().insert(m_queue->signal_erase().end(), [this](auto h) { receive_erase(h); });
|
||||
|
||||
m_task_deactivate.slot() = [this] {
|
||||
if (!m_container.empty())
|
||||
|
||||
@@ -12,7 +12,7 @@ WindowInput::redraw() {
|
||||
m_canvas->erase();
|
||||
m_canvas->print(0, 0, "%s> %s", m_title.c_str(), m_input != NULL ? m_input->c_str() : "<NULL>");
|
||||
|
||||
if (m_focus)
|
||||
if (m_focus && m_input != NULL)
|
||||
m_canvas->set_attr(m_input->get_pos() + 2 + m_title.size(), 0, 1, A_REVERSE, COLOR_PAIR(0));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <ctime>
|
||||
#include <torrent/torrent.h>
|
||||
#include <torrent/utils/thread.h>
|
||||
#include <torrent/system/thread.h>
|
||||
|
||||
#include "canvas.h"
|
||||
#include "utils.h"
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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) {
|
||||
@@ -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(),
|
||||
|
||||
+23
-1
@@ -2,7 +2,29 @@
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
rpc::ip_table_list ip_tables;
|
||||
|
||||
Control* control{};
|
||||
ThreadWorker* worker_thread{};
|
||||
|
||||
std::string
|
||||
expand_path(const std::string& path) {
|
||||
if (path.empty())
|
||||
return std::string();
|
||||
|
||||
if (path[0] == '~') {
|
||||
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 path, HOME environment variable not set.");
|
||||
|
||||
return home + path.substr(1);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
+43
-4
@@ -1,14 +1,53 @@
|
||||
#ifndef TORRENT_GLOBALS_H
|
||||
#define TORRENT_GLOBALS_H
|
||||
|
||||
#include "thread_worker.h"
|
||||
#include <torrent/common.h>
|
||||
|
||||
#include "rpc/ip_table_list.h"
|
||||
|
||||
class Control;
|
||||
|
||||
extern rpc::ip_table_list ip_tables;
|
||||
extern rpc::ip_table_list ip_tables;
|
||||
extern Control* control;
|
||||
|
||||
extern Control* control;
|
||||
extern ThreadWorker* worker_thread;
|
||||
std::string expand_path(const std::string& path);
|
||||
|
||||
namespace rpc {
|
||||
class SCgi;
|
||||
}
|
||||
|
||||
namespace session {
|
||||
class SessionManager;
|
||||
}
|
||||
|
||||
namespace scgi_thread {
|
||||
|
||||
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);
|
||||
|
||||
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::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);
|
||||
|
||||
session::SessionManager* manager();
|
||||
std::string session_path();
|
||||
|
||||
} // namespace torrent::session_thread
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,12 +14,14 @@ public:
|
||||
|
||||
InputEvent(int fd) { m_fileDesc = fd; }
|
||||
|
||||
const char* type_name() const override { return "input"; }
|
||||
|
||||
void insert(torrent::net::Poll* p);
|
||||
void remove(torrent::net::Poll* p);
|
||||
|
||||
void event_read();
|
||||
void event_write();
|
||||
void event_error();
|
||||
void event_read() override;
|
||||
void event_write() override;
|
||||
void event_error() override;
|
||||
|
||||
void slot_pressed(slot_int s) { m_slotPressed = s; }
|
||||
|
||||
|
||||
+49
-38
@@ -1,42 +1,7 @@
|
||||
// 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 <algorithm>
|
||||
#include <functional>
|
||||
#include <rak/algorithm.h>
|
||||
#include <rak/path.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -45,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
|
||||
@@ -73,7 +84,7 @@ PathInput::receive_do_complete() {
|
||||
size_type dirEnd = find_last_delim();
|
||||
|
||||
utils::Directory dir(dirEnd != 0 ? str().substr(0, dirEnd) : "./");
|
||||
|
||||
|
||||
if (!dir.update(utils::Directory::update_sort | utils::Directory::update_hide_dot) || dir.empty()) {
|
||||
mark_dirty();
|
||||
|
||||
@@ -94,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
|
||||
|
||||
+59
-113
@@ -13,7 +13,6 @@
|
||||
#include <torrent/net/fd.h>
|
||||
#include <torrent/utils/chrono.h>
|
||||
#include <torrent/utils/log.h>
|
||||
#include <rak/error_number.h>
|
||||
|
||||
#ifdef HAVE_BACKTRACE
|
||||
#include <execinfo.h>
|
||||
@@ -21,28 +20,27 @@
|
||||
|
||||
#include "core/dht_manager.h"
|
||||
#include "core/download.h"
|
||||
#include "core/download_factory.h"
|
||||
#include "core/download_store.h"
|
||||
#include "core/manager.h"
|
||||
#include "display/canvas.h"
|
||||
#include "display/window.h"
|
||||
#include "display/manager.h"
|
||||
#include "input/bindings.h"
|
||||
#include "ui/root.h"
|
||||
|
||||
#include "rpc/command_scheduler.h"
|
||||
#include "rpc/command_scheduler_item.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
#include "scgi/thread_scgi.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"
|
||||
|
||||
#include "thread_worker.h"
|
||||
|
||||
#define LT_LOG(log_fmt, ...) \
|
||||
lt_log_print(torrent::LOG_SYSTEM, "system: " log_fmt, __VA_ARGS__);
|
||||
|
||||
@@ -51,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) {
|
||||
@@ -111,43 +78,6 @@ initialize_rpc_slots() {
|
||||
};
|
||||
}
|
||||
|
||||
void
|
||||
load_session_torrents() {
|
||||
utils::Directory entries = control->core()->download_store()->get_formated_entries();
|
||||
|
||||
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.
|
||||
@@ -177,19 +107,36 @@ main(int argc, char** argv) {
|
||||
srandom(random_seed);
|
||||
srand48(random_seed);
|
||||
|
||||
torrent::initialize_main_thread();
|
||||
torrent::log_initialize();
|
||||
|
||||
// TODO: Create a fake thread object for initializing other processes and enabling logging.
|
||||
torrent::initialize_main_thread();
|
||||
|
||||
SignalHandler::set_ignore(SIGPIPE);
|
||||
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));
|
||||
|
||||
// Limited list of commands with the following format:
|
||||
//
|
||||
// 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_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);
|
||||
|
||||
@@ -200,8 +147,8 @@ main(int argc, char** argv) {
|
||||
// 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));
|
||||
if (torrent::system::Thread::should_handle_sigusr1())
|
||||
SignalHandler::set_handler(SIGUSR1, []() {});
|
||||
|
||||
torrent::log_add_group_output(torrent::LOG_NOTICE, "important");
|
||||
torrent::log_add_group_output(torrent::LOG_DHT_ERROR, "important");
|
||||
@@ -213,11 +160,10 @@ 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);
|
||||
|
||||
// TODO: Move to controller.
|
||||
worker_thread = new ThreadWorker();
|
||||
worker_thread->init_thread();
|
||||
scgi::ThreadScgi::create_thread();
|
||||
session::ThreadSession::create_thread();
|
||||
|
||||
// Initialize option handlers after libtorrent to ensure
|
||||
// torrent::ConnectionManager* are valid etc.
|
||||
@@ -263,7 +209,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"
|
||||
@@ -421,6 +374,7 @@ main(int argc, char** argv) {
|
||||
// Deprecate:
|
||||
|
||||
CMD2_VAR_STRING("dht.throttle.name", "deprecated");
|
||||
rpc::rpc.mark_safe("dht.throttle.name");
|
||||
|
||||
CMD2_REDIRECT("network.http.max_open", "network.http.max_total_connections");
|
||||
CMD2_REDIRECT("network.http.max_open.set", "network.http.max_total_connections.set");
|
||||
@@ -460,23 +414,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);
|
||||
|
||||
@@ -486,11 +433,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
|
||||
@@ -501,7 +445,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();
|
||||
@@ -530,13 +474,13 @@ main(int argc, char** argv) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
torrent::log_cleanup();
|
||||
|
||||
delete control;
|
||||
control = nullptr;
|
||||
|
||||
delete worker_thread;
|
||||
worker_thread = nullptr;
|
||||
scgi::ThreadScgi::destroy_thread();
|
||||
session::ThreadSession::destroy_thread();
|
||||
|
||||
torrent::log_cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -565,7 +509,7 @@ handle_sigbus(int signum, siginfo_t* sa, [[maybe_unused]] void* ptr) {
|
||||
#else
|
||||
output << "Stack dump not enabled." << std::endl;
|
||||
#endif
|
||||
output << std::endl << "Error: " << rak::error_number(sa->si_errno).c_str() << std::endl;
|
||||
output << std::endl << "Error: " << std::strerror(sa->si_errno) << std::endl;
|
||||
|
||||
const char* signal_reason;
|
||||
|
||||
@@ -617,7 +561,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;
|
||||
|
||||
|
||||
+6
-37
@@ -1,39 +1,7 @@
|
||||
// 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 "option_parser.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
@@ -42,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;
|
||||
@@ -137,8 +103,11 @@ void
|
||||
OptionParser::call_int_pair(slot_int_pair slot, const std::string& arg) {
|
||||
int a, b;
|
||||
|
||||
if (std::sscanf(arg.c_str(), "%u-%u", &a, &b) != 2)
|
||||
if (std::sscanf(arg.c_str(), "%d-%d", &a, &b) != 2)
|
||||
throw std::runtime_error("Invalid argument, \"" + arg + "\" should be \"a-b\"");
|
||||
|
||||
if (a < 0 || b < 0)
|
||||
throw std::runtime_error("Invalid argument, \"" + arg + "\" should be positive numbers");
|
||||
|
||||
slot(a, b);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -98,11 +98,17 @@ CommandMap::call_command(const key_type& key, const mapped_type& arg, const targ
|
||||
if (itr == base_type::end())
|
||||
throw torrent::input_error("Command \"" + std::string(key) + "\" does not exist.");
|
||||
|
||||
if (!rpc.is_trusted() && !(itr->second.m_flags & flag_untrusted_safe))
|
||||
throw untrusted_error("Command \"" + std::string(key) + "\" is not allowed for untrusted connections.");
|
||||
|
||||
return itr->second.m_anySlot(&itr->second.m_variable, target, arg);
|
||||
}
|
||||
|
||||
const CommandMap::mapped_type
|
||||
CommandMap::call_command(iterator itr, const mapped_type& arg, const target_type& target) {
|
||||
if (!rpc.is_trusted() && !(itr->second.m_flags & flag_untrusted_safe))
|
||||
throw untrusted_error("Command \"" + itr->first + "\" is not allowed for untrusted connections.");
|
||||
|
||||
return itr->second.m_anySlot(&itr->second.m_variable, target, arg);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,8 @@ public:
|
||||
static const int flag_file_target = 0x100;
|
||||
static const int flag_tracker_target = 0x200;
|
||||
|
||||
static const int flag_untrusted_safe = 0x400;
|
||||
|
||||
CommandMap() = default;
|
||||
|
||||
bool has(const std::string& key) const { return base_type::find(key) != base_type::end(); }
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -5,10 +5,9 @@
|
||||
#include <fcntl.h>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <rak/path.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"
|
||||
@@ -42,8 +41,13 @@ ExecFile::execute(const char* file, char* const* argv, int flags) {
|
||||
|
||||
pid_t childPid = fork();
|
||||
|
||||
if (childPid == -1)
|
||||
if (childPid == -1) {
|
||||
if (flags & flag_capture) {
|
||||
::close(pipeFd[0]);
|
||||
::close(pipeFd[1]);
|
||||
}
|
||||
throw torrent::input_error("ExecFile::execute(...) Fork failed.");
|
||||
}
|
||||
|
||||
if (childPid == 0) {
|
||||
if (flags & flag_background) {
|
||||
|
||||
+6
-3
@@ -133,9 +133,12 @@ jsonrpc_call_command(const std::string& method, const json& params) {
|
||||
|
||||
params_object_list.erase(params_object_list.begin());
|
||||
|
||||
const auto& result = rpc::commands.call_command(itr, params_object, target);
|
||||
|
||||
return object_to_json(result);
|
||||
try {
|
||||
const auto& result = rpc::commands.call_command(itr, params_object, target);
|
||||
return object_to_json(result);
|
||||
} catch (untrusted_error& e) {
|
||||
throw rpc_error(JSONRPC_METHOD_NOT_FOUND_ERROR, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
json
|
||||
|
||||
+21
-14
@@ -1,19 +1,19 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "rpc/lua.h"
|
||||
|
||||
#ifdef HAVE_LUA
|
||||
#include <lua.hpp>
|
||||
#endif
|
||||
|
||||
#include <rak/error_number.h>
|
||||
#include <rak/path.h>
|
||||
#include <rak/string_manip.h>
|
||||
#include <torrent/object.h>
|
||||
#include <torrent/utils/string_manip.h>
|
||||
|
||||
#include "core/download.h"
|
||||
#include "rpc/command.h"
|
||||
@@ -170,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;
|
||||
|
||||
@@ -218,12 +217,14 @@ object_to_target(const torrent::Object& obj, int call_flags, rpc::target_type* t
|
||||
|
||||
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;
|
||||
@@ -266,8 +267,10 @@ object_to_lua(lua_State* l_state, torrent::Object const& object) {
|
||||
lua_createtable(l_state, 0, static_cast<int>(object_map.size()));
|
||||
int table_idx = lua_gettop(l_state);
|
||||
for (const auto& itr : object_map) {
|
||||
object_to_lua(l_state, itr.second);
|
||||
// lua_rawset requires the value to be on top of the stack, with the
|
||||
// key below it.
|
||||
lua_pushlstring(l_state, itr.first.c_str(), itr.first.size());
|
||||
object_to_lua(l_state, itr.second);
|
||||
lua_rawset(l_state, table_idx);
|
||||
}
|
||||
break;
|
||||
@@ -380,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;
|
||||
}
|
||||
|
||||
@@ -417,12 +420,16 @@ execute_lua(LuaEngine* engine, rpc::target_type target_type, torrent::Object con
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
torrent::Object
|
||||
execute_lua(LuaEngine* engine, torrent::Object const& rawArgs, int flags) {
|
||||
execute_lua([[maybe_unused]] LuaEngine* engine, [[maybe_unused]] torrent::Object const& rawArgs, [[maybe_unused]] int flags) {
|
||||
throw torrent::input_error("Lua support not enabled");
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
LuaEngine::LuaEngine() {}
|
||||
LuaEngine::~LuaEngine() {}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace rpc
|
||||
|
||||
+234
-135
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
+33
-58
@@ -1,45 +1,11 @@
|
||||
// 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 <cstring>
|
||||
#include <cstdio>
|
||||
#include <locale>
|
||||
#include <rak/path.h>
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
#include "globals.h"
|
||||
#include "parse.h"
|
||||
|
||||
namespace rpc {
|
||||
@@ -90,7 +56,7 @@ parse_string(const char* first, const char* last, std::string* dest, bool (*deli
|
||||
|
||||
dest->push_back(*first++);
|
||||
}
|
||||
|
||||
|
||||
return first;
|
||||
}
|
||||
}
|
||||
@@ -100,7 +66,7 @@ parse_whole_string(const char* first, const char* last, std::string* dest) {
|
||||
first = parse_skip_wspace(first, last);
|
||||
first = parse_string(first, last, dest);
|
||||
first = parse_skip_wspace(first, last);
|
||||
|
||||
|
||||
if (first != last)
|
||||
throw torrent::input_error("Junk at end of input.");
|
||||
}
|
||||
@@ -154,11 +120,17 @@ parse_value_nothrow(const char* src, int64_t* value, int base, int unit) {
|
||||
case 'b':
|
||||
case 'B': ++last; break;
|
||||
case 'k':
|
||||
case 'K': *value = *value << 10; ++last; break;
|
||||
case 'K':
|
||||
if (*value > (int64_t)0x1FFFFFFFFFFFFF) return src; // overflow guard
|
||||
*value = *value << 10; ++last; break;
|
||||
case 'm':
|
||||
case 'M': *value = *value << 20; ++last; break;
|
||||
case 'M':
|
||||
if (*value > (int64_t)0x7FFFFFFFFFF) return src; // overflow guard
|
||||
*value = *value << 20; ++last; break;
|
||||
case 'g':
|
||||
case 'G': *value = *value << 30; ++last; break;
|
||||
case 'G':
|
||||
if (*value > (int64_t)0x1FFFFFFFF) return src; // overflow guard
|
||||
*value = *value << 30; ++last; break;
|
||||
// case ' ':
|
||||
// case '\0': *value = *value * unit; break;
|
||||
// default: throw torrent::input_error("Could not parse value.");
|
||||
@@ -175,7 +147,7 @@ parse_object(const char* first, const char* last, torrent::Object* dest, bool (*
|
||||
*dest = torrent::Object::create_list();
|
||||
first = parse_list(first + 1, last, dest, &parse_is_delim_block);
|
||||
first = parse_skip_wspace(first, last);
|
||||
|
||||
|
||||
if (first == last || *first != '}')
|
||||
throw torrent::input_error("Could not find closing '}'.");
|
||||
|
||||
@@ -238,7 +210,7 @@ parse_list(const char* first, const char* last, torrent::Object* dest, bool (*de
|
||||
first = parse_skip_wspace(first, last);
|
||||
|
||||
dest->as_list().push_back(tmp);
|
||||
|
||||
|
||||
if (first == last || !parse_is_seperator(*first))
|
||||
break;
|
||||
|
||||
@@ -284,12 +256,12 @@ convert_to_string(const torrent::Object& rawSrc) {
|
||||
|
||||
if (src.as_raw_bencode().is_raw_string())
|
||||
return src.as_raw_bencode().as_raw_string().as_string();
|
||||
|
||||
|
||||
if (src.as_raw_bencode().is_value())
|
||||
return src.as_raw_bencode().as_value_string();
|
||||
|
||||
default: throw torrent::input_error("Not a string.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
@@ -341,9 +313,9 @@ convert_list_to_command(torrent::Object::list_const_iterator first,
|
||||
if (first == last)
|
||||
throw torrent::input_error("Too few arguments.");
|
||||
|
||||
std::string dest = (first++)->as_string();
|
||||
std::string::size_type quoteItr = dest.find('=');
|
||||
|
||||
auto dest = (first++)->as_string();
|
||||
auto quoteItr = dest.find('=');
|
||||
|
||||
if (quoteItr == std::string::npos)
|
||||
throw torrent::input_error("Could not find '=' in command.");
|
||||
|
||||
@@ -419,7 +391,7 @@ convert_to_value_nothrow(const torrent::Object& src, int64_t* value, int base, i
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -430,20 +402,23 @@ print_object(char* first, char* last, const torrent::Object* src, int flags) {
|
||||
{
|
||||
const std::string& str = src->as_string();
|
||||
|
||||
if (first == last)
|
||||
return first;
|
||||
|
||||
if ((flags & print_expand_tilde) && *str.c_str() == '~') {
|
||||
return rak::path_expand(str.c_str(), first, last);
|
||||
auto expanded = expand_path(str);
|
||||
|
||||
} else {
|
||||
if (first == last)
|
||||
return first;
|
||||
|
||||
size_t n = std::min<size_t>(str.size(), std::distance(first, last) - 1);
|
||||
|
||||
std::memcpy(first, str.c_str(), n);
|
||||
size_t n = std::min<size_t>(expanded.size(), std::distance(first, last) - 1);
|
||||
std::memcpy(first, expanded.c_str(), n);
|
||||
*(first += n) = '\0';
|
||||
|
||||
return first;
|
||||
} else {
|
||||
size_t n = std::min<size_t>(str.size(), std::distance(first, last) - 1);
|
||||
std::memcpy(first, str.c_str(), n);
|
||||
*(first += n) = '\0';
|
||||
}
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
case torrent::Object::TYPE_VALUE:
|
||||
@@ -480,7 +455,7 @@ print_object_std(std::string* dest, const torrent::Object* src, int flags) {
|
||||
const std::string& str = src->as_string();
|
||||
|
||||
if ((flags & print_expand_tilde) && *str.c_str() == '~')
|
||||
*dest += rak::path_expand(str);
|
||||
*dest += expand_path(str);
|
||||
else
|
||||
*dest += str;
|
||||
|
||||
|
||||
+11
-12
@@ -4,9 +4,9 @@
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <rak/path.h>
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
#include "globals.h"
|
||||
#include "rpc/parse.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
#include "rpc/rpc_manager.h"
|
||||
@@ -140,7 +140,7 @@ parse_command_multiple(target_type target, const char* first, const char* last)
|
||||
|
||||
bool
|
||||
parse_command_file(const std::string& path) {
|
||||
std::fstream file(rak::path_expand(path).c_str(), std::ios::in);
|
||||
std::fstream file(expand_path(path).c_str(), std::ios::in);
|
||||
|
||||
if (!file.is_open())
|
||||
return false;
|
||||
@@ -151,22 +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
|
||||
// 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.");
|
||||
@@ -257,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+47
-8
@@ -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"
|
||||
@@ -13,6 +14,19 @@ CommandMap commands;
|
||||
RpcManager rpc;
|
||||
ExecFile execFile;
|
||||
|
||||
// Trusted/untrusted XMLRPC connection model.
|
||||
//
|
||||
// The trust state is set per-request by the SCGI layer based on the
|
||||
// UNTRUSTED_CONNECTION header. Commands without flag_untrusted_safe
|
||||
// are blocked for untrusted connections. The check is in
|
||||
// CommandMap::call_command(), which catches all command execution
|
||||
// including nested calls through argument expansion.
|
||||
|
||||
bool
|
||||
RpcManager::is_trusted() const {
|
||||
return m_trusted;
|
||||
}
|
||||
|
||||
void
|
||||
RpcManager::object_to_target(const torrent::Object& obj, int call_flags, rpc::target_type* target, std::function<void()>* deleter) {
|
||||
if (!obj.is_string())
|
||||
@@ -34,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 {
|
||||
@@ -74,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;
|
||||
}
|
||||
@@ -124,6 +138,21 @@ RpcManager::process(RPCType type, const char* in_buffer, uint32_t length, slot_r
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
RpcManager::process_untrusted(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback) {
|
||||
bool previous = m_trusted;
|
||||
m_trusted = false;
|
||||
|
||||
try {
|
||||
bool result = process(type, in_buffer, length, callback);
|
||||
m_trusted = previous;
|
||||
return result;
|
||||
} catch (...) {
|
||||
m_trusted = previous;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RpcManager::initialize_handlers() {
|
||||
if (m_handlers_initialized)
|
||||
@@ -175,4 +204,14 @@ RpcManager::insert_command(const char* name, const char* parm, const char* doc)
|
||||
m_jsonrpc.insert_command(name, parm, doc);
|
||||
}
|
||||
|
||||
void
|
||||
RpcManager::mark_safe(const std::string& key) {
|
||||
auto itr = commands.find(key);
|
||||
|
||||
if (itr == commands.end())
|
||||
return;
|
||||
|
||||
itr->second.m_flags |= CommandMap::flag_untrusted_safe;
|
||||
}
|
||||
|
||||
} // namespace rpc
|
||||
|
||||
+43
-16
@@ -1,9 +1,11 @@
|
||||
#ifndef RTORRENT_RPC_MANAGER_H
|
||||
#define RTORRENT_RPC_MANAGER_H
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <torrent/common.h>
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
#include "rpc/command.h"
|
||||
#include "rpc/command_map.h"
|
||||
@@ -34,6 +36,12 @@ private:
|
||||
std::string m_msg;
|
||||
};
|
||||
|
||||
class untrusted_error : public torrent::input_error {
|
||||
public:
|
||||
using torrent::input_error::input_error;
|
||||
virtual ~untrusted_error() throw() = default;
|
||||
};
|
||||
|
||||
class RpcManager {
|
||||
public:
|
||||
using slot_download = std::function<core::Download*(const char*)>;
|
||||
@@ -48,32 +56,48 @@ 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(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 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; }
|
||||
|
||||
static void object_to_target(const torrent::Object& obj, int callFlags, rpc::target_type* target, std::function<void()>* deleter);
|
||||
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;
|
||||
|
||||
static void object_to_target(const torrent::Object& obj, int callFlags, rpc::target_type* target, std::function<void()>* deleter);
|
||||
|
||||
private:
|
||||
bool m_trusted{true};
|
||||
|
||||
XmlRpc m_xmlrpc;
|
||||
JsonRpc m_jsonrpc;
|
||||
|
||||
@@ -81,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;
|
||||
|
||||
+115
-54
@@ -1,49 +1,54 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#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/runtime/socket_manager.h>
|
||||
|
||||
#include "control.h"
|
||||
#include "globals.h"
|
||||
#include "rpc/scgi_task.h"
|
||||
#include "utils/socket_fd.h"
|
||||
|
||||
// TODO: Figure out why moving this to the top causes a build error.
|
||||
#include "rpc/scgi.h"
|
||||
|
||||
namespace rpc {
|
||||
|
||||
SCgi::SCgi() {
|
||||
std::generate(m_tasks.begin(), m_tasks.end(), []() { return std::make_unique<SCgiTask>(); });
|
||||
|
||||
m_current = m_tasks.begin();
|
||||
}
|
||||
|
||||
SCgi::~SCgi() {
|
||||
if (!get_fd().is_valid())
|
||||
return;
|
||||
|
||||
for (SCgiTask* itr = m_task, *last = m_task + max_tasks; itr != last; ++itr)
|
||||
if (itr->is_open())
|
||||
itr->close();
|
||||
|
||||
deactivate();
|
||||
torrent::connection_manager()->dec_socket_count();
|
||||
|
||||
get_fd().close();
|
||||
get_fd().clear();
|
||||
|
||||
if (!m_path.empty())
|
||||
::unlink(m_path.c_str());
|
||||
assert(!is_open() && "SCgi::~SCgi() called while open");
|
||||
}
|
||||
|
||||
void
|
||||
SCgi::open_port(void* sa, unsigned int length, bool dontRoute) {
|
||||
if (!get_fd().open_stream() ||
|
||||
(dontRoute && !get_fd().set_dont_route(true)))
|
||||
SCgi::open_port(sockaddr* sa, unsigned int length, bool dont_route) {
|
||||
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)));
|
||||
|
||||
open(sa, length);
|
||||
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);
|
||||
|
||||
open(reinterpret_cast<sockaddr*>(sa), length);
|
||||
|
||||
torrent::runtime::socket_manager()->register_event_or_throw(this, []() {});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -51,81 +56,137 @@ SCgi::open_named(const std::string& filename) {
|
||||
if (filename.empty() || filename.size() > 4096)
|
||||
throw torrent::resource_error("Invalid filename length.");
|
||||
|
||||
auto buffer = std::make_unique<char[]>(sizeof(sockaddr_un) + filename.size());
|
||||
sockaddr_un* sa = reinterpret_cast<sockaddr_un*>(buffer.get());
|
||||
auto buffer = std::make_unique<char[]>(sizeof(sockaddr_un) + filename.size() + 1);
|
||||
|
||||
#ifdef __sun__
|
||||
sa->sun_family = AF_UNIX;
|
||||
#else
|
||||
sockaddr_un* sa = reinterpret_cast<sockaddr_un*>(buffer.get());
|
||||
sa->sun_family = AF_LOCAL;
|
||||
#endif
|
||||
|
||||
std::memcpy(sa->sun_path, filename.c_str(), filename.size() + 1);
|
||||
|
||||
if (!get_fd().open_local())
|
||||
throw torrent::resource_error("Could not open socket for listening.");
|
||||
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)));
|
||||
|
||||
set_file_descriptor(fd);
|
||||
|
||||
open(reinterpret_cast<sockaddr*>(sa), offsetof(struct sockaddr_un, sun_path) + filename.size() + 1);
|
||||
|
||||
torrent::runtime::socket_manager()->register_event_or_throw(this, []() {});
|
||||
|
||||
open(sa, offsetof(struct sockaddr_un, sun_path) + filename.size() + 1);
|
||||
m_path = filename;
|
||||
}
|
||||
|
||||
void
|
||||
SCgi::open(void* sa, unsigned int length) {
|
||||
SCgi::open_fd(int fd) {
|
||||
if (!torrent::fd_set_nonblock(fd))
|
||||
throw torrent::resource_error("Could not set non-blocking on systemd fd: " +
|
||||
std::string(std::strerror(errno)));
|
||||
set_file_descriptor(fd);
|
||||
|
||||
// fd is already bound and listening; no bind()/listen() needed.
|
||||
|
||||
torrent::runtime::socket_manager()->register_event_or_throw(this, []() {});
|
||||
}
|
||||
|
||||
void
|
||||
SCgi::open(sockaddr* sa, unsigned int length) {
|
||||
try {
|
||||
if (!get_fd().set_nonblock() ||
|
||||
!get_fd().set_reuse_address(true) ||
|
||||
!get_fd().bind_sa(reinterpret_cast<sockaddr*>(sa), length) ||
|
||||
!get_fd().listen(max_tasks))
|
||||
if (::bind(file_descriptor(), sa, length) == -1)
|
||||
throw torrent::resource_error("Could not bind socket for listening: " + std::string(std::strerror(errno)));
|
||||
|
||||
if (!torrent::fd_listen(file_descriptor(), max_tasks))
|
||||
throw torrent::resource_error("Could not prepare socket for listening: " + std::string(std::strerror(errno)));
|
||||
|
||||
torrent::connection_manager()->inc_socket_count();
|
||||
|
||||
} catch (torrent::resource_error& e) {
|
||||
get_fd().close();
|
||||
get_fd().clear();
|
||||
|
||||
throw e;
|
||||
torrent::fd_close(file_descriptor());
|
||||
set_file_descriptor(-1);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Verify this is run in correct thread, also only ever call poll methods from thread_self.
|
||||
|
||||
void
|
||||
SCgi::activate() {
|
||||
assert(std::this_thread::get_id() == worker_thread->thread_id() && "SCgi::activate() must be called from the worker thread.");
|
||||
assert(torrent::this_thread::thread() == scgi_thread::thread());
|
||||
|
||||
torrent::this_thread::poll()->open(this);
|
||||
torrent::this_thread::poll()->insert_read(this);
|
||||
torrent::this_thread::poll()->insert_error(this);
|
||||
}
|
||||
|
||||
// TODO: This should close the fd to avoid reuse.
|
||||
void
|
||||
SCgi::deactivate() {
|
||||
assert(std::this_thread::get_id() == worker_thread->thread_id() && "SCgi::deactivate() must be called from the worker thread.");
|
||||
SCgi::stop() {
|
||||
assert(torrent::this_thread::thread() == scgi_thread::thread());
|
||||
|
||||
torrent::this_thread::poll()->remove_and_close(this);
|
||||
if (!is_open())
|
||||
return;
|
||||
|
||||
for (auto& itr : m_tasks) {
|
||||
if (itr->is_open())
|
||||
itr->close();
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
if (!m_path.empty())
|
||||
::unlink(m_path.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
SCgi::event_read() {
|
||||
if (m_current < m_tasks.begin() || m_current >= m_tasks.end())
|
||||
throw torrent::internal_error("SCgi::event_read() m_current is out of bounds");
|
||||
|
||||
while (true) {
|
||||
int fd = torrent::fd_accept(get_fd().get_fd());
|
||||
// TODO: Optimize this by keeping track of count.
|
||||
auto prev = m_current;
|
||||
|
||||
m_current = std::find_if(m_current + 1, m_tasks.end(), [](const auto& task) { return !task->is_open(); });
|
||||
|
||||
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)
|
||||
break;
|
||||
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)));
|
||||
}
|
||||
|
||||
SCgiTask* task = std::find_if(m_task, m_task + max_tasks, std::mem_fn(&SCgiTask::is_available));
|
||||
|
||||
if (task == m_task + max_tasks) {
|
||||
if (m_current == prev) {
|
||||
torrent::fd_close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
task->open(this, fd);
|
||||
auto open_func = [this, fd, task = m_current->get()]() {
|
||||
task->open(this, fd);
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
if (!result)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-15
@@ -1,15 +1,11 @@
|
||||
#ifndef RTORRENT_RPC_SCGI_H
|
||||
#define RTORRENT_RPC_SCGI_H
|
||||
|
||||
#include <functional>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <torrent/event.h>
|
||||
|
||||
#include "rpc/scgi_task.h"
|
||||
#include "utils/socket_fd.h"
|
||||
|
||||
namespace utils {
|
||||
class SocketFd;
|
||||
}
|
||||
|
||||
namespace rpc {
|
||||
|
||||
@@ -17,33 +13,38 @@ class SCgi : public torrent::Event {
|
||||
public:
|
||||
static const int max_tasks = 100;
|
||||
|
||||
SCgi();
|
||||
~SCgi() override;
|
||||
|
||||
const char* type_name() const override { return "scgi"; }
|
||||
|
||||
void open_port(void* sa, unsigned int length, bool dontRoute);
|
||||
void open_port(sockaddr* sa, unsigned int length, bool dont_route);
|
||||
void open_named(const std::string& filename);
|
||||
void open_fd(int fd);
|
||||
|
||||
void activate();
|
||||
void deactivate();
|
||||
|
||||
const std::string& path() const { return m_path; }
|
||||
void stop();
|
||||
|
||||
int log_fd() const { return m_logFd; }
|
||||
void set_log_fd(int fd) { m_logFd = fd; }
|
||||
const std::string& path() const { return m_path; }
|
||||
|
||||
int log_fd() const { return m_logFd; }
|
||||
void set_log_fd(int fd) { m_logFd = fd; }
|
||||
|
||||
void event_read() override;
|
||||
void event_write() override;
|
||||
void event_error() override;
|
||||
|
||||
utils::SocketFd& get_fd() { return *reinterpret_cast<utils::SocketFd*>(&m_fileDesc); }
|
||||
|
||||
private:
|
||||
void open(void* sa, unsigned int length);
|
||||
using task_list = std::array<std::unique_ptr<SCgiTask>, max_tasks>;
|
||||
|
||||
void open(sockaddr* sa, unsigned int length);
|
||||
|
||||
std::string m_path;
|
||||
int m_logFd{-1};
|
||||
SCgiTask m_task[max_tasks];
|
||||
|
||||
task_list m_tasks;
|
||||
task_list::iterator m_current;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+308
-189
@@ -2,64 +2,101 @@
|
||||
|
||||
#include "rpc/scgi_task.h"
|
||||
|
||||
#include <rak/error_number.h>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <charconv>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include <unistd.h>
|
||||
#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/utils/log.h>
|
||||
#include <torrent/utils/thread.h>
|
||||
#include <torrent/system/thread.h>
|
||||
|
||||
#include "control.h"
|
||||
#include "globals.h"
|
||||
#include "scgi.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
#include "utils/socket_fd.h"
|
||||
#include "rpc/scgi.h"
|
||||
#include "utils/gzip.h"
|
||||
|
||||
namespace rpc {
|
||||
|
||||
void
|
||||
SCgiTask::open(SCgi* parent, int fd) {
|
||||
set_file_descriptor(fd);
|
||||
|
||||
m_parent = parent;
|
||||
m_fileDesc = fd;
|
||||
m_buffer = new char[default_buffer_size + 1];
|
||||
m_buffer_size = default_buffer_size;
|
||||
m_position = m_buffer;
|
||||
m_body = NULL;
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
SCgiTask::close() {
|
||||
if (!get_fd().is_valid())
|
||||
return;
|
||||
|
||||
torrent::main_thread::thread()->cancel_callback_and_wait(this);
|
||||
torrent::utils::Thread::self()->cancel_callback(this);
|
||||
|
||||
torrent::this_thread::poll()->remove_and_close(this);
|
||||
|
||||
get_fd().close();
|
||||
get_fd().clear();
|
||||
|
||||
auto lock = std::lock_guard<std::mutex>(m_result_mutex);
|
||||
|
||||
delete[] m_buffer;
|
||||
m_buffer = NULL;
|
||||
// Leave room for terminating nul byte for parsing the header.
|
||||
m_buffer.resize(default_buffer_size + 1);
|
||||
}
|
||||
|
||||
void
|
||||
SCgiTask::cancel_open() {
|
||||
if (!is_open())
|
||||
return;
|
||||
|
||||
torrent::this_thread::poll()->remove_and_close(this);
|
||||
|
||||
torrent::fd_close(file_descriptor());
|
||||
set_file_descriptor(-1);
|
||||
};
|
||||
|
||||
void
|
||||
SCgiTask::close() {
|
||||
if (!is_open())
|
||||
return;
|
||||
|
||||
torrent::main_thread::thread()->cancel_callback_and_wait(this);
|
||||
torrent::system::Thread::self()->cancel_callback(this);
|
||||
|
||||
torrent::runtime::socket_manager()->close_event_or_throw(this, [this]() {
|
||||
torrent::this_thread::poll()->remove_and_close(this);
|
||||
|
||||
torrent::fd_close(file_descriptor());
|
||||
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.clear();
|
||||
}
|
||||
|
||||
void
|
||||
SCgiTask::event_read() {
|
||||
int bytes = ::recv(m_fileDesc, m_position, m_buffer_size - (m_position - m_buffer), 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 || !rak::error_number::current().is_blocked_momentary())
|
||||
if (bytes == 0 || !(errno == EAGAIN || errno == EINTR))
|
||||
close();
|
||||
|
||||
return;
|
||||
@@ -67,99 +104,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, ¤t, 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 || *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++;
|
||||
|
||||
// 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));
|
||||
// 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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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, 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, m_body, std::distance(m_body, m_position));
|
||||
m_position = m_buffer + std::distance(m_body, m_position);
|
||||
m_body = m_buffer;
|
||||
|
||||
} else {
|
||||
realloc_buffer((m_buffer_size = content_length) + 1, m_body, std::distance(m_body, m_position));
|
||||
|
||||
m_position = m_buffer + std::distance(m_body, m_position);
|
||||
m_body = m_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
if ((unsigned int)std::distance(m_buffer, m_position) != m_buffer_size)
|
||||
if (m_position < m_body + m_content_length)
|
||||
return;
|
||||
|
||||
torrent::this_thread::poll()->remove_read(this);
|
||||
@@ -169,13 +155,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, m_buffer_size);
|
||||
result = write(m_parent->log_fd(), "\n---\n", sizeof("\n---\n"));
|
||||
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, 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, 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:
|
||||
@@ -185,25 +176,18 @@ event_read_failed:
|
||||
|
||||
void
|
||||
SCgiTask::event_write() {
|
||||
// Apple and Solaris do not support MSG_NOSIGNAL,
|
||||
// so disable this fix until we find a better solution
|
||||
#if defined(__APPLE__) || defined(__sun__)
|
||||
int bytes = ::send(m_fileDesc, m_position, m_buffer_size, 0);
|
||||
#else
|
||||
int bytes = ::send(m_fileDesc, m_position, m_buffer_size, MSG_NOSIGNAL);
|
||||
#endif
|
||||
int bytes = ::send(m_fileDesc, m_buffer.data() + m_position, m_buffer.size() - m_position, 0);
|
||||
|
||||
if (bytes == -1) {
|
||||
if (!rak::error_number::current().is_blocked_momentary())
|
||||
if (!(errno == EAGAIN || errno == EINTR || errno == EPIPE))
|
||||
close();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_position += bytes;
|
||||
m_buffer_size -= bytes;
|
||||
|
||||
if (bytes == 0 || m_buffer_size == 0)
|
||||
if (bytes == 0 || m_position == m_buffer.size())
|
||||
return close();
|
||||
}
|
||||
|
||||
@@ -212,6 +196,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(" ;");
|
||||
@@ -225,18 +299,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.
|
||||
@@ -246,92 +319,138 @@ 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) {
|
||||
char* tmp = new char[size];
|
||||
|
||||
std::memcpy(tmp, buffer, bufferSize);
|
||||
::free(m_buffer);
|
||||
m_buffer = 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.
|
||||
assert(torrent::system::Thread::self() == scgi_thread::thread());
|
||||
|
||||
auto scgi_thread = torrent::utils::Thread::self();
|
||||
|
||||
auto result_callback = [this, scgi_thread](const char* b, uint32_t l) {
|
||||
receive_write(b, l);
|
||||
|
||||
scgi_thread->callback_interrupt_pollling(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);
|
||||
RpcManager::RPCType rpc_type;
|
||||
|
||||
switch (content_type()) {
|
||||
case rpc::SCgiTask::ContentType::JSON:
|
||||
torrent::main_thread::thread()->callback_interrupt_pollling(this, [buffer, length, result_callback]() {
|
||||
rpc.process(RpcManager::RPCType::JSON, buffer, length,
|
||||
[result_callback](const char* b, uint32_t l) {
|
||||
result_callback(b, l);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
rpc_type = RpcManager::RPCType::JSON;
|
||||
break;
|
||||
|
||||
case rpc::SCgiTask::ContentType::XML:
|
||||
torrent::main_thread::thread()->callback_interrupt_pollling(this, [buffer, length, result_callback]() {
|
||||
rpc.process(RpcManager::RPCType::XML, buffer, length,
|
||||
[result_callback](const char* b, uint32_t l) {
|
||||
result_callback(b, l);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
rpc_type = RpcManager::RPCType::XML;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw torrent::internal_error("SCgiTask::receive_call(...) received bad input.");
|
||||
}
|
||||
|
||||
// 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?
|
||||
|
||||
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::thread()->callback_interrupt_polling(this, [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::thread()->callback_interrupt_polling(this, [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, result_callback);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
SCgiTask::receive_write(const char* buffer, uint32_t length) {
|
||||
if (buffer == NULL || length > (100 << 20))
|
||||
assert(torrent::system::Thread::self() == 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, m_buffer_size, header, length);
|
||||
|
||||
m_position = m_buffer;
|
||||
m_buffer_size = length + headerSize;
|
||||
|
||||
std::memcpy(m_buffer + 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, 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, 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
|
||||
|
||||
+32
-19
@@ -3,58 +3,71 @@
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <torrent/event.h>
|
||||
|
||||
namespace utils {
|
||||
class SocketFd;
|
||||
}
|
||||
|
||||
namespace rpc {
|
||||
|
||||
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; }
|
||||
|
||||
const char* type_name() const override { return "scgi-task"; }
|
||||
|
||||
bool is_open() const { return m_fileDesc != -1; }
|
||||
bool is_available() const { return m_fileDesc == -1; }
|
||||
|
||||
void open(SCgi* parent, int fd);
|
||||
void cancel_open();
|
||||
|
||||
void close();
|
||||
|
||||
ContentType content_type() const { return m_content_type; }
|
||||
|
||||
virtual void event_read();
|
||||
virtual void event_write();
|
||||
virtual void event_error();
|
||||
|
||||
utils::SocketFd& get_fd() { return *reinterpret_cast<utils::SocketFd*>(&m_fileDesc); }
|
||||
void event_read() override;
|
||||
void event_write() override;
|
||||
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{};
|
||||
|
||||
std::mutex m_result_mutex;
|
||||
|
||||
char* m_buffer{nullptr};
|
||||
char* m_position{nullptr};
|
||||
char* m_body{nullptr};
|
||||
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};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -385,6 +385,10 @@ xmlrpc_call_command(xmlrpc_env* env, xmlrpc_value* args, void* voidServerInfo) {
|
||||
|
||||
return object_to_xmlrpc(env, rpc::commands.call_command(itr, object, target));
|
||||
|
||||
} catch (untrusted_error& e) {
|
||||
xmlrpc_env_set_fault(env, XMLRPC_REQUEST_REFUSED_ERROR, e.what());
|
||||
return NULL;
|
||||
|
||||
} catch (xmlrpc_error_c& e) {
|
||||
xmlrpc_env_set_fault(env, e.type(), e.what());
|
||||
return NULL;
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <rak/string_manip.h>
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/object.h>
|
||||
|
||||
@@ -31,7 +30,7 @@ const int XMLRPC_PARSE_ERROR = -503;
|
||||
// const int XMLRPC_NETWORK_ERROR = -504;
|
||||
// const int XMLRPC_TIMEOUT_ERROR = -505;
|
||||
const int XMLRPC_NO_SUCH_METHOD_ERROR = -506;
|
||||
// const int XMLRPC_REQUEST_REFUSED_ERROR = -507;
|
||||
const int XMLRPC_REQUEST_REFUSED_ERROR = -507;
|
||||
// const int XMLRPC_INTROSPECTION_DISABLED_ERROR = -508;
|
||||
const int XMLRPC_LIMIT_EXCEEDED_ERROR = -509;
|
||||
// const int XMLRPC_INVALID_UTF8_ERROR = -510;
|
||||
@@ -238,7 +237,11 @@ execute_command(std::string method_name, const tinyxml2::XMLElement* params_elem
|
||||
throw rpc_error(XMLRPC_TYPE_ERROR, "invalid parameters: too few");
|
||||
}
|
||||
|
||||
return rpc::commands.call_command(cmd_itr, params_raw, target);
|
||||
try {
|
||||
return rpc::commands.call_command(cmd_itr, params_raw, target);
|
||||
} catch (untrusted_error& e) {
|
||||
throw rpc_error(XMLRPC_REQUEST_REFUSED_ERROR, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "scgi/thread_scgi.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/utils/log.h>
|
||||
|
||||
#include "globals.h"
|
||||
#include "rpc/scgi.h"
|
||||
|
||||
namespace scgi {
|
||||
|
||||
class ThreadScgiInternal {
|
||||
public:
|
||||
static ThreadScgi* thread_scgi() { return ThreadScgi::internal_thread_scgi(); }
|
||||
};
|
||||
|
||||
ThreadScgi* ThreadScgi::m_thread_scgi{};
|
||||
|
||||
void
|
||||
ThreadScgi::create_thread() {
|
||||
auto thread = new ThreadScgi;
|
||||
|
||||
m_thread_scgi = thread;
|
||||
m_thread_scgi->m_state = STATE_INITIALIZED;
|
||||
}
|
||||
|
||||
void
|
||||
ThreadScgi::destroy_thread() {
|
||||
delete m_thread_scgi;
|
||||
m_thread_scgi = nullptr;
|
||||
}
|
||||
|
||||
ThreadScgi*
|
||||
ThreadScgi::thread_scgi() {
|
||||
return m_thread_scgi;
|
||||
}
|
||||
|
||||
void
|
||||
ThreadScgi::cleanup_thread() {
|
||||
if (m_scgi != nullptr)
|
||||
m_scgi.load()->stop();
|
||||
}
|
||||
|
||||
rpc::SCgi*
|
||||
ThreadScgi::scgi() {
|
||||
return m_scgi;
|
||||
}
|
||||
|
||||
// TODO: Disable changing SCGI once set?
|
||||
|
||||
bool
|
||||
ThreadScgi::set_scgi(rpc::SCgi* scgi) {
|
||||
rpc::SCgi* expected = nullptr;
|
||||
|
||||
if (!m_scgi.compare_exchange_strong(expected, scgi))
|
||||
return false;
|
||||
|
||||
change_rpc_log();
|
||||
|
||||
callback(nullptr, [this]() {
|
||||
if (m_scgi == nullptr)
|
||||
throw torrent::internal_error("Tried to start SCGI but object was not present.");
|
||||
|
||||
m_scgi.load()->activate();
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ThreadScgi::set_rpc_log(const std::string& filename) {
|
||||
callback(nullptr, [this, filename]() {
|
||||
m_rpc_log_filename = filename;
|
||||
change_rpc_log();
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
ThreadScgi::change_rpc_log() {
|
||||
if (scgi() == nullptr)
|
||||
return;
|
||||
|
||||
if (scgi()->log_fd() != -1) {
|
||||
::close(scgi()->log_fd());
|
||||
scgi()->set_log_fd(-1);
|
||||
|
||||
lt_log_print(torrent::LOG_NOTICE, "Closed RPC log.", 0);
|
||||
}
|
||||
|
||||
if (m_rpc_log_filename.empty())
|
||||
return;
|
||||
|
||||
scgi()->set_log_fd(open(expand_path(m_rpc_log_filename).c_str(), O_WRONLY | O_APPEND | O_CREAT, 0644));
|
||||
|
||||
if (scgi()->log_fd() == -1) {
|
||||
lt_log_print(torrent::LOG_NOTICE, "Could not open RPC log file '%s'.", m_rpc_log_filename.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
lt_log_print(torrent::LOG_NOTICE, "Logging RPC events to '%s'.", m_rpc_log_filename.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
ThreadScgi::call_events() {
|
||||
if ((m_flags & flag_do_shutdown)) {
|
||||
if ((m_flags & flag_did_shutdown))
|
||||
throw torrent::internal_error("Already trigged shutdown.");
|
||||
|
||||
m_flags |= flag_did_shutdown;
|
||||
throw torrent::shutdown_exception();
|
||||
}
|
||||
|
||||
process_callbacks();
|
||||
}
|
||||
|
||||
std::chrono::microseconds
|
||||
ThreadScgi::next_timeout() {
|
||||
// TODO: This leads to kqueue crash?
|
||||
// return std::chrono::microseconds(1h);
|
||||
return std::chrono::microseconds(10min);
|
||||
}
|
||||
|
||||
} // namespace scgi
|
||||
|
||||
namespace scgi_thread {
|
||||
|
||||
torrent::system::Thread* thread() { return scgi::ThreadScgiInternal::thread_scgi(); }
|
||||
std::thread::id thread_id() { return scgi::ThreadScgiInternal::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); }
|
||||
|
||||
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); }
|
||||
|
||||
} // namespace scgi_thread
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef RTORRENT_SCGI_THREAD_SCGI_H
|
||||
#define RTORRENT_SCGI_THREAD_SCGI_H
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <torrent/system/thread.h>
|
||||
|
||||
namespace rpc {
|
||||
class SCgi;
|
||||
}
|
||||
|
||||
namespace scgi {
|
||||
|
||||
class ThreadScgiInternal;
|
||||
|
||||
class ThreadScgi : public torrent::system::Thread {
|
||||
public:
|
||||
|
||||
static void create_thread();
|
||||
static void destroy_thread();
|
||||
static ThreadScgi* thread_scgi();
|
||||
|
||||
const char* name() const override { return "rtorrent-scgi"; }
|
||||
|
||||
rpc::SCgi* scgi();
|
||||
bool set_scgi(rpc::SCgi* scgi);
|
||||
|
||||
void set_rpc_log(const std::string& filename);
|
||||
|
||||
protected:
|
||||
friend class ThreadScgiInternal;
|
||||
|
||||
ThreadScgi() = default;
|
||||
|
||||
static auto internal_thread_scgi() { return m_thread_scgi; }
|
||||
|
||||
void cleanup_thread() override;
|
||||
|
||||
void call_events() override;
|
||||
std::chrono::microseconds next_timeout() override;
|
||||
|
||||
private:
|
||||
void task_touch_log();
|
||||
void change_rpc_log();
|
||||
|
||||
static ThreadScgi* m_thread_scgi;
|
||||
|
||||
std::atomic<rpc::SCgi*> m_scgi{nullptr};
|
||||
std::string m_rpc_log_filename;
|
||||
};
|
||||
|
||||
} // namespace scgi
|
||||
|
||||
#endif // RTORRENT_SCGI_THREAD_SCGI_H
|
||||
@@ -0,0 +1,192 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "download_storer.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <fstream>
|
||||
#include <unistd.h>
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/object.h>
|
||||
#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"
|
||||
#include "utils/directory.h"
|
||||
|
||||
namespace session {
|
||||
|
||||
DownloadStorer::DownloadStorer(core::Download* download)
|
||||
: m_download(download) {
|
||||
}
|
||||
|
||||
void
|
||||
DownloadStorer::build_streams(bool skip_static) {
|
||||
auto* download = m_download->download();
|
||||
|
||||
auto& resume_base = download->bencode()->get_key("libtorrent_resume");
|
||||
auto& rtorrent_base = download->bencode()->get_key("rtorrent");
|
||||
|
||||
rtorrent_base.insert_key("chunks_done", download->file_list()->completed_chunks());
|
||||
rtorrent_base.insert_key("chunks_wanted", download->data()->wanted_chunks());
|
||||
rtorrent_base.insert_key("total_uploaded", m_download->info()->up_rate()->total());
|
||||
rtorrent_base.insert_key("total_downloaded", m_download->info()->down_rate()->total());
|
||||
|
||||
// Don't save for completed torrents when we've cleared the uncertain_pieces.
|
||||
torrent::resume_save_progress(*download, resume_base);
|
||||
torrent::resume_save_uncertain_pieces(*download, resume_base);
|
||||
|
||||
torrent::resume_save_addresses(*download, resume_base);
|
||||
torrent::resume_save_file_priorities(*download, resume_base);
|
||||
torrent::resume_save_tracker_settings(*download, resume_base);
|
||||
|
||||
// Temp fixing of all flags, move to a better place:
|
||||
resume_base.set_flags(torrent::Object::flag_session_data);
|
||||
rtorrent_base.set_flags(torrent::Object::flag_session_data);
|
||||
|
||||
auto torrent_stream = std::unique_ptr<std::stringstream>();
|
||||
auto resume_stream = std::make_unique<std::stringstream>();
|
||||
auto rtorrent_stream = std::make_unique<std::stringstream>();
|
||||
|
||||
if (!skip_static) {
|
||||
torrent_stream = std::make_unique<std::stringstream>();
|
||||
torrent::object_write_bencode(&*torrent_stream, m_download->bencode(), torrent::Object::flag_session_data);
|
||||
|
||||
if (!torrent_stream->good())
|
||||
throw torrent::internal_error("DownloadStorer::build_streams() failed to write torrent stream.");
|
||||
}
|
||||
|
||||
torrent::object_write_bencode(&*resume_stream, &resume_base, 0);
|
||||
|
||||
if (!resume_stream->good())
|
||||
throw torrent::internal_error("DownloadStorer::build_streams() failed to write resume stream.");
|
||||
|
||||
torrent::object_write_bencode(&*rtorrent_stream, &rtorrent_base, 0);
|
||||
|
||||
if (!rtorrent_stream->good())
|
||||
throw torrent::internal_error("DownloadStorer::build_streams() failed to write rtorrent stream.");
|
||||
|
||||
m_torrent_stream = std::move(torrent_stream);
|
||||
m_rtorrent_stream = std::move(rtorrent_stream);
|
||||
m_libtorrent_stream = std::move(resume_stream);
|
||||
}
|
||||
|
||||
std::string
|
||||
DownloadStorer::build_path(const std::string& session_path) {
|
||||
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::utils::transform_to_hex_str(m_download->info()->info_hash()) + ".torrent";
|
||||
}
|
||||
|
||||
void
|
||||
DownloadStorer::unlink_files(const std::string& session_path) {
|
||||
auto base_path = build_path(session_path);
|
||||
|
||||
auto torrent_path = base_path;
|
||||
auto libtorrent_path = base_path + ".libtorrent_resume";
|
||||
auto rtorrent_path = base_path + ".rtorrent";
|
||||
auto metadata_path = base_path + ".meta";
|
||||
|
||||
::unlink(libtorrent_path.c_str());
|
||||
::unlink(rtorrent_path.c_str());
|
||||
::unlink(torrent_path.c_str());
|
||||
::unlink(metadata_path.c_str());
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool
|
||||
is_correct_format(const std::string& f) {
|
||||
if (f.size() != 48 || f.substr(40) != ".torrent")
|
||||
return false;
|
||||
|
||||
for (std::string::const_iterator itr = f.begin(); itr != f.end() - 8; ++itr)
|
||||
if (!(*itr >= '0' && *itr <= '9') &&
|
||||
!(*itr >= 'A' && *itr <= 'F'))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
save_stream(const std::string& path, bool use_fsyncdisk, const std::stringstream& stream) {
|
||||
std::fstream output(path.c_str(), std::ios::out | std::ios::trunc);
|
||||
|
||||
// TODO: If we cannot open more files, wait for some to finish and try again.
|
||||
if (!output.is_open())
|
||||
throw torrent::storage_error("failed to open file for writing : " + path);
|
||||
|
||||
output << stream.rdbuf();
|
||||
|
||||
if (!output.good())
|
||||
throw torrent::storage_error("failed to write stream to file : " + path);
|
||||
|
||||
output.close();
|
||||
|
||||
// Ensure that the new file is actually written to the disk
|
||||
int fd = ::open(path.c_str(), O_WRONLY);
|
||||
|
||||
if (fd < 0)
|
||||
throw torrent::storage_error("failed to open file descriptor for fsync : " + path);
|
||||
|
||||
if (use_fsyncdisk) {
|
||||
#ifdef __APPLE__
|
||||
::fsync(fd);
|
||||
#else
|
||||
::fdatasync(fd);
|
||||
#endif
|
||||
}
|
||||
|
||||
::close(fd);
|
||||
}
|
||||
|
||||
} // namespace anonymous
|
||||
|
||||
void
|
||||
DownloadStorer::save_and_move_streams(const std::string& path, bool use_fsyncdisk,
|
||||
const std::stringstream* torrent_stream,
|
||||
const std::stringstream* rtorrent_stream,
|
||||
const std::stringstream* libtorrent_stream) {
|
||||
auto torrent_path = path;
|
||||
auto libtorrent_path = path + ".libtorrent_resume";
|
||||
auto rtorrent_path = path + ".rtorrent";
|
||||
|
||||
if (torrent_stream)
|
||||
save_stream(torrent_path + ".new", use_fsyncdisk, *torrent_stream);
|
||||
|
||||
save_stream(libtorrent_path + ".new", use_fsyncdisk, *libtorrent_stream);
|
||||
save_stream(rtorrent_path + ".new", use_fsyncdisk, *rtorrent_stream);
|
||||
|
||||
if (torrent_stream) {
|
||||
if (::rename((torrent_path + ".new").c_str(), torrent_path.c_str()) == -1)
|
||||
throw torrent::storage_error("failed to rename torrent file : " + torrent_path);
|
||||
}
|
||||
|
||||
if (::rename((libtorrent_path + ".new").c_str(), libtorrent_path.c_str()) == -1)
|
||||
throw torrent::storage_error("failed to rename libtorrent resume file : " + libtorrent_path);
|
||||
|
||||
if (::rename((rtorrent_path + ".new").c_str(), rtorrent_path.c_str()) == -1)
|
||||
throw torrent::storage_error("failed to rename rtorrent resume file : " + rtorrent_path);
|
||||
}
|
||||
|
||||
utils::Directory
|
||||
DownloadStorer::get_formated_entries(const std::string& session_path) {
|
||||
if (session_path.empty())
|
||||
return utils::Directory();
|
||||
|
||||
utils::Directory d(session_path);
|
||||
|
||||
if (!d.update(utils::Directory::update_hide_dot))
|
||||
throw torrent::storage_error("session::DownloadStorer::update() could not open session directory: " + session_path);
|
||||
|
||||
d.erase(std::remove_if(d.begin(), d.end(), [](auto& entry) { return !is_correct_format(entry.s_name); }), d.end());
|
||||
return d;
|
||||
}
|
||||
|
||||
} // namespace session
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef RTORRENT_SESSION_DOWNLOAD_STORER_H
|
||||
#define RTORRENT_SESSION_DOWNLOAD_STORER_H
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <torrent/common.h>
|
||||
|
||||
namespace core {
|
||||
class Download;
|
||||
}
|
||||
|
||||
namespace utils {
|
||||
class Directory;
|
||||
}
|
||||
|
||||
namespace session {
|
||||
|
||||
class DownloadStorer {
|
||||
public:
|
||||
DownloadStorer(core::Download* download);
|
||||
|
||||
core::Download* download() const { return m_download; }
|
||||
|
||||
std::string build_path(const std::string& session_path);
|
||||
|
||||
void build_full_streams() { build_streams(false); }
|
||||
void build_resume_streams() { build_streams(true); }
|
||||
|
||||
void unlink_files(const std::string& session_path);
|
||||
|
||||
auto torrent_stream() { return std::move(m_torrent_stream); }
|
||||
auto rtorrent_stream() { return std::move(m_rtorrent_stream); }
|
||||
auto libtorrent_stream() { return std::move(m_libtorrent_stream); }
|
||||
|
||||
static void save_and_move_streams(const std::string& path, bool use_fsyncdisk,
|
||||
const std::stringstream* torrent_stream,
|
||||
const std::stringstream* rtorrent_stream,
|
||||
const std::stringstream* libtorrent_stream);
|
||||
|
||||
static utils::Directory get_formated_entries(const std::string& session_path);
|
||||
|
||||
private:
|
||||
void build_streams(bool skip_static);
|
||||
|
||||
core::Download* m_download;
|
||||
|
||||
std::unique_ptr<std::stringstream> m_torrent_stream;
|
||||
std::unique_ptr<std::stringstream> m_rtorrent_stream;
|
||||
std::unique_ptr<std::stringstream> m_libtorrent_stream;
|
||||
};
|
||||
|
||||
} // namespace session
|
||||
|
||||
#endif // RTORRENT_SESSION_DOWNLOAD_STORER_H
|
||||
@@ -0,0 +1,564 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "session/session_manager.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/utils/log.h>
|
||||
|
||||
#include "globals.h"
|
||||
#include "session/download_storer.h"
|
||||
#include "utils/lockfile.h"
|
||||
|
||||
#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::system::Thread* thread)
|
||||
: m_thread(thread),
|
||||
m_lockfile(std::make_unique<utils::Lockfile>()) {
|
||||
}
|
||||
|
||||
SessionManager::~SessionManager() = default;
|
||||
|
||||
void
|
||||
SessionManager::set_path(std::string path) {
|
||||
assert(torrent::this_thread::thread() == torrent::main_thread::thread());
|
||||
|
||||
if (m_freeze_info)
|
||||
throw torrent::input_error("Session path cannot be changed after startup.");
|
||||
|
||||
path = expand_path(path);
|
||||
|
||||
if (path.empty() || path.back() == '/')
|
||||
m_path = path;
|
||||
else
|
||||
m_path = path + '/';
|
||||
}
|
||||
|
||||
void
|
||||
SessionManager::set_use_fsyncdisk(bool use_fsyncdisk) {
|
||||
assert(torrent::this_thread::thread() == torrent::main_thread::thread());
|
||||
|
||||
if (m_freeze_info)
|
||||
throw torrent::input_error("Session fsyncdisk option cannot be changed after startup.");
|
||||
|
||||
m_use_fsyncdisk = use_fsyncdisk;
|
||||
}
|
||||
|
||||
void
|
||||
SessionManager::set_use_lock(bool use_lock) {
|
||||
assert(torrent::this_thread::thread() == torrent::main_thread::thread());
|
||||
|
||||
if (m_freeze_info)
|
||||
throw torrent::input_error("Session lock option cannot be changed after startup.");
|
||||
|
||||
m_use_lock = use_lock;
|
||||
}
|
||||
|
||||
void
|
||||
SessionManager::save_resume_download(core::Download* download) {
|
||||
assert(torrent::this_thread::thread() == torrent::main_thread::thread());
|
||||
|
||||
if (m_path.empty())
|
||||
return;
|
||||
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
m_pending_builds.push_back(download);
|
||||
|
||||
callback_pending_builds();
|
||||
|
||||
LT_LOG("build of resume save data queued : download:%p", download);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SessionManager::save_full_download(core::Download* download) {
|
||||
assert(torrent::this_thread::thread() == torrent::main_thread::thread());
|
||||
|
||||
if (m_path.empty())
|
||||
return;
|
||||
|
||||
DownloadStorer storer(download);
|
||||
|
||||
storer.build_full_streams();
|
||||
|
||||
auto save_request = SaveRequest{
|
||||
download,
|
||||
storer.build_path(m_path),
|
||||
storer.torrent_stream(),
|
||||
storer.rtorrent_stream(),
|
||||
storer.libtorrent_stream()
|
||||
};
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
|
||||
LT_LOG("requesting save : download:%p path:%s", download, save_request.path.c_str());
|
||||
|
||||
if (!m_active)
|
||||
throw torrent::internal_error("SessionManager::save_download() called while not active.");
|
||||
|
||||
if (replace_save_request_unsafe(save_request)) {
|
||||
LT_LOG("updated pending save request with full save data : download:%p", download);
|
||||
return;
|
||||
}
|
||||
|
||||
m_save_requests.push_back(std::move(save_request));
|
||||
m_save_request_counter = m_save_requests.size();
|
||||
|
||||
LT_LOG("queued new full save request : download:%p", download);
|
||||
}
|
||||
|
||||
callback_save_request();
|
||||
}
|
||||
|
||||
void
|
||||
SessionManager::remove_download(core::Download* download) {
|
||||
assert(torrent::this_thread::thread() == torrent::main_thread::thread());
|
||||
|
||||
if (m_path.empty())
|
||||
return;
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
|
||||
if (!m_active)
|
||||
throw torrent::internal_error("SessionManager::remove_download() called while not active.");
|
||||
|
||||
if (remove_completely_unsafe(download, lock))
|
||||
LT_LOG("canceled pending save request : download:%p", download);
|
||||
|
||||
DownloadStorer(download).unlink_files(m_path);
|
||||
|
||||
LT_LOG("removed session files : download:%p", download);
|
||||
}
|
||||
|
||||
void
|
||||
SessionManager::start() {
|
||||
assert(torrent::this_thread::thread() == torrent::main_thread::thread());
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
|
||||
if (m_active || m_freeze_info)
|
||||
throw torrent::internal_error("SessionManager::start() called while already started.");
|
||||
|
||||
m_active = true;
|
||||
m_freeze_info = true;
|
||||
|
||||
if (m_path.empty()) {
|
||||
LT_LOG("session manager started with empty path, disabling session management", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
LT_LOG("starting session manager with path: %s", m_path.c_str());
|
||||
|
||||
if (m_use_lock) {
|
||||
m_lockfile->set_path(m_path + "rtorrent.lock");
|
||||
|
||||
if (!m_lockfile->try_lock()) {
|
||||
if (errno == ENOENT || errno == ENOTDIR || errno == EACCES)
|
||||
throw torrent::input_error("Could not lock session directory: " + std::string(std::strerror(errno)) + " : " + m_path);
|
||||
else
|
||||
throw torrent::input_error("Could not lock session directory, held by: " + m_lockfile->locked_by_as_string() + " : " + m_path);
|
||||
}
|
||||
|
||||
LT_LOG("locked session directory: %s", m_path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SessionManager::cleanup() {
|
||||
assert(m_thread == torrent::this_thread::thread());
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
|
||||
if (!m_active)
|
||||
throw torrent::internal_error("SessionManager::cleanup() called while not active.");
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> pending_lock(m_pending_builds_mutex);
|
||||
|
||||
if (!m_pending_builds.empty())
|
||||
throw torrent::internal_error("SessionManager::cleanup() called with pending builds.");
|
||||
}
|
||||
|
||||
m_active = false;
|
||||
|
||||
if (m_path.empty()) {
|
||||
LT_LOG("session manager cleanup called with empty path, skipping", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
LT_LOG("cleaning up session manager with path: %s", m_path.c_str());
|
||||
|
||||
flush_all_and_wait_unsafe(lock);
|
||||
|
||||
if (m_use_lock) {
|
||||
if (!m_lockfile->unlock())
|
||||
LT_LOG("could not unlock session directory: %s", m_path.c_str());
|
||||
|
||||
LT_LOG("unlocked session directory: %s", m_path.c_str());
|
||||
}
|
||||
|
||||
LT_LOG("session manager cleaned up", 0);
|
||||
|
||||
session_thread::cancel_callback(this);
|
||||
torrent::main_thread::cancel_callback(this);
|
||||
}
|
||||
|
||||
void
|
||||
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(this, [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(this, [this]() { process_save_request(); });
|
||||
}
|
||||
|
||||
void
|
||||
SessionManager::callback_finished_saves() {
|
||||
if (m_callback_scheduled_process_finished_saves.exchange(true))
|
||||
return;
|
||||
|
||||
session_thread::callback(this, [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;
|
||||
|
||||
// Only main thread is allowed to process pending builds or remove downloads, as such it is safe
|
||||
// to unlock before adding them to save requests.
|
||||
{
|
||||
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;
|
||||
|
||||
auto* download = m_pending_builds.front();
|
||||
m_pending_builds.pop_front();
|
||||
|
||||
LT_LOG("processing pending resume save : download:%p", download);
|
||||
|
||||
DownloadStorer storer(download);
|
||||
|
||||
storer.build_resume_streams();
|
||||
|
||||
auto save_request = SaveRequest{
|
||||
download,
|
||||
storer.build_path(m_path),
|
||||
nullptr,
|
||||
storer.rtorrent_stream(),
|
||||
storer.libtorrent_stream()
|
||||
};
|
||||
|
||||
requests.push_back(std::move(save_request));
|
||||
}
|
||||
}
|
||||
|
||||
if (requests.empty())
|
||||
return;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> save_lock(m_mutex);
|
||||
|
||||
if (!m_active)
|
||||
throw torrent::internal_error("SessionManager::process_pending_builds() called while not active.");
|
||||
|
||||
for (auto& save_request : requests) {
|
||||
if (replace_save_request_unsafe(save_request)) {
|
||||
LT_LOG("updated pending resume save request : download:%p", save_request.download);
|
||||
continue;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_flushing)
|
||||
callback_save_request();
|
||||
}
|
||||
|
||||
void
|
||||
SessionManager::process_save_request() {
|
||||
assert(m_thread == torrent::this_thread::thread());
|
||||
|
||||
if (m_path.empty())
|
||||
throw torrent::internal_error("SessionManager::process_save_request() called with empty path.");
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
|
||||
m_callback_scheduled_process_saves_request = false;
|
||||
|
||||
if (!m_active)
|
||||
throw torrent::internal_error("SessionManager::process_save_request() called while not active.");
|
||||
|
||||
while (!m_save_requests.empty()) {
|
||||
if (m_processing_saves.size() >= max_concurrent_processing)
|
||||
break;
|
||||
|
||||
process_next_save_request_unsafe();
|
||||
}
|
||||
}
|
||||
|
||||
callback_pending_builds();
|
||||
}
|
||||
|
||||
void
|
||||
SessionManager::process_next_save_request_unsafe() {
|
||||
auto request = std::move(m_save_requests.front());
|
||||
|
||||
m_save_requests.pop_front();
|
||||
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);
|
||||
|
||||
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 {
|
||||
DownloadStorer::save_and_move_streams(itr->second.path, m_use_fsyncdisk,
|
||||
itr->second.torrent_stream.get(),
|
||||
itr->second.rtorrent_stream.get(),
|
||||
itr->second.libtorrent_stream.get());
|
||||
} catch (...) {
|
||||
cleanup_fn();
|
||||
throw;
|
||||
}
|
||||
|
||||
cleanup_fn();
|
||||
});
|
||||
|
||||
LT_LOG("started save of download : download:%p path:%s", itr->second.download, itr->second.path.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
SessionManager::process_finished_saves() {
|
||||
assert(m_thread == torrent::this_thread::thread());
|
||||
|
||||
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.");
|
||||
|
||||
for (auto& request : m_finished_saves) {
|
||||
try {
|
||||
request.first.get();
|
||||
|
||||
} catch (torrent::storage_error& e) {
|
||||
LT_LOG("error saving download : storage error :download:%p path:%s : %s", request.second.download, request.second.path.c_str(), e.what());
|
||||
|
||||
if (m_last_storage_error_message + std::chrono::minutes(5) > torrent::this_thread::cached_time()) {
|
||||
m_ignored_storage_error_count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
lt_log_print(torrent::LOG_ERROR, "Storage errors saving session data for download: ignored:%u : %s", m_ignored_storage_error_count, e.what());
|
||||
|
||||
m_last_storage_error_message = torrent::this_thread::cached_time();
|
||||
m_ignored_storage_error_count = 0;
|
||||
|
||||
continue;
|
||||
|
||||
} catch (torrent::internal_error& e) {
|
||||
LT_LOG("error saving download : internal error : download:%p path:%s : %s", request.second.download, request.second.path.c_str(), e.what());
|
||||
throw;
|
||||
|
||||
} catch (...) {
|
||||
LT_LOG("error saving download : unknown error : download:%p path:%s", request.second.download, request.second.path.c_str());
|
||||
throw;
|
||||
}
|
||||
|
||||
LT_LOG("finished saving download : download:%p path:%s", request.second.download, request.second.path.c_str());
|
||||
}
|
||||
|
||||
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_save_counter >= max_cleanup_processing) {
|
||||
m_finished_condition.wait(lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
process_next_save_request_unsafe();
|
||||
}
|
||||
|
||||
while (!m_processing_saves.empty())
|
||||
m_finished_condition.wait(lock);
|
||||
|
||||
for (auto& request : m_finished_saves)
|
||||
LT_LOG("finished saving download : download:%p path:%s", request.second.download, request.second.path.c_str());
|
||||
|
||||
m_finished_saves.clear();
|
||||
|
||||
LT_LOG("flushed all pending saves", 0);
|
||||
}
|
||||
|
||||
bool
|
||||
SessionManager::replace_save_request_unsafe(SaveRequest& save_request) {
|
||||
// Can be run in any thread.
|
||||
|
||||
auto itr = std::find_if(m_save_requests.begin(), m_save_requests.end(), [download = save_request.download](auto& req) {
|
||||
return req.download == download;
|
||||
});
|
||||
|
||||
if (itr == m_save_requests.end())
|
||||
return false;
|
||||
|
||||
if (itr->path != save_request.path)
|
||||
throw torrent::internal_error("SessionManager::replace_save_request_unsafe() path mismatch on replace: " + itr->path + " != " + save_request.path);
|
||||
|
||||
// If the existing request is a full save (created during torrent initialization),
|
||||
// keep its torrent_stream but update the resume streams with newer data.
|
||||
//
|
||||
// Otherwise, replace all streams.
|
||||
|
||||
if (itr->torrent_stream != nullptr) {
|
||||
// Keep existing full-save torrent stream, only update resume streams
|
||||
if (save_request.rtorrent_stream != nullptr)
|
||||
itr->rtorrent_stream = std::move(save_request.rtorrent_stream);
|
||||
|
||||
if (save_request.libtorrent_stream != nullptr)
|
||||
itr->libtorrent_stream = std::move(save_request.libtorrent_stream);
|
||||
|
||||
} else {
|
||||
// No full save pending, replace everything
|
||||
itr->torrent_stream = std::move(save_request.torrent_stream);
|
||||
itr->rtorrent_stream = std::move(save_request.rtorrent_stream);
|
||||
itr->libtorrent_stream = std::move(save_request.libtorrent_stream);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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_pending = [this, download]() {
|
||||
std::unique_lock<std::mutex> pending_lock(m_pending_builds_mutex);
|
||||
|
||||
auto itr = std::remove_if(m_pending_builds.begin(), m_pending_builds.end(), [download](auto* req) {
|
||||
return req == download;
|
||||
});
|
||||
|
||||
if (itr == m_pending_builds.end())
|
||||
return false;
|
||||
|
||||
m_pending_builds.erase(itr, m_pending_builds.end());
|
||||
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()) {
|
||||
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) {
|
||||
auto itr = std::find_if(m_processing_saves.begin(), m_processing_saves.end(), [download](auto& req) {
|
||||
return req.second.download == download;
|
||||
});
|
||||
|
||||
if (itr == m_processing_saves.end())
|
||||
break;
|
||||
|
||||
m_finished_condition.wait(lock);
|
||||
}
|
||||
|
||||
// Since we're the main thread, no more requests for this download can be added.
|
||||
|
||||
// Checking active after remove_save_request_unsafe to ensure we're calling this after shutdown.
|
||||
if (!m_active)
|
||||
throw torrent::internal_error("SessionManager::remove_completely_unsafe() called while not active.");
|
||||
|
||||
return removed_something;
|
||||
}
|
||||
|
||||
} // namespace session
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
#ifndef RTORRENT_SESSION_SESSION_MANAGER_H
|
||||
#define RTORRENT_SESSION_SESSION_MANAGER_H
|
||||
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <future>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <torrent/common.h>
|
||||
|
||||
class Control;
|
||||
|
||||
namespace core {
|
||||
class Download;
|
||||
}
|
||||
|
||||
namespace utils {
|
||||
class Lockfile;
|
||||
}
|
||||
|
||||
namespace session {
|
||||
|
||||
class ThreadSession;
|
||||
|
||||
struct SaveRequest {
|
||||
core::Download* download;
|
||||
std::string path;
|
||||
std::unique_ptr<std::stringstream> torrent_stream;
|
||||
std::unique_ptr<std::stringstream> rtorrent_stream;
|
||||
std::unique_ptr<std::stringstream> libtorrent_stream;
|
||||
};
|
||||
|
||||
class SessionManager {
|
||||
public:
|
||||
typedef std::unique_ptr<std::stringstream> stream_ptr;
|
||||
|
||||
// TODO: This should depend on max open sockets / be configurable.
|
||||
|
||||
constexpr static int max_concurrent_requests = 16;
|
||||
constexpr static int max_concurrent_processing = 16;
|
||||
constexpr static int max_cleanup_processing = 64;
|
||||
|
||||
SessionManager(torrent::system::Thread* thread);
|
||||
~SessionManager();
|
||||
|
||||
bool is_used() const;
|
||||
|
||||
std::string path() const;
|
||||
void set_path(std::string path);
|
||||
|
||||
bool use_fsyncdisk() const;
|
||||
void set_use_fsyncdisk(bool use_fsyncdisk);
|
||||
|
||||
bool use_lock() const;
|
||||
void set_use_lock(bool use_lock);
|
||||
|
||||
void save_full_download(core::Download* download);
|
||||
void save_resume_download(core::Download* download);
|
||||
void remove_download(core::Download* download);
|
||||
|
||||
protected:
|
||||
friend class ::Control;
|
||||
friend class ThreadSession;
|
||||
|
||||
void save_download(core::Download* download, bool skip_static);
|
||||
|
||||
void start();
|
||||
void cleanup();
|
||||
|
||||
void flush_all_pending_builds();
|
||||
|
||||
private:
|
||||
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_next_save_request_unsafe();
|
||||
void process_finished_saves();
|
||||
|
||||
// Requires a higher number of open sockets, and should only be used during shutdown.
|
||||
void flush_all_and_wait_unsafe(std::unique_lock<std::mutex>& lock);
|
||||
|
||||
bool replace_save_request_unsafe(SaveRequest& download);
|
||||
bool remove_completely_unsafe(core::Download* download, std::unique_lock<std::mutex>& lock);
|
||||
|
||||
torrent::system::Thread* m_thread;
|
||||
|
||||
bool m_freeze_info{};
|
||||
std::string m_path;
|
||||
bool m_use_fsyncdisk{true};
|
||||
bool m_use_lock{true};
|
||||
|
||||
std::mutex m_mutex;
|
||||
bool m_active{};
|
||||
|
||||
typedef std::pair<std::future<void>, SaveRequest> ProcessingSave;
|
||||
|
||||
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{};
|
||||
|
||||
// Pending builds are only ever locked by main thread.
|
||||
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_builds(true); }
|
||||
|
||||
} // namespace session
|
||||
|
||||
#endif // RTORRENT_SESSION_SESSION_MANAGER_H
|
||||
@@ -0,0 +1,87 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "thread_session.h"
|
||||
|
||||
#include <torrent/exceptions.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
|
||||
ThreadSession::create_thread() {
|
||||
auto thread = new ThreadSession;
|
||||
|
||||
thread->m_manager = std::make_unique<SessionManager>(thread);
|
||||
|
||||
m_thread_session = thread;
|
||||
m_thread_session->m_state = STATE_INITIALIZED;
|
||||
}
|
||||
|
||||
void
|
||||
ThreadSession::destroy_thread() {
|
||||
delete m_thread_session;
|
||||
m_thread_session = nullptr;
|
||||
}
|
||||
|
||||
ThreadSession*
|
||||
ThreadSession::thread_session() {
|
||||
return m_thread_session;
|
||||
}
|
||||
|
||||
void
|
||||
ThreadSession::init_thread_pre_start() {
|
||||
m_manager->start();
|
||||
}
|
||||
|
||||
// TODO: Make sure we trigger session save before main thread exits, that it adds all required
|
||||
// downloads to the queue.
|
||||
void
|
||||
ThreadSession::cleanup_thread() {
|
||||
m_manager->cleanup();
|
||||
}
|
||||
|
||||
void
|
||||
ThreadSession::call_events() {
|
||||
// lt_log_print_locked(torrent::LOG_THREAD_NOTICE, "Got thread_disk tick.");
|
||||
|
||||
process_callbacks();
|
||||
|
||||
if ((m_flags & flag_do_shutdown)) {
|
||||
if ((m_flags & flag_did_shutdown))
|
||||
throw torrent::internal_error("Already trigged shutdown.");
|
||||
|
||||
m_flags |= flag_did_shutdown;
|
||||
throw torrent::shutdown_exception();
|
||||
}
|
||||
}
|
||||
|
||||
std::chrono::microseconds
|
||||
ThreadSession::next_timeout() {
|
||||
// TODO: This leads to kqueue crash?
|
||||
// return std::chrono::microseconds(1h);
|
||||
return std::chrono::microseconds(10min);
|
||||
}
|
||||
|
||||
} // namespace session
|
||||
|
||||
namespace session_thread {
|
||||
|
||||
torrent::system::Thread* thread() { return session::ThreadSessionInternal::thread_session(); }
|
||||
std::thread::id thread_id() { return session::ThreadSessionInternal::thread_session()->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); }
|
||||
|
||||
session::SessionManager* manager() { return session::ThreadSessionInternal::thread_session()->manager(); }
|
||||
std::string session_path() { return session::ThreadSessionInternal::thread_session()->manager()->path(); }
|
||||
|
||||
} // namespace session_thread
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef RTORRENT_SESSION_THREAD_SESSION_H
|
||||
#define RTORRENT_SESSION_THREAD_SESSION_H
|
||||
|
||||
#include <torrent/system/thread.h>
|
||||
|
||||
namespace session {
|
||||
|
||||
class SessionManager;
|
||||
class ThreadSessionInternal;
|
||||
|
||||
class ThreadSession : public torrent::system::Thread {
|
||||
public:
|
||||
|
||||
static void create_thread();
|
||||
static void destroy_thread();
|
||||
static ThreadSession* thread_session();
|
||||
|
||||
const char* name() const override { return "rtorrent-session"; }
|
||||
|
||||
SessionManager* manager() const { return m_manager.get(); }
|
||||
|
||||
protected:
|
||||
friend class ThreadSessionInternal;
|
||||
|
||||
ThreadSession() = default;
|
||||
|
||||
static auto internal_thread_session() { return m_thread_session; }
|
||||
|
||||
void init_thread_pre_start() override;
|
||||
void cleanup_thread() override;
|
||||
|
||||
void call_events() override;
|
||||
std::chrono::microseconds next_timeout() override;
|
||||
|
||||
private:
|
||||
static ThreadSession* m_thread_session;
|
||||
|
||||
std::unique_ptr<SessionManager> m_manager;
|
||||
};
|
||||
|
||||
} // namespace session
|
||||
|
||||
#endif // RTORRENT_SESSION_THREAD_SESSION_H
|
||||
+220
@@ -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
@@ -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
|
||||
+9
-42
@@ -1,44 +1,11 @@
|
||||
// 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 <cerrno>
|
||||
#include <cstring>
|
||||
#include <signal.h>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "rak/error_number.h"
|
||||
#include "signal_handler.h"
|
||||
|
||||
#ifdef __sun__
|
||||
@@ -50,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);
|
||||
@@ -59,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);
|
||||
@@ -68,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)
|
||||
@@ -80,14 +47,14 @@ SignalHandler::set_handler(unsigned int signum, slot_void slot) {
|
||||
sa.sa_handler = &SignalHandler::caught;
|
||||
|
||||
if (sigaction(signum, &sa, NULL) == -1)
|
||||
throw std::logic_error("Could not set sigaction: " + std::string(rak::error_number::current().c_str()));
|
||||
throw std::logic_error("Could not set sigaction: " + std::string(std::strerror(errno)));
|
||||
else
|
||||
m_handlers[signum] = 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;
|
||||
@@ -97,12 +64,12 @@ SignalHandler::set_sigaction_handler(unsigned int signum, handler_slot slot) {
|
||||
sigemptyset(&sa.sa_mask);
|
||||
|
||||
if (sigaction(signum, &sa, NULL) == -1)
|
||||
throw std::logic_error("Could not set sigaction: " + std::string(rak::error_number::current().c_str()));
|
||||
throw std::logic_error("Could not set sigaction: " + std::string(std::strerror(errno)));
|
||||
}
|
||||
|
||||
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,97 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "thread_worker.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <cassert>
|
||||
#include <rak/path.h>
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
#include "globals.h"
|
||||
#include "control.h"
|
||||
#include "core/manager.h"
|
||||
#include "rpc/scgi.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
|
||||
ThreadWorker::~ThreadWorker() = default;
|
||||
|
||||
void
|
||||
ThreadWorker::init_thread() {
|
||||
m_state = STATE_INITIALIZED;
|
||||
}
|
||||
|
||||
void
|
||||
ThreadWorker::cleanup_thread() {
|
||||
if (m_scgi != nullptr)
|
||||
m_scgi.load()->deactivate();
|
||||
}
|
||||
|
||||
bool
|
||||
ThreadWorker::set_scgi(rpc::SCgi* scgi) {
|
||||
rpc::SCgi* expected = nullptr;
|
||||
|
||||
if (!m_scgi.compare_exchange_strong(expected, scgi))
|
||||
return false;
|
||||
|
||||
change_rpc_log();
|
||||
|
||||
callback(nullptr, [this]() {
|
||||
if (m_scgi == NULL)
|
||||
throw torrent::internal_error("Tried to start SCGI but object was not present.");
|
||||
|
||||
m_scgi.load()->activate();
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ThreadWorker::set_rpc_log(const std::string& filename) {
|
||||
callback(nullptr, [this, filename]() {
|
||||
m_rpc_log_filename = filename;
|
||||
change_rpc_log();
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
ThreadWorker::change_rpc_log() {
|
||||
if (scgi() == NULL)
|
||||
return;
|
||||
|
||||
if (scgi()->log_fd() != -1) {
|
||||
::close(scgi()->log_fd());
|
||||
scgi()->set_log_fd(-1);
|
||||
control->core()->push_log("Closed RPC log.");
|
||||
}
|
||||
|
||||
if (m_rpc_log_filename.empty())
|
||||
return;
|
||||
|
||||
scgi()->set_log_fd(open(rak::path_expand(m_rpc_log_filename).c_str(), O_WRONLY | O_APPEND | O_CREAT, 0644));
|
||||
|
||||
if (scgi()->log_fd() == -1) {
|
||||
control->core()->push_log_std("Could not open RPC log file '" + m_rpc_log_filename + "'.");
|
||||
return;
|
||||
}
|
||||
|
||||
control->core()->push_log_std("Logging RPC events to '" + m_rpc_log_filename + "'.");
|
||||
}
|
||||
|
||||
void
|
||||
ThreadWorker::call_events() {
|
||||
if ((m_flags & flag_do_shutdown)) {
|
||||
if ((m_flags & flag_did_shutdown))
|
||||
throw torrent::internal_error("Already trigged shutdown.");
|
||||
|
||||
m_flags |= flag_did_shutdown;
|
||||
throw torrent::shutdown_exception();
|
||||
}
|
||||
|
||||
process_callbacks();
|
||||
}
|
||||
|
||||
std::chrono::microseconds
|
||||
ThreadWorker::next_timeout() {
|
||||
return std::chrono::microseconds(10min);
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
#ifndef RTORRENT_THREAD_WORKER_H
|
||||
#define RTORRENT_THREAD_WORKER_H
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <torrent/utils/thread.h>
|
||||
|
||||
namespace rpc {
|
||||
class SCgi;
|
||||
}
|
||||
|
||||
class ThreadWorker : public torrent::utils::Thread {
|
||||
public:
|
||||
ThreadWorker() = default;
|
||||
~ThreadWorker();
|
||||
|
||||
const char* name() const override { return "rtorrent scgi"; }
|
||||
|
||||
void init_thread() override;
|
||||
void cleanup_thread() override;
|
||||
|
||||
rpc::SCgi* scgi() { return m_scgi; }
|
||||
bool set_scgi(rpc::SCgi* scgi);
|
||||
|
||||
void set_rpc_log(const std::string& filename);
|
||||
|
||||
private:
|
||||
void task_touch_log();
|
||||
void change_rpc_log();
|
||||
|
||||
void call_events() override;
|
||||
std::chrono::microseconds next_timeout() override;
|
||||
|
||||
std::atomic<rpc::SCgi*> m_scgi{nullptr};
|
||||
std::string m_rpc_log_filename;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -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>
|
||||
|
||||
+15
-18
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -167,6 +167,8 @@ ElementDownloadList::receive_home() {
|
||||
|
||||
void
|
||||
ElementDownloadList::receive_end() {
|
||||
if (m_view->size_visible() == 0)
|
||||
return;
|
||||
m_view->set_focus(m_view->end_visible() - 1);
|
||||
m_view->set_last_changed();
|
||||
}
|
||||
@@ -221,10 +223,10 @@ ElementDownloadList::receive_change_view(const std::string& name) {
|
||||
|
||||
std::string old_name = view() ? view()->name() : "";
|
||||
if (!old_name.empty())
|
||||
rpc::commands.call_catch("event.view.hide", rpc::make_target(), name, "View hide event action failed: ");
|
||||
rpc::commands.call_catch("event.view.hide", rpc::make_target(), old_name, "View hide event action failed: ");
|
||||
set_view(*itr);
|
||||
if (!old_name.empty())
|
||||
rpc::commands.call_catch("event.view.show", rpc::make_target(), old_name, "View show event action failed: ");
|
||||
if (!name.empty())
|
||||
rpc::commands.call_catch("event.view.show", rpc::make_target(), name, "View show event action failed: ");
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user