mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-08-07 02:32:32 +00:00
Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 68fdb86c72 | |||
| 6b8a285e75 | |||
| 8f0331625a | |||
| 3c65afcf8c | |||
| cd9948a4a8 | |||
| 0d1b9e9d55 | |||
| 6561c00ae3 | |||
| b8cb828d96 | |||
| e3be3b2c12 | |||
| 0f93fa109c | |||
| 7e1193acab | |||
| 5792ed1ae2 | |||
| 8cfc71b936 | |||
| f2b065a798 | |||
| f33c2560a3 | |||
| db1e78fa9d | |||
| 48c40ee0c6 | |||
| 3aeb213dc4 | |||
| 5a200f5d8f | |||
| 713067ba94 | |||
| fea2cbfac6 | |||
| 6bf1731a1c | |||
| f2caa2651d | |||
| 511895a8b6 | |||
| 4e64fbc78b | |||
| 93b3a34f28 | |||
| ea1ca00f31 | |||
| 58b1fc4914 | |||
| 577818bde4 | |||
| 0ea7615d0a | |||
| 73c1233dc0 | |||
| 11ddc35d02 | |||
| 036916fd9f | |||
| b3fabd2a2b | |||
| 4b7c3ffafe | |||
| 730d43b6c7 | |||
| af2f30c183 | |||
| 36fd837d27 | |||
| 8dd8d691c5 | |||
| e367b162ab | |||
| 49cdfee65a | |||
| 5680db3598 | |||
| 4768eb8356 | |||
| fa2c7d961b | |||
| d6dcbc4a69 | |||
| cadfcb50d2 | |||
| 71a39cd8a0 | |||
| 0658486966 | |||
| 687fbbb83d | |||
| e355eb561f | |||
| 902de0b258 | |||
| 01e5df4167 | |||
| 8d85ce5857 | |||
| f9c9fa2e3b | |||
| 337897784d | |||
| 782b1e4ea8 | |||
| 3cfc213053 | |||
| b789d83b70 | |||
| 67babb65d6 | |||
| e0d5c95821 | |||
| 7f4c5df5ee | |||
| dd45684584 | |||
| fb8632e990 | |||
| b043b7b39e | |||
| 168093d256 | |||
| 0c2f0b7715 | |||
| 4a9d9952e1 | |||
| fd1d5dbdb2 | |||
| dd0c62c7f7 | |||
| 3a8e462bd3 | |||
| b360a734f1 | |||
| c18df7a366 | |||
| a269f808e1 | |||
| 4444df2e97 | |||
| 5a194ab037 | |||
| 1b43f8b591 | |||
| d6427203a4 | |||
| 87c6422052 | |||
| 6ab265ecd4 | |||
| 203c66a287 | |||
| c9fe2facfa | |||
| 5abcf52e55 | |||
| f2f84dd97a | |||
| 34de2b4ac9 | |||
| ef937863fd | |||
| 00fa4127e1 | |||
| 1b0cb722e6 | |||
| af23ca8032 | |||
| c85f9d6318 | |||
| ee120730cc | |||
| b284be6a66 | |||
| 6e976616a0 | |||
| 38b39bdafc | |||
| 48718bdac8 | |||
| 5ce84929e4 | |||
| c1d076f182 | |||
| f929594763 |
@@ -0,0 +1,33 @@
|
||||
---
|
||||
BasedOnStyle: LLVM
|
||||
Standard: c++14
|
||||
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AlwaysBreakAfterReturnType: TopLevelDefinitions
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakStringLiterals: false
|
||||
ColumnLimit: 0
|
||||
ContinuationIndentWidth: 2
|
||||
IndentCaseLabels: false
|
||||
IndentWidth: 2
|
||||
PenaltyReturnTypeOnItsOwnLine: 130
|
||||
PointerAlignment: Left
|
||||
|
||||
AlignEscapedNewlines: Right
|
||||
AlignConsecutiveDeclarations:
|
||||
Enabled: true
|
||||
AcrossEmptyLines: true
|
||||
AcrossComments: false
|
||||
AlignConsecutiveMacros:
|
||||
Enabled: true
|
||||
AlignConsecutiveAssignments:
|
||||
Enabled: true
|
||||
|
||||
IncludeCategories:
|
||||
- Regex: "^(config|globals)\\.h"
|
||||
Priority: -1
|
||||
- Regex: "^torrent/.*"
|
||||
Priority: 1
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
---
|
||||
Checks: '-*,readability-identifier-naming'
|
||||
FormatStyle: 'file'
|
||||
CheckOptions:
|
||||
- key: readability-identifier-naming.LocalVariableCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.ParameterCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.FunctionCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.PrivateMemberPrefix
|
||||
value: m_
|
||||
- key: readability-identifier-naming.PrivateMemberCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.ClassConstantCase
|
||||
value: lower_case
|
||||
@@ -0,0 +1,132 @@
|
||||
name: Static analysis
|
||||
|
||||
on: pull_request
|
||||
|
||||
jobs:
|
||||
|
||||
clang-tidy:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Update Packages
|
||||
run: |
|
||||
sudo apt-get update
|
||||
- name: Fetch libtorrent
|
||||
run: |
|
||||
git clone https://github.com/rakshasa/libtorrent
|
||||
- name: Build libtorrent
|
||||
run: |
|
||||
cd libtorrent
|
||||
libtoolize
|
||||
aclocal -I scripts
|
||||
autoconf -i
|
||||
autoheader
|
||||
automake --add-missing
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
cd ..
|
||||
rm -rf libtorrent
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0
|
||||
- name: Fetch base branch
|
||||
run: |
|
||||
git remote add upstream "https://github.com/${{ github.event.pull_request.base.repo.full_name }}"
|
||||
git fetch --no-tags --no-recurse-submodules upstream "${{ github.event.pull_request.base.ref }}"
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get install -y bear clang-tidy libcurl4-openssl-dev
|
||||
- name: Configure Project
|
||||
run: |
|
||||
libtoolize
|
||||
aclocal -I scripts
|
||||
autoconf -i
|
||||
autoheader
|
||||
automake --add-missing
|
||||
./configure
|
||||
- name: Prepare compile_commands.json
|
||||
run: |
|
||||
bear -- make
|
||||
- name: Create results directory
|
||||
run: |
|
||||
mkdir clang-tidy-result
|
||||
- name: Analyze
|
||||
run: |
|
||||
git diff -U0 "$(git merge-base HEAD "upstream/${{ github.event.pull_request.base.ref }}")" | clang-tidy-diff -p1 -path build -export-fixes clang-tidy-result/fixes.yml
|
||||
- name: Save PR metadata
|
||||
run: |
|
||||
echo "${{ github.event.number }}" > clang-tidy-result/pr-id.txt
|
||||
echo "${{ github.event.pull_request.head.repo.full_name }}" > clang-tidy-result/pr-head-repo.txt
|
||||
echo "${{ github.event.pull_request.head.sha }}" > clang-tidy-result/pr-head-sha.txt
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: clang-tidy-result
|
||||
path: clang-tidy-result/
|
||||
- name: Run clang-tidy-pr-comments action
|
||||
uses: platisd/clang-tidy-pr-comments@v1
|
||||
with:
|
||||
# The GitHub token (or a personal access token)
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# The path to the clang-tidy fixes generated previously
|
||||
clang_tidy_fixes: clang-tidy-result/fixes.yml
|
||||
# Optionally set to true if you want the Action to request
|
||||
# changes in case warnings are found
|
||||
request_changes: true
|
||||
# Optionally set the number of comments per review
|
||||
# to avoid GitHub API timeouts for heavily loaded
|
||||
# pull requests
|
||||
suggestions_per_comment: 10
|
||||
|
||||
|
||||
unit-tests:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: Update Packages
|
||||
run: |
|
||||
sudo apt-get update
|
||||
- name: Fetch libtorrent
|
||||
run: |
|
||||
git clone https://github.com/rakshasa/libtorrent
|
||||
- name: Build libtorrent
|
||||
run: |
|
||||
cd libtorrent
|
||||
libtoolize
|
||||
aclocal -I scripts
|
||||
autoconf -i
|
||||
autoheader
|
||||
automake --add-missing
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
cd ..
|
||||
rm -rf libtorrent
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0
|
||||
- name: Fetch base branch
|
||||
run: |
|
||||
git remote add upstream "https://github.com/${{ github.event.pull_request.base.repo.full_name }}"
|
||||
git fetch --no-tags --no-recurse-submodules upstream "${{ github.event.pull_request.base.ref }}"
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get install -y \
|
||||
libcppunit-dev \
|
||||
libcurl4-openssl-dev
|
||||
- name: Configure Project
|
||||
run: |
|
||||
libtoolize
|
||||
aclocal -I scripts
|
||||
autoconf -i
|
||||
autoheader
|
||||
automake --add-missing
|
||||
./configure
|
||||
- name: Build Project
|
||||
run: |
|
||||
make
|
||||
- name: Run Unit Tests
|
||||
run: |
|
||||
make check
|
||||
@@ -11,7 +11,6 @@ EXTRA_DIST= \
|
||||
rak/file_stat.h \
|
||||
rak/fs_stat.h \
|
||||
rak/functional.h \
|
||||
rak/functional_fun.h \
|
||||
rak/path.h \
|
||||
rak/partial_queue.h \
|
||||
rak/priority_queue.h \
|
||||
|
||||
@@ -14,6 +14,12 @@ Stable
|
||||
* [https://github.com/rakshasa/rtorrent-archive/raw/master/libtorrent-0.13.8.tar.gz](https://github.com/rakshasa/rtorrent-archive/raw/master/libtorrent-0.13.8.tar.gz)
|
||||
* [https://github.com/rakshasa/rtorrent-archive/raw/master/rtorrent-0.9.8.tar.gz](https://github.com/rakshasa/rtorrent-archive/raw/master/rtorrent-0.9.8.tar.gz)
|
||||
|
||||
Unstable
|
||||
------
|
||||
|
||||
* [https://github.com/rakshasa/rtorrent-archive/raw/master/libtorrent-0.14.0.tar.gz](https://github.com/rakshasa/rtorrent-archive/raw/master/libtorrent-0.14.0.tar.gz)
|
||||
* [https://github.com/rakshasa/rtorrent-archive/raw/master/rtorrent-0.10.0.tar.gz](https://github.com/rakshasa/rtorrent-archive/raw/master/rtorrent-0.10.0.tar.gz)
|
||||
|
||||
Related Projects
|
||||
----------------
|
||||
|
||||
@@ -26,7 +32,7 @@ Donate to rTorrent development
|
||||
* [Patreon](https://www.patreon.com/rtorrent)
|
||||
* [SubscribeStar](https://www.subscribestar.com/rtorrent)
|
||||
* Bitcoin: 1MpmXm5AHtdBoDaLZstJw8nupJJaeKu8V8
|
||||
* Etherium: 0x9AB1e3C3d8a875e870f161b3e9287Db0E6DAfF78
|
||||
* Ethereum: 0x9AB1e3C3d8a875e870f161b3e9287Db0E6DAfF78
|
||||
* Litecoin: LdyaVR67LBnTf6mAT4QJnjSG2Zk67qxmfQ
|
||||
* Cardano: addr1qytaslmqmk6dspltw06sp0zf83dh09u79j49ceh5y26zdcccgq4ph7nmx6kgmzeldauj43254ey97f3x4xw49d86aguqwfhlte
|
||||
|
||||
|
||||
Executable → Regular
+13
-5
@@ -1,16 +1,18 @@
|
||||
m4_pattern_allow([PKG_CHECK_EXISTS])
|
||||
|
||||
AC_INIT([rtorrent], [0.10.0], [sundell.software@gmail.com])
|
||||
AC_INIT([rtorrent],[0.15.0],[sundell.software@gmail.com])
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_MACRO_DIRS([scripts])
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
AM_PROG_AR
|
||||
|
||||
AC_DEFINE([API_VERSION], [10], [api version])
|
||||
|
||||
AC_PROG_RANLIB
|
||||
AC_PROG_CXX
|
||||
AC_SYS_LARGEFILE
|
||||
LT_INIT
|
||||
|
||||
AX_CXX_COMPILE_STDCXX([14], [noext], [mandatory])
|
||||
|
||||
@@ -49,12 +51,18 @@ fi
|
||||
|
||||
PKG_CHECK_MODULES([LIBCURL], [libcurl],, [LIBCURL_CHECK_CONFIG])
|
||||
PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
|
||||
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.14.0])
|
||||
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.15.0])
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
TORRENT_WITH_XMLRPC_C
|
||||
AC_LANG_POP(C++)
|
||||
|
||||
TORRENT_WITH_TINYXML2
|
||||
|
||||
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(HAVE_CONFIG_H, 1, true if config.h was included)
|
||||
AC_DEFINE(USER_AGENT, [std::string(PACKAGE "/" VERSION "/") + torrent::version()], Http user agent)
|
||||
|
||||
@@ -69,9 +77,9 @@ CC_ATTRIBUTE_UNUSED(
|
||||
|
||||
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 $CPPUNIT_LIBS $LIBCURL $LIBCURL_LIBS $DEPENDENCIES_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $CPPUNIT_CFLAGS $LIBCURL_CPPFLAGS $LIBCURL_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $CPPUNIT_CFLAGS $LIBCURL_CPPFLAGS $LIBCURL_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
|
||||
LIBS="$PTHREAD_LIBS $CURSES_LIB $CURSES_LIBS $LIBCURL $LIBCURL_LIBS $DEPENDENCIES_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $LIBCURL_CPPFLAGS $LIBCURL_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $LIBCURL_CPPFLAGS $LIBCURL_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS"
|
||||
|
||||
AC_CONFIG_FILES([
|
||||
Makefile
|
||||
|
||||
@@ -1,655 +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 <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
// This file contains functors that wrap function pointers and member
|
||||
// function pointers.
|
||||
//
|
||||
// 'fn' functors are polymorphic and derives from 'rak::function' and
|
||||
// thus is less strict about types, this adds the cost of calling a
|
||||
// virtual function.
|
||||
//
|
||||
// 'fun' functors are non-polymorphic and thus cheaper, but requires
|
||||
// the target object's type in the functor's template arguments.
|
||||
//
|
||||
// This should be replaced with TR1 stuff when it becomes widely
|
||||
// available. At the moment it behaves like std::auto_ptr, so be
|
||||
// careful when copying.
|
||||
|
||||
#ifndef RAK_FUNCTIONAL_FUN_H
|
||||
#define RAK_FUNCTIONAL_FUN_H
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace rak {
|
||||
|
||||
template <typename Result>
|
||||
class function_base0 {
|
||||
public:
|
||||
virtual ~function_base0() {}
|
||||
|
||||
virtual Result operator () () = 0;
|
||||
};
|
||||
|
||||
template <typename Result, typename Arg1>
|
||||
class function_base1 : public std::unary_function<Arg1, Result> {
|
||||
public:
|
||||
virtual ~function_base1() {}
|
||||
|
||||
virtual Result operator () (Arg1 arg1) = 0;
|
||||
};
|
||||
|
||||
template <typename Result, typename Arg1, typename Arg2>
|
||||
class function_base2 : public std::binary_function<Arg1, Arg2, Result> {
|
||||
public:
|
||||
virtual ~function_base2() {}
|
||||
|
||||
virtual Result operator () (Arg1 arg1, Arg2 arg2) = 0;
|
||||
};
|
||||
|
||||
template <typename Result, typename Arg1, typename Arg2, typename Arg3>
|
||||
class function_base3 {
|
||||
public:
|
||||
virtual ~function_base3() {}
|
||||
|
||||
virtual Result operator () (Arg1 arg1, Arg2 arg2, Arg3 arg3) = 0;
|
||||
};
|
||||
|
||||
template <typename Result>
|
||||
class function0 {
|
||||
public:
|
||||
typedef Result result_type;
|
||||
typedef function_base0<Result> base_type;
|
||||
|
||||
bool is_valid() const { return m_base.get() != NULL; }
|
||||
|
||||
void set(base_type* base) { m_base = std::shared_ptr<base_type>(base); }
|
||||
base_type* release() { return m_base.release(); }
|
||||
|
||||
Result operator () () { return (*m_base)(); }
|
||||
|
||||
private:
|
||||
std::shared_ptr<base_type> m_base;
|
||||
};
|
||||
|
||||
template <typename Result, typename Arg1>
|
||||
class function1 {
|
||||
public:
|
||||
typedef Result result_type;
|
||||
typedef function_base1<Result, Arg1> base_type;
|
||||
|
||||
bool is_valid() const { return m_base.get() != NULL; }
|
||||
|
||||
void set(base_type* base) { m_base = std::shared_ptr<base_type>(base); }
|
||||
base_type* release() { return m_base.release(); }
|
||||
|
||||
Result operator () (Arg1 arg1) { return (*m_base)(arg1); }
|
||||
|
||||
private:
|
||||
std::shared_ptr<base_type> m_base;
|
||||
};
|
||||
|
||||
template <typename Result, typename Arg1, typename Arg2>
|
||||
class function2 {
|
||||
public:
|
||||
typedef Result result_type;
|
||||
typedef function_base2<Result, Arg1, Arg2> base_type;
|
||||
|
||||
bool is_valid() const { return m_base.get() != NULL; }
|
||||
|
||||
void set(base_type* base) { m_base = std::shared_ptr<base_type>(base); }
|
||||
base_type* release() { return m_base.release(); }
|
||||
|
||||
Result operator () (Arg1 arg1, Arg2 arg2) { return (*m_base)(arg1, arg2); }
|
||||
|
||||
private:
|
||||
std::shared_ptr<base_type> m_base;
|
||||
};
|
||||
|
||||
template <typename Result, typename Arg2>
|
||||
class function2<Result, void, Arg2> {
|
||||
public:
|
||||
typedef Result result_type;
|
||||
typedef function_base1<Result, Arg2> base_type;
|
||||
|
||||
bool is_valid() const { return m_base.get() != NULL; }
|
||||
|
||||
void set(base_type* base) { m_base = std::shared_ptr<base_type>(base); }
|
||||
base_type* release() { return m_base.release(); }
|
||||
|
||||
Result operator () (Arg2 arg2) { return (*m_base)(arg2); }
|
||||
|
||||
template <typename Discard>
|
||||
Result operator () (Discard discard, Arg2 arg2) { return (*m_base)(arg2); }
|
||||
|
||||
private:
|
||||
std::shared_ptr<base_type> m_base;
|
||||
};
|
||||
|
||||
template <typename Result, typename Arg1, typename Arg2, typename Arg3>
|
||||
class function3 {
|
||||
public:
|
||||
typedef Result result_type;
|
||||
typedef function_base3<Result, Arg1, Arg2, Arg3> base_type;
|
||||
|
||||
bool is_valid() const { return m_base.get() != NULL; }
|
||||
|
||||
void set(base_type* base) { m_base = std::shared_ptr<base_type>(base); }
|
||||
base_type* release() { return m_base.release(); }
|
||||
|
||||
Result operator () (Arg1 arg1, Arg2 arg2, Arg3 arg3) { return (*m_base)(arg1, arg2, arg3); }
|
||||
|
||||
private:
|
||||
std::shared_ptr<base_type> m_base;
|
||||
};
|
||||
|
||||
template <typename Result>
|
||||
class ptr_fn0_t : public function_base0<Result> {
|
||||
public:
|
||||
typedef Result (*Func)();
|
||||
|
||||
ptr_fn0_t(Func func) : m_func(func) {}
|
||||
virtual ~ptr_fn0_t() {}
|
||||
|
||||
virtual Result operator () () { return m_func(); }
|
||||
|
||||
private:
|
||||
Func m_func;
|
||||
};
|
||||
|
||||
template <typename Result, typename Arg1>
|
||||
class ptr_fn1_t : public function_base1<Result, Arg1> {
|
||||
public:
|
||||
typedef Result (*Func)(Arg1);
|
||||
|
||||
ptr_fn1_t(Func func) : m_func(func) {}
|
||||
virtual ~ptr_fn1_t() {}
|
||||
|
||||
virtual Result operator () (Arg1 arg1) { return m_func(arg1); }
|
||||
|
||||
private:
|
||||
Func m_func;
|
||||
};
|
||||
|
||||
template <typename Result, typename Arg1, typename Arg2>
|
||||
class ptr_fn2_t : public function_base2<Result, Arg1, Arg2> {
|
||||
public:
|
||||
typedef Result (*Func)(Arg1, Arg2);
|
||||
|
||||
ptr_fn2_t(Func func) : m_func(func) {}
|
||||
virtual ~ptr_fn2_t() {}
|
||||
|
||||
virtual Result operator () (Arg1 arg1, Arg2 arg2) { return m_func(arg1, arg2); }
|
||||
|
||||
private:
|
||||
Func m_func;
|
||||
};
|
||||
|
||||
template <typename Object, typename Result>
|
||||
class mem_fn0_t : public function_base0<Result> {
|
||||
public:
|
||||
typedef Result (Object::*Func)();
|
||||
|
||||
mem_fn0_t(Object* object, Func func) : m_object(object), m_func(func) {}
|
||||
virtual ~mem_fn0_t() {}
|
||||
|
||||
virtual Result operator () () { return (m_object->*m_func)(); }
|
||||
|
||||
private:
|
||||
Object* m_object;
|
||||
Func m_func;
|
||||
};
|
||||
|
||||
template <typename Object, typename Result, typename Arg1>
|
||||
class mem_fn1_t : public function_base1<Result, Arg1> {
|
||||
public:
|
||||
typedef Result (Object::*Func)(Arg1);
|
||||
|
||||
mem_fn1_t(Object* object, Func func) : m_object(object), m_func(func) {}
|
||||
virtual ~mem_fn1_t() {}
|
||||
|
||||
virtual Result operator () (Arg1 arg1) { return (m_object->*m_func)(arg1); }
|
||||
|
||||
private:
|
||||
Object* m_object;
|
||||
Func m_func;
|
||||
};
|
||||
|
||||
template <typename Object, typename Result, typename Arg1, typename Arg2, typename Arg3>
|
||||
class mem_fn3_t : public function_base3<Result, Arg1, Arg2, Arg3> {
|
||||
public:
|
||||
typedef Result (Object::*Func)(Arg1, Arg2, Arg3);
|
||||
|
||||
mem_fn3_t(Object* object, Func func) : m_object(object), m_func(func) {}
|
||||
virtual ~mem_fn3_t() {}
|
||||
|
||||
virtual Result operator () (Arg1 arg1, Arg2 arg2, Arg3 arg3) { return (m_object->*m_func)(arg1, arg2, arg3); }
|
||||
|
||||
private:
|
||||
Object* m_object;
|
||||
Func m_func;
|
||||
};
|
||||
|
||||
template <typename Object, typename Result, typename Arg1, typename Arg2>
|
||||
class mem_fn2_t : public function_base2<Result, Arg1, Arg2> {
|
||||
public:
|
||||
typedef Result (Object::*Func)(Arg1, Arg2);
|
||||
|
||||
mem_fn2_t(Object* object, Func func) : m_object(object), m_func(func) {}
|
||||
virtual ~mem_fn2_t() {}
|
||||
|
||||
virtual Result operator () (Arg1 arg1, Arg2 arg2) { return (m_object->*m_func)(arg1, arg2); }
|
||||
|
||||
private:
|
||||
Object* m_object;
|
||||
Func m_func;
|
||||
};
|
||||
|
||||
template <typename Object, typename Result>
|
||||
class const_mem_fn0_t : public function_base0<Result> {
|
||||
public:
|
||||
typedef Result (Object::*Func)() const;
|
||||
|
||||
const_mem_fn0_t(const Object* object, Func func) : m_object(object), m_func(func) {}
|
||||
virtual ~const_mem_fn0_t() {}
|
||||
|
||||
virtual Result operator () () { return (m_object->*m_func)(); }
|
||||
|
||||
private:
|
||||
const Object* m_object;
|
||||
Func m_func;
|
||||
};
|
||||
|
||||
template <typename Object, typename Result, typename Arg1>
|
||||
class const_mem_fn1_t : public function_base1<Result, Arg1> {
|
||||
public:
|
||||
typedef Result (Object::*Func)(Arg1) const;
|
||||
|
||||
const_mem_fn1_t(const Object* object, Func func) : m_object(object), m_func(func) {}
|
||||
virtual ~const_mem_fn1_t() {}
|
||||
|
||||
virtual Result operator () (Arg1 arg1) { return (m_object->*m_func)(arg1); }
|
||||
|
||||
private:
|
||||
const Object* m_object;
|
||||
Func m_func;
|
||||
};
|
||||
|
||||
// Unary functor with a bound argument.
|
||||
template <typename Object, typename Result, typename Arg1>
|
||||
class mem_fn0_b1_t : public function_base0<Result> {
|
||||
public:
|
||||
typedef Result (Object::*Func)(Arg1);
|
||||
|
||||
mem_fn0_b1_t(Object* object, Func func, const Arg1 arg1) : m_object(object), m_func(func), m_arg1(arg1) {}
|
||||
virtual ~mem_fn0_b1_t() {}
|
||||
|
||||
virtual Result operator () () { return (m_object->*m_func)(m_arg1); }
|
||||
|
||||
private:
|
||||
Object* m_object;
|
||||
Func m_func;
|
||||
const Arg1 m_arg1;
|
||||
};
|
||||
|
||||
template <typename Object, typename Result, typename Arg1, typename Arg2>
|
||||
class mem_fn1_b1_t : public function_base1<Result, Arg2> {
|
||||
public:
|
||||
typedef Result (Object::*Func)(Arg1, Arg2);
|
||||
|
||||
mem_fn1_b1_t(Object* object, Func func, const Arg1 arg1) : m_object(object), m_func(func), m_arg1(arg1) {}
|
||||
virtual ~mem_fn1_b1_t() {}
|
||||
|
||||
virtual Result operator () (const Arg2 arg2) { return (m_object->*m_func)(m_arg1, arg2); }
|
||||
|
||||
private:
|
||||
Object* m_object;
|
||||
Func m_func;
|
||||
const Arg1 m_arg1;
|
||||
};
|
||||
|
||||
template <typename Object, typename Result, typename Arg1, typename Arg2>
|
||||
class mem_fn1_b2_t : public function_base1<Result, Arg1> {
|
||||
public:
|
||||
typedef Result (Object::*Func)(Arg1, Arg2);
|
||||
|
||||
mem_fn1_b2_t(Object* object, Func func, const Arg2 arg2) : m_object(object), m_func(func), m_arg2(arg2) {}
|
||||
virtual ~mem_fn1_b2_t() {}
|
||||
|
||||
virtual Result operator () (const Arg1 arg1) { return (m_object->*m_func)(arg1, m_arg2); }
|
||||
|
||||
private:
|
||||
Object* m_object;
|
||||
Func m_func;
|
||||
const Arg2 m_arg2;
|
||||
};
|
||||
|
||||
template <typename Result, typename Arg1>
|
||||
class ptr_fn0_b1_t : public function_base0<Result> {
|
||||
public:
|
||||
typedef Result (*Func)(Arg1);
|
||||
|
||||
ptr_fn0_b1_t(Func func, const Arg1 arg1) : m_func(func), m_arg1(arg1) {}
|
||||
virtual ~ptr_fn0_b1_t() {}
|
||||
|
||||
virtual Result operator () () { return m_func(m_arg1); }
|
||||
|
||||
private:
|
||||
Func m_func;
|
||||
Arg1 m_arg1;
|
||||
};
|
||||
|
||||
template <typename Result, typename Arg1, typename Arg2>
|
||||
class ptr_fn1_b1_t : public function_base1<Result, Arg2> {
|
||||
public:
|
||||
typedef Result (*Func)(Arg1, Arg2);
|
||||
|
||||
ptr_fn1_b1_t(Func func, const Arg1 arg1) : m_func(func), m_arg1(arg1) {}
|
||||
virtual ~ptr_fn1_b1_t() {}
|
||||
|
||||
virtual Result operator () (Arg2 arg2) { return m_func(m_arg1, arg2); }
|
||||
|
||||
private:
|
||||
Func m_func;
|
||||
Arg1 m_arg1;
|
||||
};
|
||||
|
||||
template <typename Result, typename Arg1, typename Arg2, typename Arg3>
|
||||
class ptr_fn2_b1_t : public function_base2<Result, Arg2, Arg3> {
|
||||
public:
|
||||
typedef Result (*Func)(Arg1, Arg2, Arg3);
|
||||
|
||||
ptr_fn2_b1_t(Func func, const Arg1 arg1) : m_func(func), m_arg1(arg1) {}
|
||||
virtual ~ptr_fn2_b1_t() {}
|
||||
|
||||
virtual Result operator () (Arg2 arg2, Arg3 arg3) { return m_func(m_arg1, arg2, arg3); }
|
||||
|
||||
private:
|
||||
Func m_func;
|
||||
Arg1 m_arg1;
|
||||
};
|
||||
|
||||
template <typename Ftor>
|
||||
class ftor_fn1_t : public function_base1<typename Ftor::result_type, typename Ftor::argument_type> {
|
||||
public:
|
||||
typedef typename Ftor::result_type result_type;
|
||||
typedef typename Ftor::argument_type argument_type;
|
||||
|
||||
ftor_fn1_t(Ftor ftor) : m_ftor(ftor) {}
|
||||
virtual ~ftor_fn1_t() {}
|
||||
|
||||
virtual result_type operator () (argument_type arg1) { return m_ftor(arg1); }
|
||||
|
||||
private:
|
||||
Ftor m_ftor;
|
||||
};
|
||||
|
||||
template <typename Ftor>
|
||||
class ftor_fn2_t : public function_base2<typename Ftor::result_type, typename Ftor::first_argument_type, typename Ftor::second_argument_type> {
|
||||
public:
|
||||
typedef typename Ftor::result_type result_type;
|
||||
typedef typename Ftor::first_argument_type first_argument_type;
|
||||
typedef typename Ftor::second_argument_type second_argument_type;
|
||||
|
||||
ftor_fn2_t(Ftor ftor) : m_ftor(ftor) {}
|
||||
virtual ~ftor_fn2_t() {}
|
||||
|
||||
virtual result_type operator () (first_argument_type arg1, second_argument_type arg2) { return m_ftor(arg1, arg2); }
|
||||
|
||||
private:
|
||||
Ftor m_ftor;
|
||||
};
|
||||
|
||||
template <typename Result>
|
||||
class value_fn0_t : public function_base0<Result> {
|
||||
public:
|
||||
value_fn0_t(const Result& val) : m_value(val) {}
|
||||
|
||||
virtual Result operator () () { return m_value; }
|
||||
|
||||
private:
|
||||
Result m_value;
|
||||
};
|
||||
|
||||
template <typename Result, typename SrcResult>
|
||||
class convert_fn0_t : public function_base0<Result> {
|
||||
public:
|
||||
typedef function0<SrcResult> src_type;
|
||||
|
||||
convert_fn0_t(typename src_type::base_type* object) { m_object.set(object); }
|
||||
virtual ~convert_fn0_t() {}
|
||||
|
||||
virtual Result operator () () {
|
||||
return m_object();
|
||||
}
|
||||
|
||||
private:
|
||||
src_type m_object;
|
||||
};
|
||||
|
||||
template <typename Result, typename Arg1, typename SrcResult, typename SrcArg1>
|
||||
class convert_fn1_t : public function_base1<Result, Arg1> {
|
||||
public:
|
||||
typedef function1<SrcResult, SrcArg1> src_type;
|
||||
|
||||
convert_fn1_t(typename src_type::base_type* object) { m_object.set(object); }
|
||||
virtual ~convert_fn1_t() {}
|
||||
|
||||
virtual Result operator () (Arg1 arg1) {
|
||||
return m_object(arg1);
|
||||
}
|
||||
|
||||
private:
|
||||
src_type m_object;
|
||||
};
|
||||
|
||||
template <typename Result, typename Arg1, typename Arg2, typename SrcResult, typename SrcArg1, typename SrcArg2>
|
||||
class convert_fn2_t : public function_base2<Result, Arg1, Arg2> {
|
||||
public:
|
||||
typedef function2<SrcResult, SrcArg1, SrcArg2> src_type;
|
||||
|
||||
convert_fn2_t(typename src_type::base_type* object) { m_object.set(object); }
|
||||
virtual ~convert_fn2_t() {}
|
||||
|
||||
virtual Result operator () (Arg1 arg1, Arg2 arg2) {
|
||||
return m_object(arg1, arg2);
|
||||
}
|
||||
|
||||
private:
|
||||
src_type m_object;
|
||||
};
|
||||
|
||||
template <typename Result>
|
||||
inline function_base0<Result>*
|
||||
ptr_fn(Result (*func)()) {
|
||||
return new ptr_fn0_t<Result>(func);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Result>
|
||||
inline function_base1<Result, Arg1>*
|
||||
ptr_fn(Result (*func)(Arg1)) {
|
||||
return new ptr_fn1_t<Result, Arg1>(func);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2, typename Result>
|
||||
inline function_base2<Result, Arg1, Arg2>*
|
||||
ptr_fn(Result (*func)(Arg1, Arg2)) {
|
||||
return new ptr_fn2_t<Result, Arg1, Arg2>(func);
|
||||
}
|
||||
|
||||
template <typename Result, typename Object>
|
||||
inline function_base0<Result>*
|
||||
mem_fn(Object* object, Result (Object::*func)()) {
|
||||
return new mem_fn0_t<Object, Result>(object, func);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Result, typename Object>
|
||||
inline function_base1<Result, Arg1>*
|
||||
mem_fn(Object* object, Result (Object::*func)(Arg1)) {
|
||||
return new mem_fn1_t<Object, Result, Arg1>(object, func);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2, typename Result, typename Object>
|
||||
inline function_base2<Result, Arg1, Arg2>*
|
||||
mem_fn(Object* object, Result (Object::*func)(Arg1, Arg2)) {
|
||||
return new mem_fn2_t<Object, Result, Arg1, Arg2>(object, func);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2, typename Arg3, typename Result, typename Object>
|
||||
inline function_base3<Result, Arg1, Arg2, Arg3>*
|
||||
mem_fn(Object* object, Result (Object::*func)(Arg1, Arg2, Arg3)) {
|
||||
return new mem_fn3_t<Object, Result, Arg1, Arg2, Arg3>(object, func);
|
||||
}
|
||||
|
||||
template <typename Result, typename Object>
|
||||
inline function_base0<Result>*
|
||||
mem_fn(const Object* object, Result (Object::*func)() const) {
|
||||
return new const_mem_fn0_t<Object, Result>(object, func);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Result, typename Object>
|
||||
inline function_base1<Result, Arg1>*
|
||||
mem_fn(const Object* object, Result (Object::*func)(Arg1) const) {
|
||||
return new const_mem_fn1_t<Object, Result, Arg1>(object, func);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Result, typename Object>
|
||||
inline function_base0<Result>*
|
||||
bind_mem_fn(Object* object, Result (Object::*func)(Arg1), const Arg1 arg1) {
|
||||
return new mem_fn0_b1_t<Object, Result, Arg1>(object, func, arg1);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2, typename Result, typename Object>
|
||||
inline function_base1<Result, Arg2>*
|
||||
bind_mem_fn(Object* object, Result (Object::*func)(Arg1, Arg2), const Arg1 arg1) {
|
||||
return new mem_fn1_b1_t<Object, Result, Arg1, Arg2>(object, func, arg1);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2, typename Result, typename Object>
|
||||
inline function_base1<Result, Arg1>*
|
||||
bind2_mem_fn(Object* object, Result (Object::*func)(Arg1, Arg2), const Arg2 arg2) {
|
||||
return new mem_fn1_b2_t<Object, Result, Arg1, Arg2>(object, func, arg2);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Result>
|
||||
inline function_base0<Result>*
|
||||
bind_ptr_fn(Result (*func)(Arg1), const Arg1 arg1) {
|
||||
return new ptr_fn0_b1_t<Result, Arg1>(func, arg1);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2, typename Result>
|
||||
inline function_base1<Result, Arg2>*
|
||||
bind_ptr_fn(Result (*func)(Arg1, Arg2), const Arg1 arg1) {
|
||||
return new ptr_fn1_b1_t<Result, Arg1, Arg2>(func, arg1);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2, typename Arg3, typename Result>
|
||||
inline function_base2<Result, Arg2, Arg3>*
|
||||
bind_ptr_fn(Result (*func)(Arg1, Arg2, Arg3), const Arg1 arg1) {
|
||||
return new ptr_fn2_b1_t<Result, Arg1, Arg2, Arg3>(func, arg1);
|
||||
}
|
||||
|
||||
template <typename Ftor>
|
||||
inline function_base1<typename Ftor::result_type, typename Ftor::argument_type>*
|
||||
ftor_fn1(Ftor ftor) {
|
||||
return new ftor_fn1_t<Ftor>(ftor);
|
||||
}
|
||||
|
||||
template <typename Ftor>
|
||||
inline function_base2<typename Ftor::result_type, typename Ftor::first_argument_type, typename Ftor::second_argument_type>*
|
||||
ftor_fn2(Ftor ftor) {
|
||||
return new ftor_fn2_t<Ftor>(ftor);
|
||||
}
|
||||
|
||||
template <typename Result>
|
||||
inline function_base0<Result>*
|
||||
value_fn(const Result& val) {
|
||||
return new value_fn0_t<Result>(val);
|
||||
}
|
||||
|
||||
template <typename A, typename B>
|
||||
struct equal_types_t {
|
||||
typedef A first_type;
|
||||
typedef B second_type;
|
||||
|
||||
const static int result = 0;
|
||||
};
|
||||
|
||||
template <typename A>
|
||||
struct equal_types_t<A, A> {
|
||||
typedef A first_type;
|
||||
typedef A second_type;
|
||||
|
||||
const static int result = 1;
|
||||
};
|
||||
|
||||
template <typename Result, typename SrcResult>
|
||||
inline function_base0<Result>*
|
||||
convert_fn(function_base0<SrcResult>* src) {
|
||||
if (equal_types_t<function_base0<Result>, function_base0<SrcResult> >::result)
|
||||
// The pointer cast never gets done if the types are different,
|
||||
// but needs to be here to pleasant the compiler.
|
||||
return reinterpret_cast<typename equal_types_t<function_base0<Result>, function_base0<SrcResult> >::first_type*>(src);
|
||||
else
|
||||
return new convert_fn0_t<Result, SrcResult>(src);
|
||||
}
|
||||
|
||||
template <typename Result, typename Arg1, typename SrcResult, typename SrcArg1>
|
||||
inline function_base1<Result, Arg1>*
|
||||
convert_fn(function_base1<SrcResult, SrcArg1>* src) {
|
||||
if (equal_types_t<function_base1<Result, Arg1>, function_base1<SrcResult, SrcArg1> >::result)
|
||||
// The pointer cast never gets done if the types are different,
|
||||
// but needs to be here to pleasant the compiler.
|
||||
return reinterpret_cast<typename equal_types_t<function_base1<Result, Arg1>, function_base1<SrcResult, SrcArg1> >::first_type*>(src);
|
||||
else
|
||||
return new convert_fn1_t<Result, Arg1, SrcResult, SrcArg1>(src);
|
||||
}
|
||||
|
||||
template <typename Result, typename Arg1, typename Arg2, typename SrcResult, typename SrcArg1, typename SrcArg2>
|
||||
inline function_base2<Result, Arg1, Arg2>*
|
||||
convert_fn(function_base2<SrcResult, SrcArg1, SrcArg2>* src) {
|
||||
if (equal_types_t<function_base2<Result, Arg1, Arg2>, function_base2<SrcResult, SrcArg1, SrcArg2> >::result)
|
||||
// The pointer cast never gets done if the types are different,
|
||||
// but needs to be here to pleasant the compiler.
|
||||
return reinterpret_cast<typename equal_types_t<function_base2<Result, Arg1, Arg2>, function_base2<SrcResult, SrcArg1, SrcArg2> >::first_type*>(src);
|
||||
else
|
||||
return new convert_fn2_t<Result, Arg1, Arg2, SrcResult, SrcArg1, SrcArg2>(src);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
+3
-2
@@ -37,6 +37,7 @@
|
||||
#ifndef RAK_PARTIAL_QUEUE_H
|
||||
#define RAK_PARTIAL_QUEUE_H
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <cinttypes>
|
||||
@@ -107,7 +108,7 @@ private:
|
||||
size_type m_index;
|
||||
size_type m_ceiling;
|
||||
|
||||
size_pair_type m_layers[num_layers];
|
||||
std::array<size_pair_type, num_layers> m_layers;
|
||||
};
|
||||
|
||||
inline void
|
||||
@@ -137,7 +138,7 @@ partial_queue::clear() {
|
||||
m_index = 0;
|
||||
m_ceiling = ceiling(num_layers - 1);
|
||||
|
||||
std::memset(m_layers, 0, num_layers * sizeof(size_pair_type));
|
||||
m_layers = {};
|
||||
}
|
||||
|
||||
inline bool
|
||||
|
||||
+6
-72
@@ -1,39 +1,3 @@
|
||||
// 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 <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
// priority_queue is a priority queue implemented using a binary
|
||||
// heap. It can contain multiple instances of a value.
|
||||
|
||||
@@ -74,6 +38,10 @@ public:
|
||||
base_type::pop_back();
|
||||
}
|
||||
|
||||
void update() {
|
||||
std::make_heap(begin(), end(), m_compare);
|
||||
}
|
||||
|
||||
void push(const value_type& value) {
|
||||
base_type::push_back(value);
|
||||
std::push_heap(begin(), end(), m_compare);
|
||||
@@ -81,7 +49,7 @@ public:
|
||||
|
||||
template <typename Key>
|
||||
iterator find(const Key& key) {
|
||||
return std::find_if(begin(), end(), std::bind2nd(m_equal, key));
|
||||
return std::find_if(begin(), end(), [&](auto& value) { return m_equal(value, key); });
|
||||
}
|
||||
|
||||
template <typename Key>
|
||||
@@ -95,12 +63,8 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
// Removes 'itr' from the queue. This assumes 'itr' has been
|
||||
// modified such that it has a higher priority than any other
|
||||
// element in the queue.
|
||||
// Removes 'itr' from the queue.
|
||||
void erase(iterator itr) {
|
||||
// std::push_heap(begin(), ++itr, m_compare);
|
||||
// pop();
|
||||
base_type::erase(itr);
|
||||
std::make_heap(begin(), end(), m_compare);
|
||||
}
|
||||
@@ -110,36 +74,6 @@ private:
|
||||
Equal m_equal;
|
||||
};
|
||||
|
||||
// Iterate while the top node has higher priority, as 'Compare'
|
||||
// returns false.
|
||||
template <typename Queue, typename Compare>
|
||||
class queue_pop_iterator
|
||||
: public std::iterator<std::forward_iterator_tag, void, void, void, void> {
|
||||
public:
|
||||
typedef Queue container_type;
|
||||
|
||||
queue_pop_iterator() : m_queue(NULL) {}
|
||||
queue_pop_iterator(Queue* q, Compare c) : m_queue(q), m_compare(c) {}
|
||||
|
||||
queue_pop_iterator& operator ++ () { m_queue->pop(); return *this; }
|
||||
queue_pop_iterator& operator ++ (int) { m_queue->pop(); return *this; }
|
||||
|
||||
typename container_type::const_reference operator * () { return m_queue->top(); }
|
||||
|
||||
bool operator != (const queue_pop_iterator& itr) { return !m_queue->empty() && !m_compare(m_queue->top()); }
|
||||
bool operator == (const queue_pop_iterator& itr) { return m_queue->empty() || m_compare(m_queue->top()); }
|
||||
|
||||
private:
|
||||
Queue* m_queue;
|
||||
Compare m_compare;
|
||||
};
|
||||
|
||||
template <typename Queue, typename Compare>
|
||||
inline queue_pop_iterator<Queue, Compare>
|
||||
queue_popper(Queue& queue, Compare comp) {
|
||||
return queue_pop_iterator<Queue, Compare>(&queue, comp);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -127,14 +127,32 @@ priority_queue_erase(priority_queue_default* queue, priority_item* item) {
|
||||
if (!item->is_valid())
|
||||
throw torrent::internal_error("priority_queue_erase(...) called on an invalid item.");
|
||||
|
||||
// Clear time before erasing to force it to the top.
|
||||
// Unqueue it before erasing.
|
||||
item->clear_time();
|
||||
|
||||
|
||||
if (!queue->erase(item))
|
||||
throw torrent::internal_error("priority_queue_erase(...) could not find item in queue.");
|
||||
}
|
||||
|
||||
if (queue->find(item) != queue->end())
|
||||
throw torrent::internal_error("priority_queue_erase(...) item still in queue.");
|
||||
inline void
|
||||
priority_queue_update(priority_queue_default* queue, priority_item* item, timer t) {
|
||||
if (t == timer())
|
||||
throw torrent::internal_error("priority_queue_update(...) received a bad timer.");
|
||||
|
||||
if (!item->is_valid())
|
||||
throw torrent::internal_error("priority_queue_update(...) called on an invalid item.");
|
||||
|
||||
if (queue->find(item) == queue->end()) {
|
||||
if (item->is_queued())
|
||||
throw torrent::internal_error("priority_queue_update(...) cannot insert an already queued item.");
|
||||
|
||||
item->set_time(t);
|
||||
queue->push(item);
|
||||
|
||||
} else {
|
||||
item->set_time(t);
|
||||
queue->update();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Executable → Regular
Executable → Regular
Executable → Regular
@@ -0,0 +1,37 @@
|
||||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_require_defined.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_REQUIRE_DEFINED(MACRO)
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# AX_REQUIRE_DEFINED is a simple helper for making sure other macros have
|
||||
# been defined and thus are available for use. This avoids random issues
|
||||
# where a macro isn't expanded. Instead the configure script emits a
|
||||
# non-fatal:
|
||||
#
|
||||
# ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found
|
||||
#
|
||||
# It's like AC_REQUIRE except it doesn't expand the required macro.
|
||||
#
|
||||
# Here's an example:
|
||||
#
|
||||
# AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2014 Mike Frysinger <vapier@gentoo.org>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 2
|
||||
|
||||
AC_DEFUN([AX_REQUIRE_DEFINED], [dnl
|
||||
m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])])
|
||||
])dnl AX_REQUIRE_DEFINED
|
||||
Executable → Regular
@@ -426,6 +426,18 @@ AC_DEFUN([TORRENT_WITH_XMLRPC_C], [
|
||||
])
|
||||
])
|
||||
|
||||
AC_DEFUN([TORRENT_WITH_TINYXML2], [
|
||||
AC_MSG_CHECKING(for tinyxml2)
|
||||
|
||||
AC_ARG_WITH(xmlrpc-tinyxml2,
|
||||
AS_HELP_STRING([--with-xmlrpc-tinyxml2],[enable XMLRPC support via tinyxml2]),
|
||||
[
|
||||
AC_MSG_RESULT(yes)
|
||||
AC_DEFINE(HAVE_XMLRPC_TINYXML2, 1, Support for XMLRPC via tinyxml2.)
|
||||
],[
|
||||
AC_MSG_RESULT(ignored)
|
||||
])
|
||||
])
|
||||
|
||||
AC_DEFUN([TORRENT_WITH_INOTIFY], [
|
||||
AC_LANG_PUSH(C++)
|
||||
|
||||
@@ -121,6 +121,10 @@ libsub_root_a_SOURCES = \
|
||||
rpc/scgi_task.h \
|
||||
rpc/xmlrpc.h \
|
||||
rpc/xmlrpc.cc \
|
||||
rpc/xmlrpc_c.cc \
|
||||
rpc/xmlrpc_tinyxml2.cc \
|
||||
rpc/tinyxml2/tinyxml2.h \
|
||||
rpc/tinyxml2/tinyxml2.cc \
|
||||
\
|
||||
ui/download.cc \
|
||||
ui/download.h \
|
||||
@@ -190,4 +194,5 @@ libsub_root_a_SOURCES = \
|
||||
thread_worker.cc \
|
||||
thread_worker.h
|
||||
|
||||
|
||||
AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir)
|
||||
|
||||
@@ -593,8 +593,7 @@ d_list_push_back_unique(core::Download* download, const torrent::Object& rawArgs
|
||||
const torrent::Object& args = (rawArgs.is_list() && !rawArgs.as_list().empty()) ? rawArgs.as_list().front() : rawArgs;
|
||||
torrent::Object::list_type& list = download_get_variable(download, first_key, second_key).as_list();
|
||||
|
||||
if (std::find_if(list.begin(), list.end(),
|
||||
rak::bind1st(std::ptr_fun(&torrent::object_equal), args)) == list.end())
|
||||
if (std::find_if(list.begin(), list.end(), [args](const torrent::Object& obj) { return torrent::object_equal(obj, args); }) == list.end())
|
||||
list.push_back(rawArgs);
|
||||
|
||||
return torrent::Object();
|
||||
@@ -605,8 +604,7 @@ d_list_has(core::Download* download, const torrent::Object& rawArgs, const char*
|
||||
const torrent::Object& args = (rawArgs.is_list() && !rawArgs.as_list().empty()) ? rawArgs.as_list().front() : rawArgs;
|
||||
torrent::Object::list_type& list = download_get_variable(download, first_key, second_key).as_list();
|
||||
|
||||
return (int64_t)(std::find_if(list.begin(), list.end(),
|
||||
rak::bind1st(std::ptr_fun(&torrent::object_equal), args)) != list.end());
|
||||
return (int64_t)(std::find_if(list.begin(), list.end(), [args](const torrent::Object& obj) { return torrent::object_equal(obj, args); }) != list.end());
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
@@ -614,7 +612,7 @@ d_list_remove(core::Download* download, const torrent::Object& rawArgs, const ch
|
||||
const torrent::Object& args = (rawArgs.is_list() && !rawArgs.as_list().empty()) ? rawArgs.as_list().front() : rawArgs;
|
||||
torrent::Object::list_type& list = download_get_variable(download, first_key, second_key).as_list();
|
||||
|
||||
list.erase(std::remove_if(list.begin(), list.end(), rak::bind1st(std::ptr_fun(&torrent::object_equal), args)), list.end());
|
||||
list.erase(std::remove_if(list.begin(), list.end(), [args](const torrent::Object& obj) { return torrent::object_equal(obj, args); }), list.end());
|
||||
|
||||
return torrent::Object();
|
||||
}
|
||||
@@ -905,7 +903,7 @@ initialize_command_download() {
|
||||
// CG_GROUP_INDEX()));
|
||||
|
||||
CMD2_DL ("d.group", std::bind(&cg_d_group, std::placeholders::_1));
|
||||
CMD2_DL ("d.group.name", std::bind(&cg_d_group, std::placeholders::_1));
|
||||
CMD2_DL ("d.group.name", std::bind(&cg_d_group_name, std::placeholders::_1));
|
||||
CMD2_DL_V ("d.group.set", std::bind(&cg_d_group_set, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
CMD2_DL_LIST ("f.multicall", std::bind(&f_multicall, std::placeholders::_1, std::placeholders::_2));
|
||||
|
||||
+104
-84
@@ -5,12 +5,12 @@
|
||||
// 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
|
||||
@@ -40,25 +40,24 @@
|
||||
#include <torrent/utils/log.h>
|
||||
#include <torrent/utils/option_strings.h>
|
||||
|
||||
#include "globals.h"
|
||||
#include "control.h"
|
||||
#include "command_helpers.h"
|
||||
#include "control.h"
|
||||
#include "globals.h"
|
||||
#include "rpc/parse.h"
|
||||
#include "rpc/parse_options.h"
|
||||
|
||||
static std::vector<std::pair<const char*, int>> object_storage_flags = {
|
||||
{ "multi", rpc::object_storage::flag_multi_type },
|
||||
{ "simple", rpc::object_storage::flag_function_type },
|
||||
{ "value", rpc::object_storage::flag_value_type },
|
||||
{ "bool", rpc::object_storage::flag_bool_type },
|
||||
{ "string", rpc::object_storage::flag_string_type },
|
||||
{ "list", rpc::object_storage::flag_list_type },
|
||||
{"multi", rpc::object_storage::flag_multi_type},
|
||||
{"simple", rpc::object_storage::flag_function_type},
|
||||
{"value", rpc::object_storage::flag_value_type},
|
||||
{"bool", rpc::object_storage::flag_bool_type},
|
||||
{"string", rpc::object_storage::flag_string_type},
|
||||
{"list", rpc::object_storage::flag_list_type},
|
||||
|
||||
{ "static", rpc::object_storage::flag_static },
|
||||
{ "private", rpc::object_storage::flag_private },
|
||||
{ "const", rpc::object_storage::flag_constant },
|
||||
{ "rlookup", rpc::object_storage::flag_rlookup }
|
||||
};
|
||||
{"static", rpc::object_storage::flag_static},
|
||||
{"private", rpc::object_storage::flag_private},
|
||||
{"const", rpc::object_storage::flag_constant},
|
||||
{"rlookup", rpc::object_storage::flag_rlookup}};
|
||||
|
||||
static int
|
||||
object_storage_parse_flag(const std::string& flag) {
|
||||
@@ -109,7 +108,7 @@ system_method_generate_command2(torrent::Object* object, torrent::Object::list_c
|
||||
if (first + 1 == last) {
|
||||
if (!first->is_dict_key())
|
||||
throw torrent::input_error("New command of wrong type.");
|
||||
|
||||
|
||||
*object = *first;
|
||||
|
||||
uint32_t flags = object->flags() & torrent::Object::mask_function;
|
||||
@@ -122,9 +121,9 @@ system_method_generate_command2(torrent::Object* object, torrent::Object::list_c
|
||||
while (first != last) {
|
||||
if (!first->is_dict_key())
|
||||
throw torrent::input_error("New command of wrong type.");
|
||||
|
||||
|
||||
object->as_list().push_back(*first++);
|
||||
|
||||
|
||||
uint32_t flags = object->as_list().back().flags() & torrent::Object::mask_function;
|
||||
object->as_list().back().unset_flags(torrent::Object::mask_function);
|
||||
object->as_list().back().set_flags((flags >> 1) & torrent::Object::mask_function);
|
||||
@@ -134,20 +133,32 @@ system_method_generate_command2(torrent::Object* object, torrent::Object::list_c
|
||||
|
||||
// torrent::Object
|
||||
// system_method_insert_function(const torrent::Object::list_type& args, int flags) {
|
||||
|
||||
|
||||
// }
|
||||
|
||||
// This is only used by tinyxml2, xmlrpc-c intercepts the call internally
|
||||
torrent::Object
|
||||
system_listMethods() {
|
||||
torrent::Object resultRaw = torrent::Object::create_list();
|
||||
torrent::Object::list_type& result = resultRaw.as_list();
|
||||
result.push_back("system.multicall"); // Handled directly by the XMLRPC code
|
||||
for (auto itr : rpc::commands) {
|
||||
result.push_back(itr.first);
|
||||
}
|
||||
return resultRaw;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
system_method_insert_object(const torrent::Object::list_type& args, int flags) {
|
||||
if (args.empty())
|
||||
throw torrent::input_error("Invalid argument count.");
|
||||
|
||||
torrent::Object::list_const_iterator itrArgs = args.begin();
|
||||
const std::string& rawKey = (itrArgs++)->as_string();
|
||||
const std::string& raw_key = (itrArgs++)->as_string();
|
||||
|
||||
if (rawKey.empty() ||
|
||||
control->object_storage()->find_raw_string(torrent::raw_string::from_string(rawKey)) != control->object_storage()->end() ||
|
||||
rpc::commands.has(rawKey) || rpc::commands.has(rawKey + ".set"))
|
||||
if (raw_key.empty() ||
|
||||
control->object_storage()->find_raw_string(torrent::raw_string::from_string(raw_key)) != control->object_storage()->end() ||
|
||||
rpc::commands.has(raw_key) || rpc::commands.has(raw_key + ".set"))
|
||||
throw torrent::input_error("Invalid key.");
|
||||
|
||||
torrent::Object value;
|
||||
@@ -171,8 +182,7 @@ system_method_insert_object(const torrent::Object::list_type& args, int flags) {
|
||||
throw torrent::input_error("Invalid type.");
|
||||
}
|
||||
|
||||
// We must initialize this varriable to prevent a critical memory leak
|
||||
int cmd_flags = rpc::CommandMap::flag_delete_key;
|
||||
int cmd_flags = 0;
|
||||
|
||||
if (!(flags & rpc::object_storage::flag_static))
|
||||
cmd_flags |= rpc::CommandMap::flag_modifiable;
|
||||
@@ -180,81 +190,93 @@ system_method_insert_object(const torrent::Object::list_type& args, int flags) {
|
||||
cmd_flags |= rpc::CommandMap::flag_public_xmlrpc;
|
||||
|
||||
if ((flags & rpc::object_storage::mask_type) == rpc::object_storage::flag_list_type) {
|
||||
torrent::Object valueList = torrent::Object::create_list();
|
||||
torrent::Object valueList = torrent::Object::create_list();
|
||||
torrent::Object::list_type& valueListType = valueList.as_list();
|
||||
|
||||
if ((itrArgs)->is_list())
|
||||
valueListType = (itrArgs)->as_list();
|
||||
|
||||
control->object_storage()->insert_str(rawKey, valueList, flags);
|
||||
control->object_storage()->insert_str(raw_key, valueList, flags);
|
||||
} else {
|
||||
control->object_storage()->insert_str(rawKey, value, flags);
|
||||
control->object_storage()->insert_str(raw_key, value, flags);
|
||||
}
|
||||
|
||||
if ((flags & rpc::object_storage::mask_type) == rpc::object_storage::flag_function_type ||
|
||||
(flags & rpc::object_storage::mask_type) == rpc::object_storage::flag_multi_type) {
|
||||
|
||||
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call<rpc::target_type> >::type>
|
||||
(create_new_key(rawKey),
|
||||
std::bind(&rpc::object_storage::call_function_str, control->object_storage(),
|
||||
rawKey, std::placeholders::_1, std::placeholders::_2),
|
||||
&rpc::command_base_call<rpc::target_type>,
|
||||
cmd_flags, NULL, NULL);
|
||||
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call<rpc::target_type>>::type>(
|
||||
raw_key,
|
||||
std::bind(&rpc::object_storage::call_function_str, control->object_storage(), raw_key, std::placeholders::_1, std::placeholders::_2),
|
||||
&rpc::command_base_call<rpc::target_type>,
|
||||
cmd_flags,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
} else {
|
||||
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call<rpc::target_type> >::type>
|
||||
(create_new_key(rawKey),
|
||||
std::bind(&rpc::object_storage::get_str, control->object_storage(), rawKey),
|
||||
&rpc::command_base_call<rpc::target_type>,
|
||||
cmd_flags, NULL, NULL);
|
||||
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call<rpc::target_type>>::type>(
|
||||
raw_key,
|
||||
std::bind(&rpc::object_storage::get_str, control->object_storage(), raw_key),
|
||||
&rpc::command_base_call<rpc::target_type>,
|
||||
cmd_flags,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
// Not the right argument.
|
||||
// if (flags & rpc::object_storage::flag_rlookup) {
|
||||
// rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_string<rpc::target_type> >::type>
|
||||
// (create_new_key<9>(rawKey, ".rlookup"),
|
||||
// std::bind(&rpc::object_storage::rlookup_obj_list, control->object_storage(), rawKey),
|
||||
// (create_new_key<9>(raw_key, ".rlookup"),
|
||||
// std::bind(&rpc::object_storage::rlookup_obj_list, control->object_storage(), raw_key),
|
||||
// &rpc::command_base_call_string<rpc::target_type>,
|
||||
// cmd_flags, NULL, NULL);
|
||||
// }
|
||||
|
||||
// TODO: Next... Make test class for this.
|
||||
|
||||
// // Ehm... no proper handling if these throw.
|
||||
// // Ehm... no proper handling if these throw.
|
||||
|
||||
if (!(flags & rpc::object_storage::flag_constant)) {
|
||||
switch (flags & rpc::object_storage::mask_type) {
|
||||
case rpc::object_storage::flag_bool_type:
|
||||
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_value<rpc::target_type> >::type>
|
||||
(create_new_key<5>(rawKey, ".set"),
|
||||
std::bind(&rpc::object_storage::set_str_bool, control->object_storage(), rawKey, std::placeholders::_2),
|
||||
&rpc::command_base_call_value<rpc::target_type>,
|
||||
cmd_flags, NULL, NULL);
|
||||
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_value<rpc::target_type>>::type>(
|
||||
raw_key + ".set",
|
||||
std::bind(&rpc::object_storage::set_str_bool, control->object_storage(), raw_key, std::placeholders::_2),
|
||||
&rpc::command_base_call_value<rpc::target_type>,
|
||||
cmd_flags,
|
||||
NULL,
|
||||
NULL);
|
||||
break;
|
||||
case rpc::object_storage::flag_value_type:
|
||||
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_value<rpc::target_type> >::type>
|
||||
(create_new_key<5>(rawKey, ".set"),
|
||||
std::bind(&rpc::object_storage::set_str_value, control->object_storage(), rawKey, std::placeholders::_2),
|
||||
&rpc::command_base_call_value<rpc::target_type>,
|
||||
cmd_flags, NULL, NULL);
|
||||
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_value<rpc::target_type>>::type>(
|
||||
raw_key + ".set",
|
||||
std::bind(&rpc::object_storage::set_str_value, control->object_storage(), raw_key, std::placeholders::_2),
|
||||
&rpc::command_base_call_value<rpc::target_type>,
|
||||
cmd_flags,
|
||||
NULL,
|
||||
NULL);
|
||||
break;
|
||||
case rpc::object_storage::flag_string_type:
|
||||
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_string<rpc::target_type> >::type>
|
||||
(create_new_key<5>(rawKey, ".set"),
|
||||
std::bind(&rpc::object_storage::set_str_string, control->object_storage(), rawKey, std::placeholders::_2),
|
||||
&rpc::command_base_call_string<rpc::target_type>,
|
||||
cmd_flags, NULL, NULL);
|
||||
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_string<rpc::target_type>>::type>(
|
||||
raw_key + ".set",
|
||||
std::bind(&rpc::object_storage::set_str_string, control->object_storage(), raw_key, std::placeholders::_2),
|
||||
&rpc::command_base_call_string<rpc::target_type>,
|
||||
cmd_flags,
|
||||
NULL,
|
||||
NULL);
|
||||
break;
|
||||
case rpc::object_storage::flag_list_type:
|
||||
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_list<rpc::target_type> >::type>
|
||||
(create_new_key<5>(rawKey, ".set"),
|
||||
std::bind(&rpc::object_storage::set_str_list, control->object_storage(), rawKey, std::placeholders::_2),
|
||||
&rpc::command_base_call_list<rpc::target_type>,
|
||||
cmd_flags, NULL, NULL);
|
||||
rpc::commands.insert_slot<rpc::command_base_is_type<rpc::command_base_call_list<rpc::target_type>>::type>(
|
||||
raw_key + ".set",
|
||||
std::bind(&rpc::object_storage::set_str_list, control->object_storage(), raw_key, std::placeholders::_2),
|
||||
&rpc::command_base_call_list<rpc::target_type>,
|
||||
cmd_flags,
|
||||
NULL,
|
||||
NULL);
|
||||
break;
|
||||
case rpc::object_storage::flag_function_type:
|
||||
case rpc::object_storage::flag_multi_type:
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,22 +304,15 @@ system_method_insert(const torrent::Object::list_type& args) {
|
||||
throw torrent::input_error("Invalid argument count.");
|
||||
|
||||
torrent::Object::list_const_iterator itrArgs = args.begin();
|
||||
const std::string& rawKey = (itrArgs++)->as_string();
|
||||
const std::string& raw_key = (itrArgs++)->as_string();
|
||||
|
||||
if (rawKey.empty() || rpc::commands.has(rawKey))
|
||||
if (raw_key.empty() || rpc::commands.has(raw_key))
|
||||
throw torrent::input_error("Invalid key.");
|
||||
|
||||
int flags = rpc::CommandMap::flag_delete_key | rpc::CommandMap::flag_modifiable | rpc::CommandMap::flag_public_xmlrpc;
|
||||
int new_flags = rpc::parse_option_flags(itrArgs->as_string(), std::bind(&object_storage_parse_flag, std::placeholders::_1));
|
||||
|
||||
if ((new_flags & rpc::object_storage::flag_private))
|
||||
flags &= ~rpc::CommandMap::flag_public_xmlrpc;
|
||||
|
||||
if ((new_flags & rpc::object_storage::flag_constant))
|
||||
flags &= ~rpc::CommandMap::flag_modifiable;
|
||||
|
||||
torrent::Object::list_type new_args;
|
||||
new_args.push_back(rawKey);
|
||||
new_args.push_back(raw_key);
|
||||
|
||||
if ((new_flags & rpc::object_storage::flag_function_type) ||
|
||||
(new_flags & rpc::object_storage::flag_multi_type)) {
|
||||
@@ -345,8 +360,7 @@ system_method_redirect(const torrent::Object::list_type& args) {
|
||||
std::string new_key = torrent::object_create_string(args.front());
|
||||
std::string dest_key = torrent::object_create_string(args.back());
|
||||
|
||||
rpc::commands.create_redirect(create_new_key(new_key), create_new_key(dest_key),
|
||||
rpc::CommandMap::flag_public_xmlrpc | rpc::CommandMap::flag_delete_key | rpc::CommandMap::flag_modifiable);
|
||||
rpc::commands.create_redirect(new_key, dest_key, rpc::CommandMap::flag_public_xmlrpc | rpc::CommandMap::flag_modifiable);
|
||||
|
||||
return torrent::Object();
|
||||
}
|
||||
@@ -372,9 +386,9 @@ system_method_has_key(const torrent::Object::list_type& args) {
|
||||
throw torrent::input_error("Invalid argument count.");
|
||||
|
||||
torrent::Object::list_const_iterator itrArgs = args.begin();
|
||||
const std::string& key = (itrArgs++)->as_string();
|
||||
const std::string& cmd_key = (itrArgs++)->as_string();
|
||||
|
||||
const std::string& key = (itrArgs++)->as_string();
|
||||
const std::string& cmd_key = (itrArgs++)->as_string();
|
||||
|
||||
return control->object_storage()->has_str_multi_key(key, cmd_key);
|
||||
}
|
||||
|
||||
@@ -384,9 +398,9 @@ system_method_set_key(const torrent::Object::list_type& args) {
|
||||
throw torrent::input_error("Invalid argument count.");
|
||||
|
||||
torrent::Object::list_const_iterator itrArgs = args.begin();
|
||||
const std::string& key = (itrArgs++)->as_string();
|
||||
const std::string& cmd_key = (itrArgs++)->as_string();
|
||||
|
||||
const std::string& key = (itrArgs++)->as_string();
|
||||
const std::string& cmd_key = (itrArgs++)->as_string();
|
||||
|
||||
if (itrArgs == args.end()) {
|
||||
control->object_storage()->erase_str_multi_key(key, cmd_key);
|
||||
return torrent::Object();
|
||||
@@ -403,9 +417,9 @@ system_method_set_key(const torrent::Object::list_type& args) {
|
||||
torrent::Object
|
||||
system_method_list_keys(const torrent::Object::string_type& args) {
|
||||
const torrent::Object::map_type& multi_cmd = control->object_storage()->get_str(args).as_map();
|
||||
|
||||
torrent::Object rawResult = torrent::Object::create_list();
|
||||
torrent::Object::list_type& result = rawResult.as_list();
|
||||
|
||||
torrent::Object rawResult = torrent::Object::create_list();
|
||||
torrent::Object::list_type& result = rawResult.as_list();
|
||||
|
||||
for (torrent::Object::map_const_iterator itr = multi_cmd.begin(), last = multi_cmd.end(); itr != last; itr++)
|
||||
result.push_back(itr->first);
|
||||
@@ -428,6 +442,11 @@ cmd_catch(rpc::target_type target, const torrent::Object& args) {
|
||||
|
||||
void
|
||||
initialize_command_dynamic() {
|
||||
// clang-format off
|
||||
#ifdef HAVE_XMLRPC_TINYXML2
|
||||
CMD2_ANY ("system.listMethods", std::bind(&system_listMethods)); // only used by tinyxml2
|
||||
#endif
|
||||
|
||||
CMD2_VAR_BOOL ("method.use_deprecated", true);
|
||||
CMD2_VAR_VALUE ("method.use_intermediate", 1);
|
||||
|
||||
@@ -472,4 +491,5 @@ initialize_command_dynamic() {
|
||||
CMD2_ANY ("strings.log_group", std::bind(&torrent::option_list_strings, torrent::OPTION_LOG_GROUP));
|
||||
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
|
||||
}
|
||||
|
||||
+11
-11
@@ -69,7 +69,7 @@ cg_d_group_set(core::Download* download, const torrent::Object& arg) {
|
||||
torrent::Object
|
||||
apply_cg_list() {
|
||||
torrent::Object::list_type result;
|
||||
|
||||
|
||||
for (torrent::ResourceManager::group_iterator
|
||||
itr = torrent::resource_manager()->group_begin(),
|
||||
last = torrent::resource_manager()->group_end(); itr != last; itr++)
|
||||
@@ -121,8 +121,7 @@ cg_get_index(const torrent::Object& raw_args) {
|
||||
|
||||
if (arg.is_string()) {
|
||||
if (!rpc::parse_whole_value_nothrow(arg.as_string().c_str(), &index)) {
|
||||
std::vector<torrent::choke_group*>::iterator itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(),
|
||||
rak::equal(arg.as_string(), std::mem_fun(&torrent::choke_group::name)));
|
||||
auto itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](torrent::choke_group* cg) { return arg.as_string() == cg->name(); });
|
||||
|
||||
if (itr == cg_list_hack.end())
|
||||
throw torrent::input_error("Choke group not found.");
|
||||
@@ -154,14 +153,17 @@ cg_get_group(const torrent::Object& raw_args) {
|
||||
}
|
||||
|
||||
int64_t cg_d_group(core::Download* download) { return download->group(); }
|
||||
const std::string& cg_d_group_name(core::Download* download) {
|
||||
return cg_list_hack.at(download->group())->name();
|
||||
}
|
||||
void cg_d_group_set(core::Download* download, const torrent::Object& arg) { download->set_group(cg_get_index(arg)); }
|
||||
|
||||
torrent::Object
|
||||
apply_cg_list() {
|
||||
torrent::Object::list_type result;
|
||||
|
||||
for (std::vector<torrent::choke_group*>::iterator itr = cg_list_hack.begin(), last = cg_list_hack.end(); itr != last; itr++)
|
||||
result.push_back((*itr)->name());
|
||||
|
||||
for (auto itr : cg_list_hack)
|
||||
result.push_back(itr->name());
|
||||
|
||||
return torrent::Object::from_list(result);
|
||||
}
|
||||
@@ -179,8 +181,7 @@ apply_cg_insert(const std::string& arg) {
|
||||
throw torrent::input_error("Cannot use a value string as choke group name.");
|
||||
|
||||
if (arg.empty() ||
|
||||
std::find_if(cg_list_hack.begin(), cg_list_hack.end(),
|
||||
rak::equal(arg, std::mem_fun(&torrent::choke_group::name))) != cg_list_hack.end())
|
||||
std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](torrent::choke_group* cg) { return arg == cg->name(); }) != cg_list_hack.end())
|
||||
throw torrent::input_error("Duplicate name for choke group.");
|
||||
|
||||
cg_list_hack.push_back(new torrent::choke_group());
|
||||
@@ -194,8 +195,7 @@ apply_cg_insert(const std::string& arg) {
|
||||
|
||||
torrent::Object
|
||||
apply_cg_index_of(const std::string& arg) {
|
||||
std::vector<torrent::choke_group*>::iterator itr =
|
||||
std::find_if(cg_list_hack.begin(), cg_list_hack.end(), rak::equal(arg, std::mem_fun(&torrent::choke_group::name)));
|
||||
auto itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](torrent::choke_group* cg) { return arg == cg->name(); });
|
||||
|
||||
if (itr == cg_list_hack.end())
|
||||
throw torrent::input_error("Choke group not found.");
|
||||
@@ -340,7 +340,7 @@ void
|
||||
initialize_command_groups() {
|
||||
CMD2_ANY ("choke_group.list", std::bind(&apply_cg_list));
|
||||
CMD2_ANY_STRING ("choke_group.insert", std::bind(&apply_cg_insert, std::placeholders::_2));
|
||||
|
||||
|
||||
#if USE_CHOKE_GROUP
|
||||
CMD2_ANY ("choke_group.size", std::bind(&torrent::ResourceManager::group_size, torrent::resource_manager()));
|
||||
CMD2_ANY_STRING ("choke_group.index_of", std::bind(&torrent::ResourceManager::group_index_of, torrent::resource_manager(), std::placeholders::_2));
|
||||
|
||||
+4
-24
@@ -148,12 +148,12 @@ void initialize_commands();
|
||||
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_xmlrpc | rpc::CommandMap::flag_tracker_target | rpc::CommandMap::flag_dont_delete);
|
||||
|
||||
#define CMD2_REDIRECT_GENERIC_STR(from_key, to_key) \
|
||||
rpc::commands.create_redirect(create_new_key(from_key), create_new_key(to_key), \
|
||||
rpc::CommandMap::flag_public_xmlrpc | rpc::CommandMap::flag_no_target | rpc::CommandMap::flag_delete_key);
|
||||
rpc::commands.create_redirect(from_key, to_key, \
|
||||
rpc::CommandMap::flag_public_xmlrpc | rpc::CommandMap::flag_no_target);
|
||||
|
||||
#define CMD2_REDIRECT_GENERIC_STR_NO_EXPORT(from_key, to_key) \
|
||||
rpc::commands.create_redirect(create_new_key(from_key), create_new_key(to_key), \
|
||||
rpc::CommandMap::flag_no_target | rpc::CommandMap::flag_delete_key);
|
||||
rpc::commands.create_redirect(from_key, to_key, \
|
||||
rpc::CommandMap::flag_no_target);
|
||||
|
||||
//
|
||||
// Conversion of return types:
|
||||
@@ -188,24 +188,4 @@ template <typename T>
|
||||
object_convert_type<T, void>
|
||||
object_convert_void(T f) { return f; }
|
||||
|
||||
//
|
||||
// Key creation:
|
||||
//
|
||||
|
||||
template <int postfix_size>
|
||||
inline const char*
|
||||
create_new_key(const std::string& key, const char postfix[postfix_size]) {
|
||||
char *buffer = new char[key.size() + std::max(postfix_size, 1)];
|
||||
std::memcpy(buffer, key.c_str(), key.size() + 1);
|
||||
std::memcpy(buffer + key.size(), postfix, postfix_size);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
inline const char*
|
||||
create_new_key(const std::string& key) {
|
||||
char *buffer = new char[key.size() + 1];
|
||||
std::memcpy(buffer, key.c_str(), key.size() + 1);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -32,7 +32,7 @@ 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.");
|
||||
|
||||
|
||||
torrent::Object::list_const_iterator itr = args.begin();
|
||||
|
||||
std::string output_id = (itr++)->as_string();
|
||||
|
||||
@@ -135,7 +135,7 @@ initialize_xmlrpc() {
|
||||
if (!(itr->second.m_flags & rpc::CommandMap::flag_public_xmlrpc))
|
||||
continue;
|
||||
|
||||
rpc::xmlrpc.insert_command(itr->first, itr->second.m_parm, itr->second.m_doc);
|
||||
rpc::xmlrpc.insert_command(itr->first.c_str(), itr->second.m_parm, itr->second.m_doc);
|
||||
}
|
||||
|
||||
lt_log_print(torrent::LOG_RPC_EVENTS, "XMLRPC initialized with %u functions.", count);
|
||||
@@ -299,8 +299,8 @@ initialize_command_network() {
|
||||
CMD2_VAR_BOOL ("network.scgi.dont_route", false);
|
||||
|
||||
CMD2_ANY_STRING ("network.xmlrpc.dialect.set", std::bind(&apply_xmlrpc_dialect, std::placeholders::_2));
|
||||
CMD2_ANY ("network.xmlrpc.size_limit", std::bind(&rpc::XmlRpc::size_limit));
|
||||
CMD2_ANY_VALUE_V ("network.xmlrpc.size_limit.set", std::bind(&rpc::XmlRpc::set_size_limit, std::placeholders::_2));
|
||||
CMD2_ANY ("network.xmlrpc.size_limit", std::bind(&rpc::XmlRpc::size_limit, rpc::xmlrpc));
|
||||
CMD2_ANY_VALUE_V ("network.xmlrpc.size_limit.set", std::bind(&rpc::XmlRpc::set_size_limit, rpc::xmlrpc, std::placeholders::_2));
|
||||
|
||||
CMD2_ANY ("network.block.ipv4", std::bind(&torrent::ConnectionManager::is_block_ipv4, cm));
|
||||
CMD2_ANY_VALUE_V ("network.block.ipv4.set", std::bind(&torrent::ConnectionManager::set_block_ipv4, cm, std::placeholders::_2));
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
|
||||
#include <rak/algorithm.h>
|
||||
#include <rak/functional.h>
|
||||
#include <rak/functional_fun.h>
|
||||
#include <torrent/utils/log.h>
|
||||
|
||||
#include "core/manager.h"
|
||||
|
||||
+1
-1
@@ -82,7 +82,7 @@ Control::Control() :
|
||||
|
||||
m_taskShutdown.slot() = std::bind(&Control::handle_shutdown, this);
|
||||
|
||||
m_commandScheduler->set_slot_error_message(rak::mem_fn(m_core, &core::Manager::push_log_std));
|
||||
m_commandScheduler->set_slot_error_message([this](const std::string& msg) { m_core->push_log_std(msg); });
|
||||
}
|
||||
|
||||
Control::~Control() {
|
||||
|
||||
+5
-4
@@ -37,6 +37,7 @@
|
||||
#ifndef RTORRENT_CONTROL_H
|
||||
#define RTORRENT_CONTROL_H
|
||||
|
||||
#include <atomic>
|
||||
#include <cinttypes>
|
||||
#include <sys/types.h>
|
||||
#include <rak/timer.h>
|
||||
@@ -87,8 +88,8 @@ public:
|
||||
|
||||
void handle_shutdown();
|
||||
|
||||
void receive_normal_shutdown() { m_shutdownReceived = true; __sync_synchronize(); }
|
||||
void receive_quick_shutdown() { m_shutdownReceived = true; m_shutdownQuick = true; __sync_synchronize(); }
|
||||
void receive_normal_shutdown() { m_shutdownReceived = true; }
|
||||
void receive_quick_shutdown() { m_shutdownReceived = true; m_shutdownQuick = true; }
|
||||
|
||||
core::Manager* core() { return m_core; }
|
||||
core::ViewManager* view_manager() { return m_viewManager; }
|
||||
@@ -134,8 +135,8 @@ private:
|
||||
|
||||
rak::priority_item m_taskShutdown;
|
||||
|
||||
bool m_shutdownReceived lt_cacheline_aligned;
|
||||
bool m_shutdownQuick lt_cacheline_aligned;
|
||||
std::atomic<bool> m_shutdownReceived;
|
||||
std::atomic<bool> m_shutdownQuick;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -67,6 +67,9 @@ CurlGet::start() {
|
||||
if (m_stream == NULL)
|
||||
throw torrent::internal_error("Tried to call CurlGet::start without a valid output stream.");
|
||||
|
||||
if (!m_stack->is_running())
|
||||
return;
|
||||
|
||||
m_handle = curl_easy_init();
|
||||
|
||||
if (m_handle == NULL)
|
||||
@@ -83,8 +86,7 @@ CurlGet::start() {
|
||||
// Normally libcurl should handle the timeout. But sometimes that doesn't
|
||||
// work right so we do a fallback timeout that just aborts the transfer.
|
||||
m_taskTimeout.slot() = std::bind(&CurlGet::receive_timeout, this);
|
||||
priority_queue_erase(&taskScheduler, &m_taskTimeout);
|
||||
priority_queue_insert(&taskScheduler, &m_taskTimeout, cachedTime + rak::timer::from_seconds(m_timeout + 5));
|
||||
priority_queue_update(&taskScheduler, &m_taskTimeout, cachedTime + rak::timer::from_seconds(m_timeout + 5));
|
||||
}
|
||||
|
||||
curl_easy_setopt(m_handle, CURLOPT_FORBID_REUSE, (long)1);
|
||||
|
||||
+4
-37
@@ -1,39 +1,3 @@
|
||||
// rTorrent - BitTorrent client
|
||||
// Copyright (C) 2005-2008, 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 <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
@@ -55,6 +19,9 @@ CurlSocket::receive_socket(void* easy_handle, curl_socket_t fd, int what, void*
|
||||
CurlStack* stack = (CurlStack*)userp;
|
||||
CurlSocket* socket = (CurlSocket*)socketp;
|
||||
|
||||
if (!stack->is_running())
|
||||
return 0;
|
||||
|
||||
if (what == CURL_POLL_REMOVE) {
|
||||
// We also probably need the special code here as we're not
|
||||
// guaranteed that the fd will be closed, afaik.
|
||||
@@ -75,7 +42,7 @@ CurlSocket::receive_socket(void* easy_handle, curl_socket_t fd, int what, void*
|
||||
// No interface for libcurl to signal when it's interested in error events.
|
||||
// Assume that hence it must always be interested in them.
|
||||
torrent::main_thread()->poll()->insert_error(socket);
|
||||
}
|
||||
}
|
||||
|
||||
if (what == CURL_POLL_NONE || what == CURL_POLL_OUT)
|
||||
torrent::main_thread()->poll()->remove_read(socket);
|
||||
|
||||
+39
-69
@@ -1,39 +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 <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -47,15 +11,9 @@
|
||||
|
||||
namespace core {
|
||||
|
||||
CurlStack::CurlStack() :
|
||||
m_handle((void*)curl_multi_init()),
|
||||
m_active(0),
|
||||
m_maxActive(32),
|
||||
m_ssl_verify_host(true),
|
||||
m_ssl_verify_peer(true),
|
||||
m_dns_timeout(60) {
|
||||
|
||||
m_taskTimeout.slot() = std::bind(&CurlStack::receive_timeout, this);
|
||||
CurlStack::CurlStack() {
|
||||
m_handle = (void*)curl_multi_init();
|
||||
m_task_timeout.slot() = std::bind(&CurlStack::receive_timeout, this);
|
||||
|
||||
#if (LIBCURL_VERSION_NUM >= 0x071000)
|
||||
curl_multi_setopt((CURLM*)m_handle, CURLMOPT_TIMERDATA, this);
|
||||
@@ -66,11 +24,21 @@ CurlStack::CurlStack() :
|
||||
}
|
||||
|
||||
CurlStack::~CurlStack() {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
CurlStack::shutdown() {
|
||||
if (!m_running)
|
||||
return;
|
||||
|
||||
m_running = false;
|
||||
|
||||
while (!empty())
|
||||
front()->close();
|
||||
|
||||
curl_multi_cleanup((CURLM*)m_handle);
|
||||
priority_queue_erase(&taskScheduler, &m_taskTimeout);
|
||||
priority_queue_erase(&taskScheduler, &m_task_timeout);
|
||||
}
|
||||
|
||||
CurlGet*
|
||||
@@ -80,6 +48,9 @@ CurlStack::new_object() {
|
||||
|
||||
CurlSocket*
|
||||
CurlStack::new_socket(int fd) {
|
||||
if (!m_running)
|
||||
throw torrent::internal_error("CurlStack::new_socket() called when not running.");
|
||||
|
||||
CurlSocket* socket = new CurlSocket(fd, this);
|
||||
curl_multi_assign((CURLM*)m_handle, fd, socket);
|
||||
return socket;
|
||||
@@ -115,7 +86,7 @@ CurlStack::receive_action(CurlSocket* socket, int events) {
|
||||
; // Do nothing.
|
||||
|
||||
if (empty())
|
||||
priority_queue_erase(&taskScheduler, &m_taskTimeout);
|
||||
priority_queue_erase(&taskScheduler, &m_task_timeout);
|
||||
}
|
||||
|
||||
} while (code == CURLM_CALL_MULTI_PERFORM);
|
||||
@@ -133,11 +104,11 @@ CurlStack::process_done_handle() {
|
||||
throw torrent::internal_error("CurlStack::receive_action() msg->msg != CURLMSG_DONE.");
|
||||
|
||||
if (msg->data.result == CURLE_COULDNT_RESOLVE_HOST) {
|
||||
iterator itr = std::find_if(begin(), end(), rak::equal(msg->easy_handle, std::mem_fun(&CurlGet::handle)));
|
||||
|
||||
iterator itr = std::find_if(begin(), end(), [&msg](CurlGet* get) { return get->handle() == msg->easy_handle; });
|
||||
|
||||
if (itr == end())
|
||||
throw torrent::internal_error("Could not find CurlGet when calling CurlStack::receive_action.");
|
||||
|
||||
|
||||
if (!(*itr)->is_using_ipv6()) {
|
||||
(*itr)->retry_ipv6();
|
||||
|
||||
@@ -155,7 +126,7 @@ CurlStack::process_done_handle() {
|
||||
|
||||
void
|
||||
CurlStack::transfer_done(void* handle, const char* msg) {
|
||||
iterator itr = std::find_if(begin(), end(), rak::equal(handle, std::mem_fun(&CurlGet::handle)));
|
||||
iterator itr = std::find_if(begin(), end(), [&handle](CurlGet* get) { return get->handle() == handle; });
|
||||
|
||||
if (itr == end())
|
||||
throw torrent::internal_error("Could not find CurlGet with the right easy_handle.");
|
||||
@@ -171,30 +142,30 @@ CurlStack::receive_timeout() {
|
||||
receive_action(NULL, 0);
|
||||
|
||||
// Sometimes libcurl forgets to reset the timeout. Try to poll the value in that case, or use 10 seconds.
|
||||
if (!empty() && !m_taskTimeout.is_queued()) {
|
||||
if (!empty() && !m_task_timeout.is_queued()) {
|
||||
long timeout;
|
||||
curl_multi_timeout((CURLM*)m_handle, &timeout);
|
||||
priority_queue_insert(&taskScheduler, &m_taskTimeout,
|
||||
priority_queue_insert(&taskScheduler, &m_task_timeout,
|
||||
cachedTime + rak::timer::from_milliseconds(std::max<unsigned long>(timeout, 10000)));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CurlStack::add_get(CurlGet* get) {
|
||||
if (!m_userAgent.empty())
|
||||
curl_easy_setopt(get->handle(), CURLOPT_USERAGENT, m_userAgent.c_str());
|
||||
if (!m_user_agent.empty())
|
||||
curl_easy_setopt(get->handle(), CURLOPT_USERAGENT, m_user_agent.c_str());
|
||||
|
||||
if (!m_httpProxy.empty())
|
||||
curl_easy_setopt(get->handle(), CURLOPT_PROXY, m_httpProxy.c_str());
|
||||
if (!m_http_proxy.empty())
|
||||
curl_easy_setopt(get->handle(), CURLOPT_PROXY, m_http_proxy.c_str());
|
||||
|
||||
if (!m_bindAddress.empty())
|
||||
curl_easy_setopt(get->handle(), CURLOPT_INTERFACE, m_bindAddress.c_str());
|
||||
if (!m_bind_address.empty())
|
||||
curl_easy_setopt(get->handle(), CURLOPT_INTERFACE, m_bind_address.c_str());
|
||||
|
||||
if (!m_httpCaPath.empty())
|
||||
curl_easy_setopt(get->handle(), CURLOPT_CAPATH, m_httpCaPath.c_str());
|
||||
if (!m_http_ca_path.empty())
|
||||
curl_easy_setopt(get->handle(), CURLOPT_CAPATH, m_http_ca_path.c_str());
|
||||
|
||||
if (!m_httpCaCert.empty())
|
||||
curl_easy_setopt(get->handle(), CURLOPT_CAINFO, m_httpCaCert.c_str());
|
||||
if (!m_http_ca_cert.empty())
|
||||
curl_easy_setopt(get->handle(), CURLOPT_CAINFO, m_http_ca_cert.c_str());
|
||||
|
||||
curl_easy_setopt(get->handle(), CURLOPT_SSL_VERIFYHOST, (long)(m_ssl_verify_host ? 2 : 0));
|
||||
curl_easy_setopt(get->handle(), CURLOPT_SSL_VERIFYPEER, (long)(m_ssl_verify_peer ? 1 : 0));
|
||||
@@ -202,12 +173,12 @@ CurlStack::add_get(CurlGet* get) {
|
||||
|
||||
base_type::push_back(get);
|
||||
|
||||
if (m_active >= m_maxActive)
|
||||
if (m_active >= m_max_active)
|
||||
return;
|
||||
|
||||
m_active++;
|
||||
get->set_active(true);
|
||||
|
||||
|
||||
if (curl_multi_add_handle((CURLM*)m_handle, get->handle()) > 0)
|
||||
throw torrent::internal_error("Error calling curl_multi_add_handle.");
|
||||
|
||||
@@ -234,8 +205,8 @@ CurlStack::remove_get(CurlGet* get) {
|
||||
if (curl_multi_remove_handle((CURLM*)m_handle, get->handle()) > 0)
|
||||
throw torrent::internal_error("Error calling curl_multi_remove_handle.");
|
||||
|
||||
if (m_active == m_maxActive &&
|
||||
(itr = std::find_if(begin(), end(), std::not1(std::mem_fun(&CurlGet::is_active)))) != end()) {
|
||||
if (m_active == m_max_active &&
|
||||
(itr = std::find_if(begin(), end(), [](CurlGet* get) { return !get->is_active(); })) != end()) {
|
||||
(*itr)->set_active(true);
|
||||
|
||||
if (curl_multi_add_handle((CURLM*)m_handle, (*itr)->handle()) > 0)
|
||||
@@ -262,8 +233,7 @@ int
|
||||
CurlStack::set_timeout(void* handle, long timeout_ms, void* userp) {
|
||||
CurlStack* stack = (CurlStack*)userp;
|
||||
|
||||
priority_queue_erase(&taskScheduler, &stack->m_taskTimeout);
|
||||
priority_queue_insert(&taskScheduler, &stack->m_taskTimeout, cachedTime + rak::timer::from_milliseconds(timeout_ms));
|
||||
priority_queue_update(&taskScheduler, &stack->m_task_timeout, cachedTime + rak::timer::from_milliseconds(timeout_ms));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+28
-59
@@ -1,39 +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 <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
#ifndef RTORRENT_CORE_CURL_STACK_H
|
||||
#define RTORRENT_CORE_CURL_STACK_H
|
||||
|
||||
@@ -81,24 +45,27 @@ class CurlStack : std::deque<CurlGet*> {
|
||||
CurlStack();
|
||||
~CurlStack();
|
||||
|
||||
void shutdown();
|
||||
bool is_running() const { return m_running; }
|
||||
|
||||
CurlGet* new_object();
|
||||
CurlSocket* new_socket(int fd);
|
||||
|
||||
unsigned int active() const { return m_active; }
|
||||
unsigned int max_active() const { return m_maxActive; }
|
||||
void set_max_active(unsigned int a) { m_maxActive = a; }
|
||||
unsigned int max_active() const { return m_max_active; }
|
||||
void set_max_active(unsigned int a) { m_max_active = a; }
|
||||
|
||||
const std::string& user_agent() const { return m_userAgent; }
|
||||
const std::string& http_proxy() const { return m_httpProxy; }
|
||||
const std::string& bind_address() const { return m_bindAddress; }
|
||||
const std::string& http_capath() const { return m_httpCaPath; }
|
||||
const std::string& http_cacert() const { return m_httpCaCert; }
|
||||
const std::string& user_agent() const { return m_user_agent; }
|
||||
const std::string& http_proxy() const { return m_http_proxy; }
|
||||
const std::string& bind_address() const { return m_bind_address; }
|
||||
const std::string& http_capath() const { return m_http_ca_path; }
|
||||
const std::string& http_cacert() const { return m_http_ca_cert; }
|
||||
|
||||
void set_user_agent(const std::string& s) { m_userAgent = s; }
|
||||
void set_http_proxy(const std::string& s) { m_httpProxy = s; }
|
||||
void set_bind_address(const std::string& s) { m_bindAddress = s; }
|
||||
void set_http_capath(const std::string& s) { m_httpCaPath = s; }
|
||||
void set_http_cacert(const std::string& s) { m_httpCaCert = s; }
|
||||
void set_user_agent(const std::string& s) { m_user_agent = s; }
|
||||
void set_http_proxy(const std::string& s) { m_http_proxy = s; }
|
||||
void set_bind_address(const std::string& s) { m_bind_address = s; }
|
||||
void set_http_capath(const std::string& s) { m_http_ca_path = s; }
|
||||
void set_http_cacert(const std::string& s) { m_http_ca_cert = s; }
|
||||
|
||||
bool ssl_verify_host() const { return m_ssl_verify_host; }
|
||||
bool ssl_verify_peer() const { return m_ssl_verify_peer; }
|
||||
@@ -131,20 +98,22 @@ class CurlStack : std::deque<CurlGet*> {
|
||||
|
||||
void* m_handle;
|
||||
|
||||
unsigned int m_active;
|
||||
unsigned int m_maxActive;
|
||||
bool m_running{true};
|
||||
|
||||
rak::priority_item m_taskTimeout;
|
||||
unsigned int m_active{0};
|
||||
unsigned int m_max_active{32};
|
||||
|
||||
std::string m_userAgent;
|
||||
std::string m_httpProxy;
|
||||
std::string m_bindAddress;
|
||||
std::string m_httpCaPath;
|
||||
std::string m_httpCaCert;
|
||||
rak::priority_item m_task_timeout;
|
||||
|
||||
bool m_ssl_verify_host;
|
||||
bool m_ssl_verify_peer;
|
||||
long m_dns_timeout;
|
||||
std::string m_user_agent;
|
||||
std::string m_http_proxy;
|
||||
std::string m_bind_address;
|
||||
std::string m_http_ca_path;
|
||||
std::string m_http_ca_cert;
|
||||
|
||||
bool m_ssl_verify_host{true};
|
||||
bool m_ssl_verify_peer{true};
|
||||
long m_dns_timeout{60};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,39 +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 <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <cstdlib>
|
||||
@@ -76,7 +40,7 @@ is_network_uri(const std::string& uri) {
|
||||
static bool
|
||||
download_factory_add_stream(torrent::Object* root, const char* key, const char* filename) {
|
||||
std::fstream stream(filename, std::ios::in | std::ios::binary);
|
||||
|
||||
|
||||
if (!stream.is_open())
|
||||
return false;
|
||||
|
||||
@@ -228,7 +192,7 @@ DownloadFactory::receive_success() {
|
||||
torrent::Object& meta = root->insert_key("rtorrent_meta_download", torrent::Object::create_map());
|
||||
meta.insert_key("start", m_start);
|
||||
meta.insert_key("print_log", m_printLog);
|
||||
|
||||
|
||||
torrent::Object::list_type& commands = meta.insert_key("commands", torrent::Object::create_list()).as_list();
|
||||
|
||||
for (command_list_type::iterator itr = m_commands.begin(); itr != m_commands.end(); ++itr)
|
||||
@@ -238,7 +202,7 @@ DownloadFactory::receive_success() {
|
||||
if (m_session) {
|
||||
download_factory_add_stream(root, "rtorrent", (rak::path_expand(m_uri) + ".rtorrent").c_str());
|
||||
download_factory_add_stream(root, "libtorrent_resume", (rak::path_expand(m_uri) + ".libtorrent_resume").c_str());
|
||||
|
||||
|
||||
} else {
|
||||
// We only allow session torrents to keep their
|
||||
// 'rtorrent/libtorrent' sections. The "fast_resume" section
|
||||
@@ -321,8 +285,9 @@ DownloadFactory::receive_success() {
|
||||
if (torrent::log_groups[torrent::LOG_TORRENT_DEBUG].valid())
|
||||
log_created(download, rtorrent);
|
||||
|
||||
std::for_each(m_commands.begin(), m_commands.end(),
|
||||
rak::bind2nd(std::ptr_fun(&rpc::parse_command_multiple_std), rpc::make_target(download)));
|
||||
std::for_each(m_commands.begin(), m_commands.end(), [&download](const std::string& cmd) {
|
||||
rpc::parse_command_multiple_std(cmd, rpc::make_target(download));
|
||||
});
|
||||
|
||||
if (m_manager->download_list()->find(infohash) == m_manager->download_list()->end())
|
||||
throw torrent::input_error("The newly created download was removed.");
|
||||
@@ -338,7 +303,7 @@ DownloadFactory::receive_success() {
|
||||
|
||||
if (m_printLog)
|
||||
m_manager->push_log_std(msg);
|
||||
|
||||
|
||||
if (m_manager->download_list()->find(infohash) != m_manager->download_list()->end()) {
|
||||
// Should stop it, mark it bad. Perhaps even delete it?
|
||||
download->set_hash_failed(true);
|
||||
|
||||
@@ -1,39 +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 <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
// DownloadStore handles the saving and listing of session torrents.
|
||||
|
||||
#include "config.h"
|
||||
@@ -129,7 +93,11 @@ DownloadStore::write_bencode(const std::string& filename, const torrent::Object&
|
||||
if (fd < 0)
|
||||
goto download_store_save_error;
|
||||
|
||||
#ifdef __APPLE__
|
||||
fsync(fd);
|
||||
#else
|
||||
fdatasync(fd);
|
||||
#endif
|
||||
::close(fd);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1,39 +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 <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
#ifndef RTORRENT_CORE_DOWNLOAD_STORE_H
|
||||
#define RTORRENT_CORE_DOWNLOAD_STORE_H
|
||||
|
||||
|
||||
+14
-47
@@ -1,39 +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 <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <cstdio>
|
||||
@@ -186,6 +150,8 @@ Manager::initialize_second() {
|
||||
|
||||
void
|
||||
Manager::cleanup() {
|
||||
m_httpStack->shutdown();
|
||||
|
||||
// Need to disconnect log signals? Not really since we won't receive
|
||||
// any more.
|
||||
|
||||
@@ -198,15 +164,14 @@ Manager::cleanup() {
|
||||
|
||||
delete m_httpStack;
|
||||
CurlStack::global_cleanup();
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
Manager::shutdown(bool force) {
|
||||
if (!force)
|
||||
std::for_each(m_downloadList->begin(), m_downloadList->end(), std::bind1st(std::mem_fun(&DownloadList::pause_default), m_downloadList));
|
||||
std::for_each(m_downloadList->begin(), m_downloadList->end(), [this](Download* d) { m_downloadList->pause_default(d); });
|
||||
else
|
||||
std::for_each(m_downloadList->begin(), m_downloadList->end(), std::bind1st(std::mem_fun(&DownloadList::close_quick), m_downloadList));
|
||||
std::for_each(m_downloadList->begin(), m_downloadList->end(), [this](Download* d) { m_downloadList->close_quick(d); });
|
||||
}
|
||||
|
||||
void
|
||||
@@ -222,7 +187,7 @@ Manager::listen_open() {
|
||||
if (portRange.is_string()) {
|
||||
if (std::sscanf(portRange.as_string().c_str(), "%i-%i", &portFirst, &portLast) != 2)
|
||||
throw torrent::input_error("Invalid port_range argument.");
|
||||
|
||||
|
||||
// } else if (portRange.is_list()) {
|
||||
|
||||
} else {
|
||||
@@ -329,12 +294,12 @@ Manager::set_proxy_address(const std::string& addr) {
|
||||
|
||||
if ((err = rak::address_info::get_address_info(buf, PF_INET, SOCK_STREAM, &ai)) != 0)
|
||||
throw torrent::input_error("Could not set proxy address: " + std::string(rak::address_info::strerror(err)) + ".");
|
||||
|
||||
|
||||
try {
|
||||
|
||||
ai->address()->set_port(port);
|
||||
torrent::connection_manager()->set_proxy_address(ai->address()->c_sockaddr());
|
||||
|
||||
|
||||
rak::address_info::free_address_info(ai);
|
||||
|
||||
} catch (torrent::input_error& e) {
|
||||
@@ -366,7 +331,7 @@ Manager::try_create_download(const std::string& uri, int flags, const command_li
|
||||
|
||||
f->set_start(flags & create_start);
|
||||
f->set_print_log(!(flags & create_quiet));
|
||||
f->slot_finished(std::bind(&rak::call_delete_func<core::DownloadFactory>, f));
|
||||
f->slot_finished([f]() { delete f; });
|
||||
|
||||
if (flags & create_raw_data)
|
||||
f->load_raw_data(uri);
|
||||
@@ -390,7 +355,7 @@ Manager::try_create_download_from_meta_download(torrent::Object* bencode, const
|
||||
|
||||
f->set_start(meta.get_key_value("start"));
|
||||
f->set_print_log(meta.get_key_value("print_log"));
|
||||
f->slot_finished(std::bind(&rak::call_delete_func<core::DownloadFactory>, f));
|
||||
f->slot_finished([f]() { delete f; });
|
||||
|
||||
// Bit of a waste to create the bencode repesentation here
|
||||
// only to have the DownloadFactory decode it.
|
||||
@@ -414,7 +379,7 @@ path_expand(std::vector<std::string>* paths, const std::string& pattern) {
|
||||
|
||||
rak::split_iterator_t<std::string> first = rak::split_iterator(pattern, '/');
|
||||
rak::split_iterator_t<std::string> last = rak::split_iterator(pattern);
|
||||
|
||||
|
||||
if (first == last)
|
||||
return;
|
||||
|
||||
@@ -443,9 +408,11 @@ path_expand(std::vector<std::string>* paths, const std::string& pattern) {
|
||||
// Only include filenames starting with '.' if the pattern
|
||||
// starts with the same.
|
||||
itr->update((r.pattern()[0] != '.') ? utils::Directory::update_hide_dot : 0);
|
||||
itr->erase(std::remove_if(itr->begin(), itr->end(), rak::on(rak::mem_ref(&utils::directory_entry::s_name), std::not1(r))), itr->end());
|
||||
itr->erase(std::remove_if(itr->begin(), itr->end(), [r](const utils::directory_entry& entry) { return !r(entry.s_name); }), itr->end());
|
||||
|
||||
std::transform(itr->begin(), itr->end(), std::back_inserter(nextCache), rak::bind1st(std::ptr_fun(&path_expand_transform), itr->path() + (itr->path() == "/" ? "" : "/")));
|
||||
std::transform(itr->begin(), itr->end(), std::back_inserter(nextCache), [itr](const utils::directory_entry& entry) {
|
||||
return path_expand_transform(itr->path() + (itr->path() == "/" ? "" : "/"), entry);
|
||||
});
|
||||
}
|
||||
|
||||
currentCache.clear();
|
||||
|
||||
+32
-30
@@ -5,12 +5,12 @@
|
||||
// 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
|
||||
@@ -39,25 +39,25 @@
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <rak/functional.h>
|
||||
#include <rak/functional_fun.h>
|
||||
#include <torrent/download.h>
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
#include "rpc/parse_commands.h"
|
||||
#include "rpc/object_storage.h"
|
||||
#include "control.h"
|
||||
#include "download.h"
|
||||
#include "download_list.h"
|
||||
#include "manager.h"
|
||||
#include "rpc/object_storage.h"
|
||||
#include "rpc/parse_commands.h"
|
||||
#include "view.h"
|
||||
|
||||
namespace core {
|
||||
|
||||
// Also add focus thingie here?
|
||||
struct view_downloads_compare : std::binary_function<Download*, Download*, bool> {
|
||||
view_downloads_compare(const torrent::Object& cmd) : m_command(cmd) {}
|
||||
view_downloads_compare(const torrent::Object& cmd) :
|
||||
m_command(cmd) {}
|
||||
|
||||
bool operator () (Download* d1, Download* d2) const {
|
||||
bool operator()(Download* d1, Download* d2) const {
|
||||
try {
|
||||
if (m_command.is_empty())
|
||||
return false;
|
||||
@@ -75,8 +75,7 @@ struct view_downloads_compare : std::binary_function<Download*, Download*, bool>
|
||||
// return rpc::commands.call_command(tmp_command.as_dict_key().c_str(), tmp_command.as_dict_obj(),
|
||||
// rpc::make_target_pair(d1, d2)).as_value();
|
||||
|
||||
return rpc::commands.call_command(m_command.as_dict_key().c_str(), m_command.as_dict_obj(),
|
||||
rpc::make_target_pair(d1, d2)).as_value();
|
||||
return rpc::commands.call_command(m_command.as_dict_key().c_str(), m_command.as_dict_obj(), rpc::make_target_pair(d1, d2)).as_value();
|
||||
|
||||
} catch (torrent::input_error& e) {
|
||||
control->core()->push_log(e.what());
|
||||
@@ -89,9 +88,10 @@ struct view_downloads_compare : std::binary_function<Download*, Download*, bool>
|
||||
};
|
||||
|
||||
struct view_downloads_filter : std::unary_function<Download*, bool> {
|
||||
view_downloads_filter(const torrent::Object& cmd, const torrent::Object& cmd2) : m_command(cmd), m_command2(cmd2) {}
|
||||
view_downloads_filter(const torrent::Object& cmd, const torrent::Object& cmd2) :
|
||||
m_command(cmd), m_command2(cmd2) {}
|
||||
|
||||
bool operator () (Download* d1) const {
|
||||
bool operator()(Download* d1) const {
|
||||
return this->evalCmd(m_command, d1) && this->evalCmd(m_command2, d1);
|
||||
}
|
||||
|
||||
@@ -121,11 +121,16 @@ struct view_downloads_filter : std::unary_function<Download*, bool> {
|
||||
|
||||
switch (result.type()) {
|
||||
// case torrent::Object::TYPE_RAW_BENCODE: return !result.as_raw_bencode().empty();
|
||||
case torrent::Object::TYPE_VALUE: return result.as_value();
|
||||
case torrent::Object::TYPE_STRING: return !result.as_string().empty();
|
||||
case torrent::Object::TYPE_LIST: return !result.as_list().empty();
|
||||
case torrent::Object::TYPE_MAP: return !result.as_map().empty();
|
||||
default: return false;
|
||||
case torrent::Object::TYPE_VALUE:
|
||||
return result.as_value();
|
||||
case torrent::Object::TYPE_STRING:
|
||||
return !result.as_string().empty();
|
||||
case torrent::Object::TYPE_LIST:
|
||||
return !result.as_list().empty();
|
||||
case torrent::Object::TYPE_MAP:
|
||||
return !result.as_map().empty();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// The default filter action is to return true, to not filter
|
||||
@@ -139,14 +144,13 @@ struct view_downloads_filter : std::unary_function<Download*, bool> {
|
||||
}
|
||||
}
|
||||
|
||||
const torrent::Object& m_command;
|
||||
const torrent::Object& m_command2;
|
||||
const torrent::Object& m_command;
|
||||
const torrent::Object& m_command2;
|
||||
};
|
||||
|
||||
void
|
||||
View::emit_changed() {
|
||||
priority_queue_erase(&taskScheduler, &m_delayChanged);
|
||||
priority_queue_insert(&taskScheduler, &m_delayChanged, cachedTime);
|
||||
priority_queue_update(&taskScheduler, &m_delayChanged, cachedTime);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -178,7 +182,7 @@ View::initialize(const std::string& name) {
|
||||
// Urgh, wrong. No filtering being done.
|
||||
std::for_each(dlist->begin(), dlist->end(), rak::bind1st(std::mem_fun(&View::push_back), this));
|
||||
|
||||
m_size = base_type::size();
|
||||
m_size = base_type::size();
|
||||
m_focus = 0;
|
||||
|
||||
set_last_changed(rak::timer());
|
||||
@@ -267,12 +271,12 @@ View::filter() {
|
||||
return;
|
||||
|
||||
// Parition the list in two steps so we know which elements changed.
|
||||
iterator splitVisible = std::stable_partition(begin_visible(), end_visible(), view_downloads_filter(m_filter, m_temp_filter));
|
||||
iterator splitFiltered = std::stable_partition(begin_filtered(), end_filtered(), view_downloads_filter(m_filter, m_temp_filter));
|
||||
iterator splitVisible = std::stable_partition(begin_visible(), end_visible(), view_downloads_filter(m_filter, m_temp_filter));
|
||||
iterator splitFiltered = std::stable_partition(begin_filtered(), end_filtered(), view_downloads_filter(m_filter, m_temp_filter));
|
||||
|
||||
base_type changed(splitVisible, splitFiltered);
|
||||
iterator splitChanged = changed.begin() + std::distance(splitVisible, end_visible());
|
||||
|
||||
iterator splitChanged = changed.begin() + std::distance(splitVisible, end_visible());
|
||||
|
||||
m_size = std::distance(begin(), std::copy(splitChanged, changed.end(), splitVisible));
|
||||
std::copy(changed.begin(), splitChanged, begin_filtered());
|
||||
|
||||
@@ -290,12 +294,10 @@ View::filter() {
|
||||
// set the elements to NULL as we trigger commands on them. Or
|
||||
// perhaps always clear them, thus not throwing anything.
|
||||
if (!m_event_removed.is_empty())
|
||||
std::for_each(changed.begin(), splitChanged,
|
||||
std::bind(&rpc::call_object_d_nothrow, m_event_removed, std::placeholders::_1));
|
||||
std::for_each(changed.begin(), splitChanged, std::bind(&rpc::call_object_d_nothrow, m_event_removed, std::placeholders::_1));
|
||||
|
||||
if (!m_event_added.is_empty())
|
||||
std::for_each(changed.begin(), splitChanged,
|
||||
std::bind(&rpc::call_object_d_nothrow, m_event_added, std::placeholders::_1));
|
||||
std::for_each(changed.begin(), splitChanged, std::bind(&rpc::call_object_d_nothrow, m_event_added, std::placeholders::_1));
|
||||
|
||||
emit_changed();
|
||||
}
|
||||
@@ -377,4 +379,4 @@ View::erase_internal(iterator itr) {
|
||||
base_type::erase(itr);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace core
|
||||
|
||||
+70
-67
@@ -5,12 +5,12 @@
|
||||
// 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
|
||||
@@ -64,77 +64,80 @@ class Download;
|
||||
|
||||
class View : private std::vector<Download*> {
|
||||
public:
|
||||
typedef std::vector<Download*> base_type;
|
||||
typedef std::function<void ()> slot_void;
|
||||
typedef std::list<slot_void> signal_void;
|
||||
typedef std::vector<Download*> base_type;
|
||||
typedef std::function<void()> slot_void;
|
||||
typedef std::list<slot_void> signal_void;
|
||||
|
||||
using base_type::iterator;
|
||||
using base_type::const_iterator;
|
||||
using base_type::reverse_iterator;
|
||||
using base_type::const_reverse_iterator;
|
||||
|
||||
using base_type::iterator;
|
||||
using base_type::reverse_iterator;
|
||||
|
||||
using base_type::size_type;
|
||||
|
||||
View() {}
|
||||
~View();
|
||||
|
||||
void initialize(const std::string& name);
|
||||
void initialize(const std::string& name);
|
||||
|
||||
const std::string& name() const { return m_name; }
|
||||
const std::string& name() const { return m_name; }
|
||||
|
||||
bool empty_visible() const { return m_size == 0; }
|
||||
bool empty_visible() const { return m_size == 0; }
|
||||
|
||||
size_type size() const { return m_size; }
|
||||
size_type size_visible() const { return m_size; }
|
||||
size_type size_not_visible() const { return base_type::size() - m_size; }
|
||||
size_type size() const { return m_size; }
|
||||
size_type size_visible() const { return m_size; }
|
||||
size_type size_not_visible() const { return base_type::size() - m_size; }
|
||||
|
||||
// Perhaps this should be renamed?
|
||||
iterator begin_visible() { return begin(); }
|
||||
const_iterator begin_visible() const { return begin(); }
|
||||
iterator begin_visible() { return begin(); }
|
||||
const_iterator begin_visible() const { return begin(); }
|
||||
|
||||
iterator end_visible() { return begin() + m_size; }
|
||||
const_iterator end_visible() const { return begin() + m_size; }
|
||||
iterator end_visible() { return begin() + m_size; }
|
||||
const_iterator end_visible() const { return begin() + m_size; }
|
||||
|
||||
iterator begin_filtered() { return begin() + m_size; }
|
||||
const_iterator begin_filtered() const { return begin() + m_size; }
|
||||
iterator begin_filtered() { return begin() + m_size; }
|
||||
const_iterator begin_filtered() const { return begin() + m_size; }
|
||||
|
||||
iterator end_filtered() { return base_type::end(); }
|
||||
const_iterator end_filtered() const { return base_type::end(); }
|
||||
iterator end_filtered() { return base_type::end(); }
|
||||
const_iterator end_filtered() const { return base_type::end(); }
|
||||
|
||||
iterator focus() { return begin() + m_focus; }
|
||||
const_iterator focus() const { return begin() + m_focus; }
|
||||
void set_focus(iterator itr) { m_focus = position(itr); emit_changed(); }
|
||||
iterator focus() { return begin() + m_focus; }
|
||||
const_iterator focus() const { return begin() + m_focus; }
|
||||
void set_focus(iterator itr) {
|
||||
m_focus = position(itr);
|
||||
emit_changed();
|
||||
}
|
||||
|
||||
void insert(Download* download) { base_type::push_back(download); }
|
||||
void erase(Download* download);
|
||||
void insert(Download* download) { base_type::push_back(download); }
|
||||
void erase(Download* download);
|
||||
|
||||
void set_visible(Download* download);
|
||||
void set_not_visible(Download* download);
|
||||
void set_visible(Download* download);
|
||||
void set_not_visible(Download* download);
|
||||
|
||||
void next_focus();
|
||||
void prev_focus();
|
||||
void next_focus();
|
||||
void prev_focus();
|
||||
|
||||
void sort();
|
||||
void sort();
|
||||
|
||||
void set_sort_new(const torrent::Object& s) { m_sortNew = s; }
|
||||
void set_sort_current(const torrent::Object& s) { m_sortCurrent = s; }
|
||||
void set_sort_new(const torrent::Object& s) { m_sortNew = s; }
|
||||
void set_sort_current(const torrent::Object& s) { m_sortCurrent = s; }
|
||||
|
||||
// Need to explicity trigger filtering.
|
||||
void filter();
|
||||
void filter_by(const torrent::Object& condition, base_type& result);
|
||||
void filter_download(core::Download* download);
|
||||
void filter();
|
||||
void filter_by(const torrent::Object& condition, base_type& result);
|
||||
void filter_download(core::Download* download);
|
||||
|
||||
const torrent::Object& get_filter() const { return m_filter; }
|
||||
void set_filter(const torrent::Object& s) { m_filter = s; }
|
||||
void set_filter(const torrent::Object& s) { m_filter = s; }
|
||||
const torrent::Object& get_filter_temp() const { return m_temp_filter; }
|
||||
void set_filter_temp(const torrent::Object& s) { m_temp_filter = s; }
|
||||
void set_filter_on_event(const std::string& event);
|
||||
void set_filter_temp(const torrent::Object& s) { m_temp_filter = s; }
|
||||
void set_filter_on_event(const std::string& event);
|
||||
|
||||
void clear_filter_on();
|
||||
void clear_filter_on();
|
||||
|
||||
const torrent::Object& event_added() const { return m_event_added; }
|
||||
const torrent::Object& event_removed() const { return m_event_removed; }
|
||||
void set_event_added(const torrent::Object& cmd) { m_event_added = cmd; }
|
||||
const torrent::Object& event_added() const { return m_event_added; }
|
||||
const torrent::Object& event_removed() const { return m_event_removed; }
|
||||
void set_event_added(const torrent::Object& cmd) { m_event_added = cmd; }
|
||||
void set_event_removed(const torrent::Object& cmd) { m_event_removed = cmd; }
|
||||
|
||||
// The time of the last change to the view, semantics of this is
|
||||
@@ -143,50 +146,50 @@ public:
|
||||
//
|
||||
// Currently initialized to rak::timer(), though perhaps we should
|
||||
// use cachedTimer.
|
||||
rak::timer last_changed() const { return m_lastChanged; }
|
||||
void set_last_changed(const rak::timer& t = ::cachedTime) { m_lastChanged = t; }
|
||||
rak::timer last_changed() const { return m_lastChanged; }
|
||||
void set_last_changed(const rak::timer& t = ::cachedTime) { m_lastChanged = t; }
|
||||
|
||||
// Don't connect any slots until after initialize else it get's
|
||||
// triggered when adding the Download's in DownloadList.
|
||||
signal_void& signal_changed() { return m_signal_changed; }
|
||||
signal_void& signal_changed() { return m_signal_changed; }
|
||||
|
||||
private:
|
||||
View(const View&);
|
||||
void operator = (const View&);
|
||||
void operator=(const View&);
|
||||
|
||||
void push_back(Download* d) { base_type::push_back(d); }
|
||||
void push_back(Download* d) { base_type::push_back(d); }
|
||||
|
||||
inline void insert_visible(Download* d);
|
||||
inline void erase_internal(iterator itr);
|
||||
inline void insert_visible(Download* d);
|
||||
inline void erase_internal(iterator itr);
|
||||
|
||||
void emit_changed();
|
||||
void emit_changed_now();
|
||||
void emit_changed();
|
||||
void emit_changed_now();
|
||||
|
||||
size_type position(const_iterator itr) const { return itr - begin(); }
|
||||
size_type position(const_iterator itr) const { return itr - begin(); }
|
||||
|
||||
// An received thing for changed status so we can sort and filter.
|
||||
|
||||
std::string m_name;
|
||||
std::string m_name;
|
||||
|
||||
size_type m_size;
|
||||
size_type m_focus;
|
||||
size_type m_size;
|
||||
size_type m_focus;
|
||||
|
||||
// These should be replaced by a faster non-string command type.
|
||||
torrent::Object m_sortNew;
|
||||
torrent::Object m_sortCurrent;
|
||||
torrent::Object m_sortNew;
|
||||
torrent::Object m_sortCurrent;
|
||||
|
||||
torrent::Object m_filter;
|
||||
torrent::Object m_temp_filter; // Temporary view filter (eg: name based filter)
|
||||
torrent::Object m_filter;
|
||||
torrent::Object m_temp_filter; // Temporary view filter (eg: name based filter)
|
||||
|
||||
torrent::Object m_event_added;
|
||||
torrent::Object m_event_removed;
|
||||
torrent::Object m_event_added;
|
||||
torrent::Object m_event_removed;
|
||||
|
||||
rak::timer m_lastChanged;
|
||||
rak::timer m_lastChanged;
|
||||
|
||||
signal_void m_signal_changed;
|
||||
rak::priority_item m_delayChanged;
|
||||
signal_void m_signal_changed;
|
||||
rak::priority_item m_delayChanged;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace core
|
||||
|
||||
#endif
|
||||
|
||||
@@ -64,8 +64,7 @@ Manager::force_redraw() {
|
||||
|
||||
void
|
||||
Manager::schedule(Window* w, rak::timer t) {
|
||||
rak::priority_queue_erase(&m_scheduler, w->task_update());
|
||||
rak::priority_queue_insert(&m_scheduler, w->task_update(), t);
|
||||
rak::priority_queue_update(&m_scheduler, w->task_update(), t);
|
||||
schedule_update(50000);
|
||||
}
|
||||
|
||||
@@ -114,8 +113,7 @@ Manager::schedule_update(uint32_t minInterval) {
|
||||
}
|
||||
|
||||
if (!m_taskUpdate.is_queued() || m_taskUpdate.time() > m_scheduler.top()->time()) {
|
||||
rak::priority_queue_erase(&taskScheduler, &m_taskUpdate);
|
||||
rak::priority_queue_insert(&taskScheduler, &m_taskUpdate, std::max(m_scheduler.top()->time(), m_timeLastUpdate + minInterval));
|
||||
rak::priority_queue_update(&taskScheduler, &m_taskUpdate, std::max(m_scheduler.top()->time(), m_timeLastUpdate + minInterval));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-42
@@ -1,46 +1,8 @@
|
||||
// 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 <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
#ifndef RTORRENT_RPC_COMMAND_H
|
||||
#define RTORRENT_RPC_COMMAND_H
|
||||
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <torrent/object.h>
|
||||
|
||||
#include <torrent/object.h>
|
||||
#include <torrent/data/file_list_iterator.h>
|
||||
|
||||
@@ -60,7 +22,7 @@ namespace rpc {
|
||||
|
||||
template <typename Target>
|
||||
struct target_wrapper {
|
||||
typedef Target target_type;
|
||||
typedef Target target_type;
|
||||
typedef Target cleaned_type;
|
||||
};
|
||||
|
||||
@@ -68,7 +30,7 @@ template <>
|
||||
struct target_wrapper<void> {
|
||||
struct no_type {};
|
||||
|
||||
typedef void target_type;
|
||||
typedef void target_type;
|
||||
typedef no_type* cleaned_type;
|
||||
};
|
||||
|
||||
@@ -146,10 +108,10 @@ public:
|
||||
struct stack_type {
|
||||
torrent::Object* begin() { return reinterpret_cast<torrent::Object*>(buffer); }
|
||||
torrent::Object* end() { return reinterpret_cast<torrent::Object*>(buffer) + max_arguments; }
|
||||
|
||||
|
||||
const torrent::Object* begin() const { return reinterpret_cast<const torrent::Object*>(buffer); }
|
||||
const torrent::Object* end() const { return reinterpret_cast<const torrent::Object*>(buffer) + max_arguments; }
|
||||
|
||||
|
||||
torrent::Object& operator [] (unsigned int idx) { return *(begin() + idx); }
|
||||
const torrent::Object& operator [] (unsigned int idx) const { return *(begin() + idx); }
|
||||
|
||||
|
||||
+3
-37
@@ -1,42 +1,8 @@
|
||||
// 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 <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
#ifndef RTORRENT_RPC_COMMAND_IMPL_H
|
||||
#define RTORRENT_RPC_COMMAND_IMPL_H
|
||||
|
||||
#include "rpc/command.h"
|
||||
|
||||
namespace rpc {
|
||||
|
||||
//template <> struct target_type_id<command_base::generic_slot> { static const int value = command_base::target_generic; };
|
||||
@@ -63,7 +29,7 @@ template <> struct target_type_id<core::Download*, core::Download*> { static con
|
||||
template <> inline bool
|
||||
is_target_compatible<target_type>(const target_type& target) { return true; }
|
||||
template <> inline bool
|
||||
is_target_compatible<torrent::File*>(const target_type& target) { return target.first == command_base::target_file || command_base::target_file_itr; }
|
||||
is_target_compatible<torrent::File*>(const target_type& target) { return target.first == command_base::target_file || target.first == command_base::target_file_itr; }
|
||||
|
||||
template <> inline target_type
|
||||
get_target_cast<target_type>(target_type target, int type) { return target; }
|
||||
|
||||
+8
-41
@@ -36,7 +36,6 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <vector>
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/object.h>
|
||||
#include <torrent/data/file_list_iterator.h>
|
||||
@@ -57,23 +56,8 @@ namespace rpc {
|
||||
|
||||
command_base::stack_type command_base::current_stack;
|
||||
|
||||
CommandMap::~CommandMap() {
|
||||
std::vector<const char*> keys;
|
||||
|
||||
for (iterator itr = base_type::begin(), last = base_type::end(); itr != last; itr++) {
|
||||
// if (!(itr->second.m_flags & flag_dont_delete))
|
||||
// delete itr->second.m_variable;
|
||||
|
||||
if (itr->second.m_flags & flag_delete_key)
|
||||
keys.push_back(itr->first);
|
||||
}
|
||||
|
||||
for (std::vector<const char*>::iterator itr = keys.begin(), last = keys.end(); itr != last; itr++)
|
||||
delete [] *itr;
|
||||
}
|
||||
|
||||
CommandMap::iterator
|
||||
CommandMap::insert(key_type key, int flags, const char* parm, const char* doc) {
|
||||
CommandMap::insert(const key_type& key, int flags, const char* parm, const char* doc) {
|
||||
iterator itr = base_type::find(key);
|
||||
|
||||
if (itr != base_type::end())
|
||||
@@ -81,25 +65,11 @@ CommandMap::insert(key_type key, int flags, const char* parm, const char* doc) {
|
||||
|
||||
// TODO: This is not honoring the public_xmlrpc flags!!!
|
||||
if (rpc::xmlrpc.is_valid() && (flags & flag_public_xmlrpc))
|
||||
// if (rpc::xmlrpc.is_valid())
|
||||
rpc::xmlrpc.insert_command(key, parm, doc);
|
||||
rpc::xmlrpc.insert_command(key.c_str(), parm, doc);
|
||||
|
||||
return base_type::insert(itr, value_type(key, command_map_data_type(flags, parm, doc)));
|
||||
}
|
||||
|
||||
// void
|
||||
// CommandMap::insert(key_type key, const command_map_data_type src) {
|
||||
// iterator itr = base_type::find(key);
|
||||
|
||||
// if (itr != base_type::end())
|
||||
// throw torrent::internal_error("CommandMap::insert(...) tried to insert an already existing key.");
|
||||
|
||||
// itr = base_type::insert(itr, value_type(key, command_map_data_type(src.m_variable, src.m_flags | flag_dont_delete, src.m_parm, src.m_doc)));
|
||||
|
||||
// // We can assume all the slots are the same size.
|
||||
// itr->second.m_anySlot = src.m_anySlot;
|
||||
// }
|
||||
|
||||
void
|
||||
CommandMap::erase(iterator itr) {
|
||||
if (itr == end())
|
||||
@@ -112,14 +82,11 @@ CommandMap::erase(iterator itr) {
|
||||
// if (!(itr->second.m_flags & flag_dont_delete))
|
||||
// delete itr->second.m_variable;
|
||||
|
||||
const char* key = itr->second.m_flags & flag_delete_key ? itr->first : NULL;
|
||||
|
||||
base_type::erase(itr);
|
||||
delete [] key;
|
||||
}
|
||||
|
||||
void
|
||||
CommandMap::create_redirect(key_type key_new, key_type key_dest, int flags) {
|
||||
CommandMap::create_redirect(const key_type& key_new, const key_type& key_dest, int flags) {
|
||||
iterator new_itr = base_type::find(key_new);
|
||||
iterator dest_itr = base_type::find(key_dest);
|
||||
|
||||
@@ -135,11 +102,11 @@ CommandMap::create_redirect(key_type key_new, key_type key_dest, int flags) {
|
||||
|
||||
dest_itr->second.m_flags |= flag_has_redirects;
|
||||
|
||||
flags |= dest_itr->second.m_flags & ~(flag_delete_key | flag_has_redirects | flag_public_xmlrpc);
|
||||
flags |= dest_itr->second.m_flags & ~(flag_has_redirects | flag_public_xmlrpc);
|
||||
|
||||
// TODO: This is not honoring the public_xmlrpc flags!!!
|
||||
if (rpc::xmlrpc.is_valid() && (flags & flag_public_xmlrpc))
|
||||
rpc::xmlrpc.insert_command(key_new, dest_itr->second.m_parm, dest_itr->second.m_doc);
|
||||
rpc::xmlrpc.insert_command(key_new.c_str(), dest_itr->second.m_parm, dest_itr->second.m_doc);
|
||||
|
||||
iterator itr = base_type::insert(base_type::end(),
|
||||
value_type(key_new, command_map_data_type(flags,
|
||||
@@ -152,7 +119,7 @@ CommandMap::create_redirect(key_type key_new, key_type key_dest, int flags) {
|
||||
}
|
||||
|
||||
const CommandMap::mapped_type
|
||||
CommandMap::call_catch(key_type key, target_type target, const mapped_type& args, const char* err) {
|
||||
CommandMap::call_catch(const key_type& key, const target_type& target, const mapped_type& args, const char* err) {
|
||||
try {
|
||||
return call_command(key, args, target);
|
||||
} catch (torrent::input_error& e) {
|
||||
@@ -162,7 +129,7 @@ CommandMap::call_catch(key_type key, target_type target, const mapped_type& args
|
||||
}
|
||||
|
||||
const CommandMap::mapped_type
|
||||
CommandMap::call_command(key_type key, const mapped_type& arg, target_type target) {
|
||||
CommandMap::call_command(const key_type& key, const mapped_type& arg, const target_type& target) {
|
||||
iterator itr = base_type::find(key);
|
||||
|
||||
if (itr == base_type::end())
|
||||
@@ -172,7 +139,7 @@ CommandMap::call_command(key_type key, const mapped_type& arg, target_type targe
|
||||
}
|
||||
|
||||
const CommandMap::mapped_type
|
||||
CommandMap::call_command(iterator itr, const mapped_type& arg, target_type target) {
|
||||
CommandMap::call_command(iterator itr, const mapped_type& arg, const target_type& target) {
|
||||
return itr->second.m_anySlot(&itr->second.m_variable, target, arg);
|
||||
}
|
||||
|
||||
|
||||
+17
-25
@@ -46,10 +46,6 @@
|
||||
|
||||
namespace rpc {
|
||||
|
||||
struct command_map_comp : public std::binary_function<const char*, const char*, bool> {
|
||||
bool operator () (const char* arg1, const char* arg2) const { return std::strcmp(arg1, arg2) < 0; }
|
||||
};
|
||||
|
||||
struct command_map_data_type {
|
||||
// Some commands will need to share data, like get/set a variable. So
|
||||
// instead of using a single virtual member function, each command
|
||||
@@ -73,9 +69,9 @@ struct command_map_data_type {
|
||||
const char* m_doc;
|
||||
};
|
||||
|
||||
class CommandMap : public std::map<const char*, command_map_data_type, command_map_comp> {
|
||||
class CommandMap : public std::map<std::string, command_map_data_type> {
|
||||
public:
|
||||
typedef std::map<const char*, command_map_data_type, command_map_comp> base_type;
|
||||
typedef std::map<std::string, command_map_data_type> base_type;
|
||||
|
||||
typedef torrent::Object mapped_type;
|
||||
typedef mapped_type::value_type mapped_value_type;
|
||||
@@ -90,7 +86,6 @@ public:
|
||||
using base_type::find;
|
||||
|
||||
static const int flag_dont_delete = 0x1;
|
||||
static const int flag_delete_key = 0x2;
|
||||
static const int flag_public_xmlrpc = 0x4;
|
||||
static const int flag_modifiable = 0x10;
|
||||
static const int flag_is_redirect = 0x20;
|
||||
@@ -101,39 +96,36 @@ public:
|
||||
static const int flag_tracker_target = 0x400;
|
||||
|
||||
CommandMap() {}
|
||||
~CommandMap();
|
||||
|
||||
bool has(const char* key) const { return base_type::find(key) != base_type::end(); }
|
||||
bool has(const std::string& key) const { return has(key.c_str()); }
|
||||
bool has(const std::string& key) const { return base_type::find(key) != base_type::end(); }
|
||||
|
||||
bool is_modifiable(const_iterator itr) { return itr != end() && (itr->second.m_flags & flag_modifiable); }
|
||||
|
||||
iterator insert(key_type key, int flags, const char* parm, const char* doc);
|
||||
iterator insert(const key_type& key, int flags, const char* parm, const char* doc);
|
||||
|
||||
template <typename T, typename Slot>
|
||||
void
|
||||
insert_slot(key_type key, Slot variable, command_base::any_slot targetSlot, int flags, const char* parm, const char* doc) {
|
||||
insert_slot(const key_type& key, Slot variable, command_base::any_slot target_slot, int flags, const char* parm, const char* doc) {
|
||||
iterator itr = insert(key, flags, parm, doc);
|
||||
itr->second.m_variable.set_function<T>(variable);
|
||||
itr->second.m_anySlot = targetSlot;
|
||||
itr->second.m_anySlot = target_slot;
|
||||
}
|
||||
|
||||
// void insert(key_type key, const command_map_data_type src);
|
||||
void erase(iterator itr);
|
||||
|
||||
void create_redirect(key_type key_new, key_type key_dest, int flags);
|
||||
void create_redirect(const key_type& key_new, const key_type& key_dest, int flags);
|
||||
|
||||
const mapped_type call(key_type key, const mapped_type& args = mapped_type());
|
||||
const mapped_type call(key_type key, target_type target, const mapped_type& args = mapped_type()) { return call_command(key, args, target); }
|
||||
const mapped_type call_catch(key_type key, target_type target, const mapped_type& args = mapped_type(), const char* err = "Command failed: ");
|
||||
const mapped_type call(const key_type& key, const mapped_type& args = mapped_type());
|
||||
const mapped_type call(const key_type& key, const target_type& target, const mapped_type& args = mapped_type()) { return call_command(key, args, target); }
|
||||
const mapped_type call_catch(const key_type& key, const target_type& target, const mapped_type& args = mapped_type(), const char* err = "Command failed: ");
|
||||
|
||||
const mapped_type call_command (key_type key, const mapped_type& arg, target_type target = target_type((int)command_base::target_generic, NULL));
|
||||
const mapped_type call_command (iterator itr, const mapped_type& arg, target_type target = target_type((int)command_base::target_generic, NULL));
|
||||
const mapped_type call_command (const key_type& key, const mapped_type& arg, const target_type& target = target_type((int)command_base::target_generic, NULL));
|
||||
const mapped_type call_command (iterator itr, const mapped_type& arg, const target_type& target = target_type((int)command_base::target_generic, NULL));
|
||||
|
||||
const mapped_type call_command_d(key_type key, core::Download* download, const mapped_type& arg) { return call_command(key, arg, target_type((int)command_base::target_download, download)); }
|
||||
const mapped_type call_command_p(key_type key, torrent::Peer* peer, const mapped_type& arg) { return call_command(key, arg, target_type((int)command_base::target_peer, peer)); }
|
||||
const mapped_type call_command_t(key_type key, torrent::Tracker* tracker, const mapped_type& arg) { return call_command(key, arg, target_type((int)command_base::target_tracker, tracker)); }
|
||||
const mapped_type call_command_f(key_type key, torrent::File* file, const mapped_type& arg) { return call_command(key, arg, target_type((int)command_base::target_file, file)); }
|
||||
const mapped_type call_command_d(const key_type& key, core::Download* download, const mapped_type& arg) { return call_command(key, arg, target_type((int)command_base::target_download, download)); }
|
||||
const mapped_type call_command_p(const key_type& key, torrent::Peer* peer, const mapped_type& arg) { return call_command(key, arg, target_type((int)command_base::target_peer, peer)); }
|
||||
const mapped_type call_command_t(const key_type& key, torrent::Tracker* tracker, const mapped_type& arg) { return call_command(key, arg, target_type((int)command_base::target_tracker, tracker)); }
|
||||
const mapped_type call_command_f(const key_type& key, torrent::File* file, const mapped_type& arg) { return call_command(key, arg, target_type((int)command_base::target_file, file)); }
|
||||
|
||||
private:
|
||||
CommandMap(const CommandMap&);
|
||||
@@ -175,7 +167,7 @@ create_object_list(const torrent::Object& o1, const torrent::Object& o2, const t
|
||||
}
|
||||
|
||||
inline const CommandMap::mapped_type
|
||||
CommandMap::call(key_type key, const mapped_type& args) {
|
||||
CommandMap::call(const key_type& key, const mapped_type& args) {
|
||||
return call_command(key, args, make_target());
|
||||
}
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ CommandScheduler::call_item(value_type item) {
|
||||
rpc::call_object(item->command());
|
||||
|
||||
} catch (torrent::input_error& e) {
|
||||
if (m_slotErrorMessage.is_valid())
|
||||
if (m_slotErrorMessage)
|
||||
m_slotErrorMessage("Scheduled command failed: " + item->key() + ": " + e.what());
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <rak/functional_fun.h>
|
||||
|
||||
namespace torrent {
|
||||
class Object;
|
||||
@@ -52,7 +51,7 @@ class CommandSchedulerItem;
|
||||
|
||||
class CommandScheduler : public std::vector<CommandSchedulerItem*> {
|
||||
public:
|
||||
typedef rak::function1<void, const std::string&> SlotString;
|
||||
typedef std::function<void (const std::string&)> SlotString;
|
||||
typedef std::pair<int, int> Time;
|
||||
typedef std::vector<CommandSchedulerItem*> base_type;
|
||||
|
||||
@@ -63,7 +62,7 @@ public:
|
||||
CommandScheduler() {}
|
||||
~CommandScheduler();
|
||||
|
||||
void set_slot_error_message(SlotString::base_type* s) { m_slotErrorMessage.set(s); }
|
||||
void set_slot_error_message(SlotString s) { m_slotErrorMessage = s; }
|
||||
|
||||
// slot_error_message or something.
|
||||
|
||||
|
||||
+1
-4
@@ -166,13 +166,10 @@ SCgi::event_error() {
|
||||
|
||||
bool
|
||||
SCgi::receive_call(SCgiTask* task, const char* buffer, uint32_t length) {
|
||||
slot_write slotWrite;
|
||||
slotWrite.set(rak::mem_fn(task, &SCgiTask::receive_write));
|
||||
|
||||
torrent::thread_base::acquire_global_lock();
|
||||
torrent::main_thread()->interrupt();
|
||||
|
||||
bool result = xmlrpc.process(buffer, length, slotWrite);
|
||||
bool result = xmlrpc.process(buffer, length, [task](const char* b, uint32_t l) { return task->receive_write(b, l); });
|
||||
|
||||
torrent::thread_base::release_global_lock();
|
||||
|
||||
|
||||
+2
-3
@@ -37,8 +37,7 @@
|
||||
#ifndef RTORRENT_RPC_SCGI_H
|
||||
#define RTORRENT_RPC_SCGI_H
|
||||
|
||||
#include <string>
|
||||
#include <rak/functional_fun.h>
|
||||
#include <functional>
|
||||
#include <torrent/event.h>
|
||||
|
||||
#include "scgi_task.h"
|
||||
@@ -51,7 +50,7 @@ namespace rpc {
|
||||
|
||||
class lt_cacheline_aligned SCgi : public torrent::Event {
|
||||
public:
|
||||
typedef rak::function2<bool, const char*, uint32_t> slot_write;
|
||||
typedef std::function<bool (const char*, uint32_t)> slot_write;
|
||||
|
||||
static const int max_tasks = 100;
|
||||
|
||||
|
||||
@@ -200,7 +200,13 @@ SCgiTask::event_read() {
|
||||
|
||||
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_bufferSize, 0);
|
||||
#else
|
||||
int bytes = ::send(m_fileDesc, m_position, m_bufferSize, MSG_NOSIGNAL);
|
||||
#endif
|
||||
|
||||
if (bytes == -1) {
|
||||
if (!rak::error_number::current().is_blocked_momentary())
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+77
-548
@@ -36,578 +36,104 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_XMLRPC_C
|
||||
#include <stdlib.h>
|
||||
#include <xmlrpc-c/server.h>
|
||||
#endif
|
||||
|
||||
#include <cctype>
|
||||
#include <rak/string_manip.h>
|
||||
#include <torrent/object.h>
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
#include "xmlrpc.h"
|
||||
|
||||
#include "parse_commands.h"
|
||||
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
namespace rpc {
|
||||
|
||||
#ifdef HAVE_XMLRPC_C
|
||||
|
||||
class xmlrpc_error : public torrent::base_error {
|
||||
public:
|
||||
xmlrpc_error(xmlrpc_env* env) : m_type(env->fault_code), m_msg(env->fault_string) {}
|
||||
xmlrpc_error(int type, const char* msg) : m_type(type), m_msg(msg) {}
|
||||
xmlrpc_error(int type, std::string msg) : m_type(type), m_msg(msg) {}
|
||||
virtual ~xmlrpc_error() throw() {}
|
||||
|
||||
virtual int type() const throw() { return m_type; }
|
||||
virtual const char* what() const throw() { return m_msg; }
|
||||
virtual const char* what() const throw() { return m_msg.c_str(); }
|
||||
|
||||
private:
|
||||
int m_type;
|
||||
const char* m_msg;
|
||||
std::string m_msg;
|
||||
};
|
||||
|
||||
torrent::Object xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value, int callType = 0, rpc::target_type* target = NULL);
|
||||
|
||||
inline torrent::Object
|
||||
xmlrpc_list_entry_to_object(xmlrpc_env* env, xmlrpc_value* src, int index) {
|
||||
xmlrpc_value* tmp;
|
||||
xmlrpc_array_read_item(env, src, index, &tmp);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error(env);
|
||||
|
||||
torrent::Object obj = xmlrpc_to_object(env, tmp);
|
||||
xmlrpc_DECREF(tmp);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
int64_t
|
||||
xmlrpc_list_entry_to_value(xmlrpc_env* env, xmlrpc_value* src, int index) {
|
||||
xmlrpc_value* tmp;
|
||||
xmlrpc_array_read_item(env, src, index, &tmp);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error(env);
|
||||
|
||||
switch (xmlrpc_value_type(tmp)) {
|
||||
case XMLRPC_TYPE_INT:
|
||||
int v;
|
||||
xmlrpc_read_int(env, tmp, &v);
|
||||
xmlrpc_DECREF(tmp);
|
||||
return v;
|
||||
|
||||
#ifdef XMLRPC_HAVE_I8
|
||||
case XMLRPC_TYPE_I8:
|
||||
xmlrpc_int64 v2;
|
||||
xmlrpc_read_i8(env, tmp, &v2);
|
||||
xmlrpc_DECREF(tmp);
|
||||
return v2;
|
||||
#endif
|
||||
|
||||
case XMLRPC_TYPE_STRING:
|
||||
{
|
||||
const char* str;
|
||||
xmlrpc_read_string(env, tmp, &str);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error(env);
|
||||
|
||||
const char* end = str;
|
||||
int64_t v3 = ::strtoll(str, (char**)&end, 0);
|
||||
|
||||
::free((void*)str);
|
||||
|
||||
if (*str == '\0' || *end != '\0')
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "Invalid index.");
|
||||
|
||||
return v3;
|
||||
}
|
||||
|
||||
default:
|
||||
xmlrpc_DECREF(tmp);
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "Invalid type found.");
|
||||
}
|
||||
}
|
||||
|
||||
// Consider making a helper function that creates a target_type from a
|
||||
// torrent::Object, then we can just use xmlrpc_to_object.
|
||||
rpc::target_type
|
||||
xmlrpc_to_target(xmlrpc_env* env, xmlrpc_value* value) {
|
||||
rpc::target_type target;
|
||||
|
||||
switch (xmlrpc_value_type(value)) {
|
||||
case XMLRPC_TYPE_STRING:
|
||||
{
|
||||
const char* str;
|
||||
xmlrpc_read_string(env, value, &str);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error(env);
|
||||
|
||||
if (std::strlen(str) == 0) {
|
||||
// When specifying void, we require a zero-length string.
|
||||
::free((void*)str);
|
||||
return rpc::make_target();
|
||||
|
||||
} else if (std::strlen(str) < 40) {
|
||||
::free((void*)str);
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "Unsupported target type found.");
|
||||
}
|
||||
|
||||
core::Download* download = xmlrpc.slot_find_download()(str);
|
||||
|
||||
if (download == NULL) {
|
||||
::free((void*)str);
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "Could not find info-hash.");
|
||||
}
|
||||
|
||||
if (std::strlen(str) == 40) {
|
||||
::free((void*)str);
|
||||
return rpc::make_target(download);
|
||||
}
|
||||
|
||||
if (std::strlen(str) < 42 || str[40] != ':') {
|
||||
::free((void*)str);
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "Unsupported target type found.");
|
||||
}
|
||||
|
||||
// Files: "<hash>:f<index>"
|
||||
// Trackers: "<hash>:t<index>"
|
||||
|
||||
int index;
|
||||
const char* end_ptr = str + 42;
|
||||
|
||||
switch (str[41]) {
|
||||
case 'f':
|
||||
index = ::strtol(str + 42, (char**)&end_ptr, 0);
|
||||
|
||||
if (*str == '\0' || *end_ptr != '\0')
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "Invalid index.");
|
||||
|
||||
target = rpc::make_target(XmlRpc::call_file, xmlrpc.slot_find_file()(download, index));
|
||||
break;
|
||||
|
||||
case 't':
|
||||
index = ::strtol(str + 42, (char**)&end_ptr, 0);
|
||||
|
||||
if (*str == '\0' || *end_ptr != '\0')
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "Invalid index.");
|
||||
|
||||
target = rpc::make_target(XmlRpc::call_tracker, xmlrpc.slot_find_tracker()(download, index));
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
{
|
||||
torrent::HashString hash;
|
||||
const char* hash_end = torrent::hash_string_from_hex_c_str(str + 42, hash);
|
||||
|
||||
if (hash_end == end_ptr || *hash_end != '\0')
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "Not a hash string.");
|
||||
|
||||
target = rpc::make_target(XmlRpc::call_peer, xmlrpc.slot_find_peer()(download, hash));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
::free((void*)str);
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "Unsupported target type found.");
|
||||
}
|
||||
|
||||
::free((void*)str);
|
||||
|
||||
// Check if the target pointer is NULL.
|
||||
if (target.second == NULL)
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "Invalid index.");
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
default:
|
||||
return rpc::make_target();
|
||||
}
|
||||
}
|
||||
|
||||
rpc::target_type
|
||||
xmlrpc_to_index_type(int index, int callType, core::Download* download) {
|
||||
void* result;
|
||||
|
||||
switch (callType) {
|
||||
case XmlRpc::call_file: result = xmlrpc.slot_find_file()(download, index); break;
|
||||
case XmlRpc::call_tracker: result = xmlrpc.slot_find_tracker()(download, index); break;
|
||||
default: result = NULL; break;
|
||||
}
|
||||
|
||||
if (result == NULL)
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "Invalid index.");
|
||||
|
||||
return rpc::make_target(callType, result);
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value, int callType, rpc::target_type* target) {
|
||||
switch (xmlrpc_value_type(value)) {
|
||||
case XMLRPC_TYPE_INT:
|
||||
int v;
|
||||
xmlrpc_read_int(env, value, &v);
|
||||
|
||||
return torrent::Object((int64_t)v);
|
||||
|
||||
#ifdef XMLRPC_HAVE_I8
|
||||
case XMLRPC_TYPE_I8:
|
||||
xmlrpc_int64 v2;
|
||||
xmlrpc_read_i8(env, value, &v2);
|
||||
|
||||
return torrent::Object((int64_t)v2);
|
||||
#endif
|
||||
|
||||
// case XMLRPC_TYPE_BOOL:
|
||||
// case XMLRPC_TYPE_DOUBLE:
|
||||
// case XMLRPC_TYPE_DATETIME:
|
||||
|
||||
case XMLRPC_TYPE_STRING:
|
||||
|
||||
if (callType != XmlRpc::call_generic) {
|
||||
// When the call type is not supposed to be void, we'll try to
|
||||
// convert it to a command target. It's not that important that
|
||||
// it is converted to the right type here, as an mismatch will
|
||||
// be caught when executing the command.
|
||||
*target = xmlrpc_to_target(env, value);
|
||||
return torrent::Object();
|
||||
|
||||
} else {
|
||||
const char* valueString;
|
||||
xmlrpc_read_string(env, value, &valueString);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error(env);
|
||||
|
||||
torrent::Object result = torrent::Object(std::string(valueString));
|
||||
|
||||
// Urgh, seriously?
|
||||
::free((void*)valueString);
|
||||
return result;
|
||||
}
|
||||
|
||||
case XMLRPC_TYPE_BASE64:
|
||||
{
|
||||
size_t valueSize;
|
||||
const char* valueString;
|
||||
|
||||
xmlrpc_read_base64(env, value, &valueSize, (const unsigned char**)&valueString);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error(env);
|
||||
|
||||
torrent::Object result = torrent::Object(std::string(valueString, valueSize));
|
||||
|
||||
// Urgh, seriously?
|
||||
::free((void*)valueString);
|
||||
return result;
|
||||
}
|
||||
|
||||
case XMLRPC_TYPE_ARRAY:
|
||||
{
|
||||
unsigned int current = 0;
|
||||
unsigned int last = xmlrpc_array_size(env, value);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error(env);
|
||||
|
||||
if (callType != XmlRpc::call_generic && last != 0) {
|
||||
if (last < 1)
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "Too few arguments.");
|
||||
|
||||
xmlrpc_value* tmp;
|
||||
xmlrpc_array_read_item(env, value, current++, &tmp);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error(env);
|
||||
|
||||
*target = xmlrpc_to_target(env, tmp);
|
||||
xmlrpc_DECREF(tmp);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error(env);
|
||||
|
||||
if (target->first == XmlRpc::call_download &&
|
||||
(callType == XmlRpc::call_file || callType == XmlRpc::call_tracker)) {
|
||||
// If we have a download target and the call type requires
|
||||
// another contained type, then we try to use the next
|
||||
// parameter as the index to support old-style calls.
|
||||
|
||||
if (current == last)
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "Too few arguments, missing index.");
|
||||
|
||||
*target = xmlrpc_to_index_type(xmlrpc_list_entry_to_value(env, value, current++), callType, (core::Download*)target->second);
|
||||
}
|
||||
}
|
||||
|
||||
if (current + 1 < last) {
|
||||
torrent::Object result = torrent::Object::create_list();
|
||||
torrent::Object::list_type& listRef = result.as_list();
|
||||
|
||||
while (current != last)
|
||||
listRef.push_back(xmlrpc_list_entry_to_object(env, value, current++));
|
||||
|
||||
return result;
|
||||
|
||||
} else if (current + 1 == last) {
|
||||
return xmlrpc_list_entry_to_object(env, value, current);
|
||||
|
||||
} else {
|
||||
return torrent::Object();
|
||||
}
|
||||
}
|
||||
|
||||
// case XMLRPC_TYPE_STRUCT:
|
||||
// case XMLRPC_TYPE_C_PTR:
|
||||
// case XMLRPC_TYPE_NIL:
|
||||
// case XMLRPC_TYPE_DEAD:
|
||||
default:
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "Unsupported type found.");
|
||||
}
|
||||
}
|
||||
|
||||
xmlrpc_value*
|
||||
object_to_xmlrpc(xmlrpc_env* env, const torrent::Object& object) {
|
||||
switch (object.type()) {
|
||||
case torrent::Object::TYPE_VALUE:
|
||||
|
||||
#ifdef XMLRPC_HAVE_I8
|
||||
if (xmlrpc.dialect() != XmlRpc::dialect_generic)
|
||||
return xmlrpc_i8_new(env, object.as_value());
|
||||
#else
|
||||
return xmlrpc_int_new(env, object.as_value());
|
||||
#endif
|
||||
|
||||
case torrent::Object::TYPE_STRING:
|
||||
{
|
||||
#ifdef XMLRPC_HAVE_I8
|
||||
// The versions that support I8 do implicit utf-8 validation.
|
||||
xmlrpc_value* result = xmlrpc_string_new(env, object.as_string().c_str());
|
||||
#else
|
||||
// In older versions, xmlrpc-c doesn't validate the utf-8 encoding itself.
|
||||
xmlrpc_validate_utf8(env, object.as_string().c_str(), object.as_string().length());
|
||||
|
||||
xmlrpc_value* result = env->fault_occurred ? NULL : xmlrpc_string_new(env, object.as_string().c_str());
|
||||
#endif
|
||||
|
||||
if (env->fault_occurred) {
|
||||
xmlrpc_env_clean(env);
|
||||
xmlrpc_env_init(env);
|
||||
|
||||
const std::string& str = object.as_string();
|
||||
char buffer[str.size() + 1];
|
||||
char* dst = buffer;
|
||||
for (std::string::const_iterator itr = str.begin(); itr != str.end(); ++itr)
|
||||
*dst++ = ((*itr < 0x20 && *itr != '\r' && *itr != '\n' && *itr != '\t') || (*itr & 0x80)) ? '?' : *itr;
|
||||
*dst = 0;
|
||||
|
||||
result = xmlrpc_string_new(env, buffer);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
case torrent::Object::TYPE_LIST:
|
||||
{
|
||||
xmlrpc_value* result = xmlrpc_array_new(env);
|
||||
|
||||
for (torrent::Object::list_const_iterator itr = object.as_list().begin(), last = object.as_list().end(); itr != last; itr++) {
|
||||
xmlrpc_value* item = object_to_xmlrpc(env, *itr);
|
||||
xmlrpc_array_append_item(env, result, item);
|
||||
xmlrpc_DECREF(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
case torrent::Object::TYPE_MAP:
|
||||
{
|
||||
xmlrpc_value* result = xmlrpc_struct_new(env);
|
||||
|
||||
for (torrent::Object::map_const_iterator itr = object.as_map().begin(), last = object.as_map().end(); itr != last; itr++) {
|
||||
xmlrpc_value* item = object_to_xmlrpc(env, itr->second);
|
||||
xmlrpc_struct_set_value(env, result, itr->first.c_str(), item);
|
||||
xmlrpc_DECREF(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
case torrent::Object::TYPE_DICT_KEY:
|
||||
{
|
||||
xmlrpc_value* result = xmlrpc_array_new(env);
|
||||
|
||||
xmlrpc_value* key_item = object_to_xmlrpc(env, object.as_dict_key());
|
||||
xmlrpc_array_append_item(env, result, key_item);
|
||||
xmlrpc_DECREF(key_item);
|
||||
|
||||
if (object.as_dict_obj().is_list()) {
|
||||
for (torrent::Object::list_const_iterator
|
||||
itr = object.as_dict_obj().as_list().begin(),
|
||||
last = object.as_dict_obj().as_list().end();
|
||||
itr != last; itr++) {
|
||||
xmlrpc_value* item = object_to_xmlrpc(env, *itr);
|
||||
xmlrpc_array_append_item(env, result, item);
|
||||
xmlrpc_DECREF(item);
|
||||
}
|
||||
} else {
|
||||
xmlrpc_value* arg_item = object_to_xmlrpc(env, object.as_dict_obj());
|
||||
xmlrpc_array_append_item(env, result, arg_item);
|
||||
xmlrpc_DECREF(arg_item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
default:
|
||||
return xmlrpc_int_new(env, 0);
|
||||
}
|
||||
}
|
||||
|
||||
xmlrpc_value*
|
||||
xmlrpc_call_command(xmlrpc_env* env, xmlrpc_value* args, void* voidServerInfo) {
|
||||
CommandMap::iterator itr = commands.find((const char*)voidServerInfo);
|
||||
|
||||
if (itr == commands.end()) {
|
||||
xmlrpc_env_set_fault(env, XMLRPC_PARSE_ERROR, ("Command \"" + std::string((const char*)voidServerInfo) + "\" does not exist.").c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
try {
|
||||
torrent::Object object;
|
||||
rpc::target_type target = rpc::make_target();
|
||||
|
||||
if (itr->second.m_flags & CommandMap::flag_no_target)
|
||||
xmlrpc_to_object(env, args, XmlRpc::call_generic, &target).swap(object);
|
||||
else if (itr->second.m_flags & CommandMap::flag_file_target)
|
||||
xmlrpc_to_object(env, args, XmlRpc::call_file, &target).swap(object);
|
||||
else if (itr->second.m_flags & CommandMap::flag_tracker_target)
|
||||
xmlrpc_to_object(env, args, XmlRpc::call_tracker, &target).swap(object);
|
||||
else
|
||||
xmlrpc_to_object(env, args, XmlRpc::call_any, &target).swap(object);
|
||||
|
||||
if (env->fault_occurred)
|
||||
return NULL;
|
||||
|
||||
return object_to_xmlrpc(env, rpc::commands.call_command(itr, object, target));
|
||||
|
||||
} catch (xmlrpc_error& e) {
|
||||
xmlrpc_env_set_fault(env, e.type(), e.what());
|
||||
return NULL;
|
||||
|
||||
} catch (torrent::local_error& e) {
|
||||
xmlrpc_env_set_fault(env, XMLRPC_PARSE_ERROR, e.what());
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
XmlRpc::initialize() {
|
||||
#ifndef XMLRPC_HAVE_I8
|
||||
m_dialect = dialect_generic;
|
||||
#endif
|
||||
|
||||
m_env = new xmlrpc_env;
|
||||
|
||||
xmlrpc_env_init((xmlrpc_env*)m_env);
|
||||
m_registry = xmlrpc_registry_new((xmlrpc_env*)m_env);
|
||||
}
|
||||
|
||||
void
|
||||
XmlRpc::cleanup() {
|
||||
if (!is_valid())
|
||||
XmlRpc::object_to_target(const torrent::Object& obj, int callFlags, rpc::target_type* target) {
|
||||
if (callFlags & CommandMap::flag_no_target)
|
||||
return;
|
||||
|
||||
xmlrpc_registry_free((xmlrpc_registry*)m_registry);
|
||||
xmlrpc_env_clean((xmlrpc_env*)m_env);
|
||||
delete (xmlrpc_env*)m_env;
|
||||
}
|
||||
|
||||
bool
|
||||
XmlRpc::process(const char* inBuffer, uint32_t length, slot_write slotWrite) {
|
||||
xmlrpc_env localEnv;
|
||||
xmlrpc_env_init(&localEnv);
|
||||
|
||||
xmlrpc_mem_block* memblock = xmlrpc_registry_process_call(&localEnv, (xmlrpc_registry*)m_registry, NULL, inBuffer, length);
|
||||
|
||||
if (localEnv.fault_occurred && localEnv.fault_code == XMLRPC_INTERNAL_ERROR)
|
||||
throw torrent::internal_error("Internal error in XMLRPC.");
|
||||
|
||||
bool result = slotWrite((const char*)xmlrpc_mem_block_contents(memblock),
|
||||
xmlrpc_mem_block_size(memblock));
|
||||
|
||||
xmlrpc_mem_block_free(memblock);
|
||||
xmlrpc_env_clean(&localEnv);
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
XmlRpc::insert_command(const char* name, const char* parm, const char* doc) {
|
||||
xmlrpc_env localEnv;
|
||||
xmlrpc_env_init(&localEnv);
|
||||
|
||||
xmlrpc_registry_add_method_w_doc(&localEnv, (xmlrpc_registry*)m_registry, NULL, name,
|
||||
&xmlrpc_call_command, const_cast<char*>(name), parm, doc);
|
||||
|
||||
if (localEnv.fault_occurred)
|
||||
throw torrent::internal_error("Fault occured while inserting xmlrpc call.");
|
||||
|
||||
xmlrpc_env_clean(&localEnv);
|
||||
}
|
||||
|
||||
void
|
||||
XmlRpc::set_dialect(int dialect) {
|
||||
if (!is_valid())
|
||||
throw torrent::input_error("Cannot select XMLRPC dialect before it is initialized.");
|
||||
|
||||
xmlrpc_env localEnv;
|
||||
xmlrpc_env_init(&localEnv);
|
||||
|
||||
switch (dialect) {
|
||||
case dialect_generic:
|
||||
break;
|
||||
|
||||
#ifdef XMLRPC_HAVE_I8
|
||||
case dialect_i8:
|
||||
xmlrpc_registry_set_dialect(&localEnv, (xmlrpc_registry*)m_registry, xmlrpc_dialect_i8);
|
||||
break;
|
||||
|
||||
case dialect_apache:
|
||||
xmlrpc_registry_set_dialect(&localEnv, (xmlrpc_registry*)m_registry, xmlrpc_dialect_apache);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
xmlrpc_env_clean(&localEnv);
|
||||
throw torrent::input_error("Unsupported XMLRPC dialect selected.");
|
||||
if (!obj.is_string()) {
|
||||
throw torrent::input_error("invalid parameters: target must be a string");
|
||||
}
|
||||
std::string target_string = obj.as_string();
|
||||
bool require_index = (callFlags & (CommandMap::flag_tracker_target | CommandMap::flag_file_target));
|
||||
if (target_string.size() == 0 && !require_index) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (localEnv.fault_occurred) {
|
||||
xmlrpc_env_clean(&localEnv);
|
||||
throw torrent::input_error("Unsupported XMLRPC dialect selected.");
|
||||
// Length of SHA1 hash is 40
|
||||
if (target_string.size() < 40) {
|
||||
throw torrent::input_error("invalid parameters: invalid target");
|
||||
}
|
||||
|
||||
xmlrpc_env_clean(&localEnv);
|
||||
m_dialect = dialect;
|
||||
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) {
|
||||
throw torrent::input_error("invalid parameters: no index");
|
||||
}
|
||||
hash = target_string;
|
||||
} else {
|
||||
hash = target_string.substr(0, delim_pos);
|
||||
type = target_string[delim_pos + 1];
|
||||
index = target_string.substr(delim_pos + 2);
|
||||
}
|
||||
core::Download* download = xmlrpc.slot_find_download()(hash.c_str());
|
||||
|
||||
if (download == nullptr)
|
||||
throw torrent::input_error("invalid parameters: info-hash not found");
|
||||
|
||||
try {
|
||||
switch (type) {
|
||||
case 'd':
|
||||
*target = rpc::make_target(download);
|
||||
break;
|
||||
case 'f':
|
||||
*target = rpc::make_target(
|
||||
command_base::target_file,
|
||||
xmlrpc.slot_find_file()(download, std::stoi(std::string(index))));
|
||||
break;
|
||||
case 't':
|
||||
*target = rpc::make_target(
|
||||
command_base::target_tracker,
|
||||
xmlrpc.slot_find_tracker()(download, std::stoi(std::string(index))));
|
||||
break;
|
||||
case 'p': {
|
||||
if (index.size() < 40) {
|
||||
// -501 == XMLRPC_TYPE_ERROR, used here directly to avoid
|
||||
// conflicts between tinyxml2 and xmlrpc-c
|
||||
throw xmlrpc_error(-501, "Not a hash string.");
|
||||
}
|
||||
torrent::HashString hash;
|
||||
torrent::hash_string_from_hex_c_str(index.c_str(), hash);
|
||||
*target = rpc::make_target(
|
||||
command_base::target_peer,
|
||||
xmlrpc.slot_find_peer()(download, hash));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw torrent::input_error("invalid parameters: unexpected target type");
|
||||
}
|
||||
} catch (const std::logic_error&) {
|
||||
throw torrent::input_error("invalid parameters: invalid index");
|
||||
}
|
||||
}
|
||||
|
||||
int64_t
|
||||
XmlRpc::size_limit() {
|
||||
return xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID);
|
||||
}
|
||||
|
||||
void
|
||||
XmlRpc::set_size_limit(uint64_t size) {
|
||||
if (size >= (64 << 20))
|
||||
throw torrent::input_error("Invalid XMLRPC limit size.");
|
||||
|
||||
xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, size);
|
||||
}
|
||||
|
||||
#else
|
||||
#ifndef HAVE_XMLRPC_C
|
||||
#ifndef HAVE_XMLRPC_TINYXML2
|
||||
|
||||
void XmlRpc::initialize() { throw torrent::resource_error("XMLRPC not supported."); }
|
||||
void XmlRpc::cleanup() {}
|
||||
@@ -620,6 +146,9 @@ bool XmlRpc::process(__UNUSED const char* inBuffer, __UNUSED uint32_t length, __
|
||||
int64_t XmlRpc::size_limit() { return 0; }
|
||||
void XmlRpc::set_size_limit(uint64_t size) {}
|
||||
|
||||
bool XmlRpc::is_valid() const { return false; }
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
+19
-8
@@ -39,6 +39,9 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "command.h"
|
||||
#include "scgi_task.h"
|
||||
|
||||
#include <torrent/hash_string.h>
|
||||
|
||||
namespace core {
|
||||
@@ -74,9 +77,11 @@ public:
|
||||
static const int call_file = 5;
|
||||
static const int call_file_itr = 6;
|
||||
|
||||
XmlRpc() : m_env(NULL), m_registry(NULL), m_dialect(dialect_i8) {}
|
||||
static void object_to_target(const torrent::Object& obj, int callFlags, rpc::target_type* target);
|
||||
|
||||
bool is_valid() const { return m_env != NULL; }
|
||||
XmlRpc() : m_env(NULL), m_registry(NULL), m_dialect(dialect_i8), m_sizeLimit(SCgiTask::max_content_size) {}
|
||||
|
||||
bool is_valid() const;
|
||||
|
||||
void initialize();
|
||||
void cleanup();
|
||||
@@ -93,19 +98,25 @@ public:
|
||||
slot_tracker& slot_find_tracker() { return m_slotFindTracker; }
|
||||
slot_peer& slot_find_peer() { return m_slotFindPeer; }
|
||||
|
||||
static int64_t size_limit();
|
||||
static void set_size_limit(uint64_t size);
|
||||
int64_t size_limit();
|
||||
void set_size_limit(uint64_t size);
|
||||
|
||||
private:
|
||||
slot_download m_slotFindDownload;
|
||||
slot_file m_slotFindFile;
|
||||
slot_tracker m_slotFindTracker;
|
||||
slot_peer m_slotFindPeer;
|
||||
|
||||
// Only used by xmlrpc-c
|
||||
void* m_env;
|
||||
void* m_registry;
|
||||
|
||||
int m_dialect;
|
||||
|
||||
slot_download m_slotFindDownload;
|
||||
slot_file m_slotFindFile;
|
||||
slot_tracker m_slotFindTracker;
|
||||
slot_peer m_slotFindPeer;
|
||||
// Only used by tinyxml2
|
||||
bool m_isValid;
|
||||
uint64_t m_sizeLimit;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,530 @@
|
||||
// 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 <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_XMLRPC_C
|
||||
#ifdef HAVE_XMLRPC_TINYXML2
|
||||
#error HAVE_XMLRPC_C and HAVE_XMLRPC_TINYXML2 cannot be used together. Please choose only one
|
||||
#endif
|
||||
|
||||
#include <cctype>
|
||||
#include <string>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <xmlrpc-c/server.h>
|
||||
|
||||
#include <rak/string_manip.h>
|
||||
#include <torrent/object.h>
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
#include "xmlrpc.h"
|
||||
#include "parse_commands.h"
|
||||
|
||||
namespace rpc {
|
||||
|
||||
class xmlrpc_error_c : public torrent::base_error {
|
||||
public:
|
||||
xmlrpc_error_c(xmlrpc_env* env) : m_type(env->fault_code), m_msg(env->fault_string) {}
|
||||
xmlrpc_error_c(int type, const char* msg) : m_type(type), m_msg(msg) {}
|
||||
virtual ~xmlrpc_error_c() throw() {}
|
||||
|
||||
virtual int type() const throw() { return m_type; }
|
||||
virtual const char* what() const throw() { return m_msg; }
|
||||
|
||||
private:
|
||||
int m_type;
|
||||
const char* m_msg;
|
||||
};
|
||||
|
||||
torrent::Object xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value, int callType = 0, rpc::target_type* target = NULL);
|
||||
|
||||
inline torrent::Object
|
||||
xmlrpc_list_entry_to_object(xmlrpc_env* env, xmlrpc_value* src, int index) {
|
||||
xmlrpc_value* tmp;
|
||||
xmlrpc_array_read_item(env, src, index, &tmp);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error_c(env);
|
||||
|
||||
torrent::Object obj = xmlrpc_to_object(env, tmp);
|
||||
xmlrpc_DECREF(tmp);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
int64_t
|
||||
xmlrpc_list_entry_to_value(xmlrpc_env* env, xmlrpc_value* src, int index) {
|
||||
xmlrpc_value* tmp;
|
||||
xmlrpc_array_read_item(env, src, index, &tmp);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error_c(env);
|
||||
|
||||
switch (xmlrpc_value_type(tmp)) {
|
||||
case XMLRPC_TYPE_INT:
|
||||
int v;
|
||||
xmlrpc_read_int(env, tmp, &v);
|
||||
xmlrpc_DECREF(tmp);
|
||||
return v;
|
||||
|
||||
#ifdef XMLRPC_HAVE_I8
|
||||
case XMLRPC_TYPE_I8:
|
||||
xmlrpc_int64 v2;
|
||||
xmlrpc_read_i8(env, tmp, &v2);
|
||||
xmlrpc_DECREF(tmp);
|
||||
return v2;
|
||||
#endif
|
||||
|
||||
case XMLRPC_TYPE_STRING:
|
||||
{
|
||||
const char* str;
|
||||
xmlrpc_read_string(env, tmp, &str);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error_c(env);
|
||||
|
||||
const char* end = str;
|
||||
int64_t v3 = ::strtoll(str, (char**)&end, 0);
|
||||
|
||||
::free((void*)str);
|
||||
|
||||
if (*str == '\0' || *end != '\0')
|
||||
throw xmlrpc_error_c(XMLRPC_TYPE_ERROR, "Invalid index.");
|
||||
|
||||
return v3;
|
||||
}
|
||||
|
||||
default:
|
||||
xmlrpc_DECREF(tmp);
|
||||
throw xmlrpc_error_c(XMLRPC_TYPE_ERROR, "Invalid type found.");
|
||||
}
|
||||
}
|
||||
|
||||
rpc::target_type
|
||||
xmlrpc_to_target(xmlrpc_env* env, xmlrpc_value* value, int callType) {
|
||||
rpc::target_type target;
|
||||
|
||||
XmlRpc::object_to_target(xmlrpc_to_object(env, value, -1, nullptr), callType, &target);
|
||||
return target;
|
||||
}
|
||||
|
||||
rpc::target_type
|
||||
xmlrpc_to_index_type(int index, int callType, core::Download* download) {
|
||||
void* result;
|
||||
|
||||
switch (callType) {
|
||||
case XmlRpc::call_file: result = xmlrpc.slot_find_file()(download, index); break;
|
||||
case XmlRpc::call_tracker: result = xmlrpc.slot_find_tracker()(download, index); break;
|
||||
default: result = NULL; break;
|
||||
}
|
||||
|
||||
if (result == NULL)
|
||||
throw xmlrpc_error_c(XMLRPC_TYPE_ERROR, "Invalid index.");
|
||||
|
||||
return rpc::make_target(callType, result);
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value, int callType, rpc::target_type* target) {
|
||||
switch (xmlrpc_value_type(value)) {
|
||||
case XMLRPC_TYPE_INT:
|
||||
int v;
|
||||
xmlrpc_read_int(env, value, &v);
|
||||
|
||||
return torrent::Object((int64_t)v);
|
||||
|
||||
#ifdef XMLRPC_HAVE_I8
|
||||
case XMLRPC_TYPE_I8:
|
||||
xmlrpc_int64 v2;
|
||||
xmlrpc_read_i8(env, value, &v2);
|
||||
|
||||
return torrent::Object((int64_t)v2);
|
||||
#endif
|
||||
|
||||
// case XMLRPC_TYPE_BOOL:
|
||||
// case XMLRPC_TYPE_DOUBLE:
|
||||
// case XMLRPC_TYPE_DATETIME:
|
||||
|
||||
case XMLRPC_TYPE_STRING:
|
||||
|
||||
if (callType != XmlRpc::call_generic && target != nullptr) {
|
||||
// When the call type is not supposed to be void, we'll try to
|
||||
// convert it to a command target. It's not that important that
|
||||
// it is converted to the right type here, as an mismatch will
|
||||
// be caught when executing the command.
|
||||
*target = xmlrpc_to_target(env, value, callType);
|
||||
return torrent::Object();
|
||||
|
||||
} else {
|
||||
const char* valueString;
|
||||
xmlrpc_read_string(env, value, &valueString);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error_c(env);
|
||||
|
||||
torrent::Object result = torrent::Object(std::string(valueString));
|
||||
|
||||
// Urgh, seriously?
|
||||
::free((void*)valueString);
|
||||
return result;
|
||||
}
|
||||
|
||||
case XMLRPC_TYPE_BASE64:
|
||||
{
|
||||
size_t valueSize;
|
||||
const char* valueString;
|
||||
|
||||
xmlrpc_read_base64(env, value, &valueSize, (const unsigned char**)&valueString);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error_c(env);
|
||||
|
||||
torrent::Object result = torrent::Object(std::string(valueString, valueSize));
|
||||
|
||||
// Urgh, seriously?
|
||||
::free((void*)valueString);
|
||||
return result;
|
||||
}
|
||||
|
||||
case XMLRPC_TYPE_ARRAY:
|
||||
{
|
||||
unsigned int current = 0;
|
||||
unsigned int last = xmlrpc_array_size(env, value);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error_c(env);
|
||||
|
||||
if (callType != XmlRpc::call_generic && last != 0) {
|
||||
if (last < 1)
|
||||
throw xmlrpc_error_c(XMLRPC_TYPE_ERROR, "Too few arguments.");
|
||||
|
||||
xmlrpc_value* tmp;
|
||||
xmlrpc_array_read_item(env, value, current++, &tmp);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error_c(env);
|
||||
|
||||
if (target != nullptr)
|
||||
*target = xmlrpc_to_target(env, tmp, callType);
|
||||
xmlrpc_DECREF(tmp);
|
||||
|
||||
if (env->fault_occurred)
|
||||
throw xmlrpc_error_c(env);
|
||||
|
||||
if (target->first == XmlRpc::call_download &&
|
||||
(callType == XmlRpc::call_file || callType == XmlRpc::call_tracker)) {
|
||||
// If we have a download target and the call type requires
|
||||
// another contained type, then we try to use the next
|
||||
// parameter as the index to support old-style calls.
|
||||
|
||||
if (current == last)
|
||||
throw xmlrpc_error_c(XMLRPC_TYPE_ERROR, "Too few arguments, missing index.");
|
||||
|
||||
*target = xmlrpc_to_index_type(xmlrpc_list_entry_to_value(env, value, current++), callType, (core::Download*)target->second);
|
||||
}
|
||||
}
|
||||
|
||||
if (current + 1 < last) {
|
||||
torrent::Object result = torrent::Object::create_list();
|
||||
torrent::Object::list_type& listRef = result.as_list();
|
||||
|
||||
while (current != last)
|
||||
listRef.push_back(xmlrpc_list_entry_to_object(env, value, current++));
|
||||
|
||||
return result;
|
||||
|
||||
} else if (current + 1 == last) {
|
||||
return xmlrpc_list_entry_to_object(env, value, current);
|
||||
|
||||
} else {
|
||||
return torrent::Object();
|
||||
}
|
||||
}
|
||||
|
||||
// case XMLRPC_TYPE_STRUCT:
|
||||
// case XMLRPC_TYPE_C_PTR:
|
||||
// case XMLRPC_TYPE_NIL:
|
||||
// case XMLRPC_TYPE_DEAD:
|
||||
default:
|
||||
throw xmlrpc_error_c(XMLRPC_TYPE_ERROR, "Unsupported type found.");
|
||||
}
|
||||
}
|
||||
|
||||
xmlrpc_value*
|
||||
object_to_xmlrpc(xmlrpc_env* env, const torrent::Object& object) {
|
||||
switch (object.type()) {
|
||||
case torrent::Object::TYPE_VALUE:
|
||||
|
||||
#ifdef XMLRPC_HAVE_I8
|
||||
if (xmlrpc.dialect() != XmlRpc::dialect_generic)
|
||||
return xmlrpc_i8_new(env, object.as_value());
|
||||
#else
|
||||
return xmlrpc_int_new(env, object.as_value());
|
||||
#endif
|
||||
|
||||
case torrent::Object::TYPE_STRING:
|
||||
{
|
||||
#ifdef XMLRPC_HAVE_I8
|
||||
// The versions that support I8 do implicit utf-8 validation.
|
||||
xmlrpc_value* result = xmlrpc_string_new(env, object.as_string().c_str());
|
||||
#else
|
||||
// In older versions, xmlrpc-c doesn't validate the utf-8 encoding itself.
|
||||
xmlrpc_validate_utf8(env, object.as_string().c_str(), object.as_string().length());
|
||||
|
||||
xmlrpc_value* result = env->fault_occurred ? NULL : xmlrpc_string_new(env, object.as_string().c_str());
|
||||
#endif
|
||||
|
||||
if (env->fault_occurred) {
|
||||
xmlrpc_env_clean(env);
|
||||
xmlrpc_env_init(env);
|
||||
|
||||
const std::string& str = object.as_string();
|
||||
char buffer[str.size() + 1];
|
||||
char* dst = buffer;
|
||||
for (std::string::const_iterator itr = str.begin(); itr != str.end(); ++itr)
|
||||
*dst++ = ((*itr < 0x20 && *itr != '\r' && *itr != '\n' && *itr != '\t') || (*itr & 0x80)) ? '?' : *itr;
|
||||
*dst = 0;
|
||||
|
||||
result = xmlrpc_string_new(env, buffer);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
case torrent::Object::TYPE_LIST:
|
||||
{
|
||||
xmlrpc_value* result = xmlrpc_array_new(env);
|
||||
|
||||
for (torrent::Object::list_const_iterator itr = object.as_list().begin(), last = object.as_list().end(); itr != last; itr++) {
|
||||
xmlrpc_value* item = object_to_xmlrpc(env, *itr);
|
||||
xmlrpc_array_append_item(env, result, item);
|
||||
xmlrpc_DECREF(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
case torrent::Object::TYPE_MAP:
|
||||
{
|
||||
xmlrpc_value* result = xmlrpc_struct_new(env);
|
||||
|
||||
for (torrent::Object::map_const_iterator itr = object.as_map().begin(), last = object.as_map().end(); itr != last; itr++) {
|
||||
xmlrpc_value* item = object_to_xmlrpc(env, itr->second);
|
||||
xmlrpc_struct_set_value(env, result, itr->first.c_str(), item);
|
||||
xmlrpc_DECREF(item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
case torrent::Object::TYPE_DICT_KEY:
|
||||
{
|
||||
xmlrpc_value* result = xmlrpc_array_new(env);
|
||||
|
||||
xmlrpc_value* key_item = object_to_xmlrpc(env, object.as_dict_key());
|
||||
xmlrpc_array_append_item(env, result, key_item);
|
||||
xmlrpc_DECREF(key_item);
|
||||
|
||||
if (object.as_dict_obj().is_list()) {
|
||||
for (torrent::Object::list_const_iterator
|
||||
itr = object.as_dict_obj().as_list().begin(),
|
||||
last = object.as_dict_obj().as_list().end();
|
||||
itr != last; itr++) {
|
||||
xmlrpc_value* item = object_to_xmlrpc(env, *itr);
|
||||
xmlrpc_array_append_item(env, result, item);
|
||||
xmlrpc_DECREF(item);
|
||||
}
|
||||
} else {
|
||||
xmlrpc_value* arg_item = object_to_xmlrpc(env, object.as_dict_obj());
|
||||
xmlrpc_array_append_item(env, result, arg_item);
|
||||
xmlrpc_DECREF(arg_item);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
default:
|
||||
return xmlrpc_int_new(env, 0);
|
||||
}
|
||||
}
|
||||
|
||||
xmlrpc_value*
|
||||
xmlrpc_call_command(xmlrpc_env* env, xmlrpc_value* args, void* voidServerInfo) {
|
||||
CommandMap::iterator itr = commands.find((const char*)voidServerInfo);
|
||||
|
||||
if (itr == commands.end()) {
|
||||
xmlrpc_env_set_fault(env, XMLRPC_PARSE_ERROR, ("Command \"" + std::string((const char*)voidServerInfo) + "\" does not exist.").c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
try {
|
||||
torrent::Object object;
|
||||
rpc::target_type target = rpc::make_target();
|
||||
|
||||
if (itr->second.m_flags & CommandMap::flag_no_target)
|
||||
xmlrpc_to_object(env, args, XmlRpc::call_generic, &target).swap(object);
|
||||
else if (itr->second.m_flags & CommandMap::flag_file_target)
|
||||
xmlrpc_to_object(env, args, XmlRpc::call_file, &target).swap(object);
|
||||
else if (itr->second.m_flags & CommandMap::flag_tracker_target)
|
||||
xmlrpc_to_object(env, args, XmlRpc::call_tracker, &target).swap(object);
|
||||
else
|
||||
xmlrpc_to_object(env, args, XmlRpc::call_any, &target).swap(object);
|
||||
|
||||
if (env->fault_occurred)
|
||||
return NULL;
|
||||
|
||||
return object_to_xmlrpc(env, rpc::commands.call_command(itr, object, target));
|
||||
|
||||
} catch (xmlrpc_error_c& e) {
|
||||
xmlrpc_env_set_fault(env, e.type(), e.what());
|
||||
return NULL;
|
||||
|
||||
} catch (torrent::local_error& e) {
|
||||
xmlrpc_env_set_fault(env, XMLRPC_PARSE_ERROR, e.what());
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
XmlRpc::initialize() {
|
||||
#ifndef XMLRPC_HAVE_I8
|
||||
m_dialect = dialect_generic;
|
||||
#endif
|
||||
|
||||
m_env = new xmlrpc_env;
|
||||
|
||||
xmlrpc_env_init((xmlrpc_env*)m_env);
|
||||
m_registry = xmlrpc_registry_new((xmlrpc_env*)m_env);
|
||||
}
|
||||
|
||||
void
|
||||
XmlRpc::cleanup() {
|
||||
if (!is_valid())
|
||||
return;
|
||||
|
||||
xmlrpc_registry_free((xmlrpc_registry*)m_registry);
|
||||
xmlrpc_env_clean((xmlrpc_env*)m_env);
|
||||
delete (xmlrpc_env*)m_env;
|
||||
}
|
||||
|
||||
bool
|
||||
XmlRpc::process(const char* inBuffer, uint32_t length, slot_write slotWrite) {
|
||||
xmlrpc_env localEnv;
|
||||
xmlrpc_env_init(&localEnv);
|
||||
|
||||
xmlrpc_mem_block* memblock = xmlrpc_registry_process_call(&localEnv, (xmlrpc_registry*)m_registry, NULL, inBuffer, length);
|
||||
|
||||
if (localEnv.fault_occurred && localEnv.fault_code == XMLRPC_INTERNAL_ERROR)
|
||||
throw torrent::internal_error("Internal error in XMLRPC.");
|
||||
|
||||
bool result = slotWrite((const char*)xmlrpc_mem_block_contents(memblock),
|
||||
xmlrpc_mem_block_size(memblock));
|
||||
|
||||
xmlrpc_mem_block_free(memblock);
|
||||
xmlrpc_env_clean(&localEnv);
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
XmlRpc::insert_command(const char* name, const char* parm, const char* doc) {
|
||||
xmlrpc_env localEnv;
|
||||
xmlrpc_env_init(&localEnv);
|
||||
|
||||
xmlrpc_registry_add_method_w_doc(&localEnv, (xmlrpc_registry*)m_registry, NULL, name,
|
||||
&xmlrpc_call_command, const_cast<char*>(name), parm, doc);
|
||||
|
||||
if (localEnv.fault_occurred)
|
||||
throw torrent::internal_error("Fault occured while inserting xmlrpc call.");
|
||||
|
||||
xmlrpc_env_clean(&localEnv);
|
||||
}
|
||||
|
||||
void
|
||||
XmlRpc::set_dialect(int dialect) {
|
||||
if (!is_valid())
|
||||
throw torrent::input_error("Cannot select XMLRPC dialect before it is initialized.");
|
||||
|
||||
xmlrpc_env localEnv;
|
||||
xmlrpc_env_init(&localEnv);
|
||||
|
||||
switch (dialect) {
|
||||
case dialect_generic:
|
||||
break;
|
||||
|
||||
#ifdef XMLRPC_HAVE_I8
|
||||
case dialect_i8:
|
||||
xmlrpc_registry_set_dialect(&localEnv, (xmlrpc_registry*)m_registry, xmlrpc_dialect_i8);
|
||||
break;
|
||||
|
||||
case dialect_apache:
|
||||
xmlrpc_registry_set_dialect(&localEnv, (xmlrpc_registry*)m_registry, xmlrpc_dialect_apache);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
xmlrpc_env_clean(&localEnv);
|
||||
throw torrent::input_error("Unsupported XMLRPC dialect selected.");
|
||||
}
|
||||
|
||||
if (localEnv.fault_occurred) {
|
||||
xmlrpc_env_clean(&localEnv);
|
||||
throw torrent::input_error("Unsupported XMLRPC dialect selected.");
|
||||
}
|
||||
|
||||
xmlrpc_env_clean(&localEnv);
|
||||
m_dialect = dialect;
|
||||
}
|
||||
|
||||
int64_t
|
||||
XmlRpc::size_limit() {
|
||||
return xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID);
|
||||
}
|
||||
|
||||
void
|
||||
XmlRpc::set_size_limit(uint64_t size) {
|
||||
if (size >= (64 << 20))
|
||||
throw torrent::input_error("Invalid XMLRPC limit size.");
|
||||
|
||||
xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, size);
|
||||
}
|
||||
|
||||
bool
|
||||
XmlRpc::is_valid() const { return m_env != NULL; }
|
||||
|
||||
} // namespace rpc
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,392 @@
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_XMLRPC_TINYXML2
|
||||
#ifdef HAVE_XMLRPC_C
|
||||
#error HAVE_XMLRPC_C and HAVE_XMLRPC_TINYXML2 cannot be used together. Please choose only one
|
||||
#endif
|
||||
|
||||
#include <cctype>
|
||||
#include <initializer_list>
|
||||
#include <string>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <rak/string_manip.h>
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/object.h>
|
||||
|
||||
#include "parse_commands.h"
|
||||
#include "rpc/tinyxml2/tinyxml2.h"
|
||||
#include "utils/base64.h"
|
||||
#include "xmlrpc.h"
|
||||
|
||||
namespace rpc {
|
||||
|
||||
// Taken from xmlrpc-c
|
||||
const int XMLRPC_INTERNAL_ERROR = -500;
|
||||
const int XMLRPC_TYPE_ERROR = -501;
|
||||
const int XMLRPC_INDEX_ERROR = -502;
|
||||
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_INTROSPECTION_DISABLED_ERROR = -508;
|
||||
const int XMLRPC_LIMIT_EXCEEDED_ERROR = -509;
|
||||
const int XMLRPC_INVALID_UTF8_ERROR = -510;
|
||||
|
||||
class xmlrpc_error : public torrent::base_error {
|
||||
public:
|
||||
xmlrpc_error(int type, std::string msg) :
|
||||
m_type(type), m_msg(msg) {}
|
||||
virtual ~xmlrpc_error() throw() {}
|
||||
|
||||
virtual int type() const throw() { return m_type; }
|
||||
virtual const char* what() const throw() { return m_msg.c_str(); }
|
||||
|
||||
private:
|
||||
int m_type;
|
||||
std::string m_msg;
|
||||
};
|
||||
|
||||
const tinyxml2::XMLElement*
|
||||
element_access(const tinyxml2::XMLElement* elem, std::initializer_list<std::string> names) {
|
||||
// Helper function to check each step of a element access, in lieu of XPath
|
||||
const tinyxml2::XMLElement* result = elem;
|
||||
for (auto itr : names) {
|
||||
result = result->FirstChildElement(itr.c_str());
|
||||
if (result == nullptr) {
|
||||
throw xmlrpc_error(XMLRPC_PARSE_ERROR, "could not find expected element " + itr);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
long long
|
||||
element_to_int(const tinyxml2::XMLNode* elem) {
|
||||
char* pos;
|
||||
if (elem->FirstChild() == nullptr) {
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "unable to parse empty integer");
|
||||
}
|
||||
auto str = elem->FirstChild()->ToText()->Value();
|
||||
auto result = std::strtoll(str, &pos, 10);
|
||||
if (pos == str || *pos != '\0')
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "unable to parse integer value");
|
||||
return result;
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
xml_value_to_object(const tinyxml2::XMLNode* elem) {
|
||||
if (elem == nullptr) {
|
||||
throw xmlrpc_error(XMLRPC_INTERNAL_ERROR, "received null element to convert");
|
||||
}
|
||||
if (std::strncmp(elem->Value(), "value", sizeof("value")) != 0) {
|
||||
throw xmlrpc_error(XMLRPC_INTERNAL_ERROR, "received non-value element to convert");
|
||||
}
|
||||
auto root_element = elem->FirstChild();
|
||||
auto root_type = root_element->Value();
|
||||
if (std::strncmp(root_type, "string", sizeof("string")) == 0) {
|
||||
auto child_element = root_element->FirstChild();
|
||||
if (child_element == nullptr) {
|
||||
return torrent::Object("");
|
||||
}
|
||||
return torrent::Object(child_element->ToText()->Value());
|
||||
} else if (std::strncmp(root_type, "int", sizeof("int")) == 0 ||
|
||||
std::strncmp(root_type, "i4", sizeof("i4")) == 0 ||
|
||||
std::strncmp(root_type, "i8", sizeof("i8")) == 0) {
|
||||
return torrent::Object(element_to_int(root_element));
|
||||
} else if (std::strncmp(root_type, "boolean", sizeof("boolean")) == 0) {
|
||||
auto boolean_text = std::string(root_element->FirstChild()->ToText()->Value());
|
||||
if (boolean_text == "1") {
|
||||
return torrent::Object((int64_t)1);
|
||||
} else if (boolean_text == "0") {
|
||||
return torrent::Object((int64_t)0);
|
||||
}
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "unknown boolean value: " + boolean_text);
|
||||
} else if (std::strncmp(root_type, "array", sizeof("array")) == 0) {
|
||||
auto array_raw = torrent::Object::create_list();
|
||||
auto& array = array_raw.as_list();
|
||||
auto data_element = root_element->ToElement()->FirstChildElement("data");
|
||||
if (data_element == nullptr)
|
||||
throw xmlrpc_error(XMLRPC_PARSE_ERROR, "could not find expected data element in array");
|
||||
for (auto child = data_element->FirstChildElement("value"); child; child = child->NextSiblingElement("value")) {
|
||||
array.push_back(xml_value_to_object(child));
|
||||
}
|
||||
return array_raw;
|
||||
} else if (std::strncmp(root_type, "struct", sizeof("struct")) == 0) {
|
||||
auto map_raw = torrent::Object::create_map();
|
||||
auto& map = map_raw.as_map();
|
||||
for (auto child = root_element->FirstChildElement("member"); child; child = child->NextSiblingElement("member")) {
|
||||
auto key = child->FirstChildElement("name")->GetText();
|
||||
map[key] = std::move(xml_value_to_object(child->FirstChildElement("value")));
|
||||
}
|
||||
return map_raw;
|
||||
} else if (std::strncmp(root_type, "base64", sizeof("base64")) == 0) {
|
||||
auto child_element = root_element->FirstChild();
|
||||
if (child_element == nullptr) {
|
||||
return torrent::Object("");
|
||||
}
|
||||
return torrent::Object(utils::decode_base64(utils::remove_newlines(child_element->ToText()->Value())));
|
||||
} else {
|
||||
throw xmlrpc_error(XMLRPC_INTERNAL_ERROR, "received unsupported value type: " + std::string(root_type));
|
||||
}
|
||||
return torrent::Object();
|
||||
}
|
||||
|
||||
void
|
||||
print_object_xml(const torrent::Object& obj, tinyxml2::XMLPrinter* printer) {
|
||||
switch (obj.type()) {
|
||||
case torrent::Object::TYPE_STRING:
|
||||
printer->OpenElement("string", true);
|
||||
printer->PushText(obj.as_string().c_str());
|
||||
printer->CloseElement(true);
|
||||
break;
|
||||
case torrent::Object::TYPE_VALUE:
|
||||
if (obj.as_value() > ((torrent::Object::value_type)2 << 30) || obj.as_value() < -((torrent::Object::value_type)2 << 30)) {
|
||||
printer->OpenElement("i8", true);
|
||||
} else {
|
||||
printer->OpenElement("i4", true);
|
||||
}
|
||||
printer->PushText(std::to_string(obj.as_value()).c_str());
|
||||
printer->CloseElement(true);
|
||||
break;
|
||||
case torrent::Object::TYPE_LIST:
|
||||
printer->OpenElement("array", true);
|
||||
for (const auto& itr : obj.as_list()) {
|
||||
printer->OpenElement("value", true);
|
||||
print_object_xml(itr, printer);
|
||||
printer->CloseElement(true);
|
||||
}
|
||||
printer->CloseElement(true);
|
||||
break;
|
||||
case torrent::Object::TYPE_MAP:
|
||||
printer->OpenElement("struct", true);
|
||||
for (const auto& itr : obj.as_map()) {
|
||||
printer->OpenElement("member", true);
|
||||
printer->OpenElement("name", true);
|
||||
printer->PushText(itr.first.c_str());
|
||||
printer->CloseElement(true);
|
||||
printer->OpenElement("value", true);
|
||||
print_object_xml(itr.second, printer);
|
||||
printer->CloseElement(true);
|
||||
printer->CloseElement(true);
|
||||
}
|
||||
printer->CloseElement(true);
|
||||
break;
|
||||
case torrent::Object::TYPE_DICT_KEY:
|
||||
printer->OpenElement("array", true);
|
||||
|
||||
printer->OpenElement("value", true);
|
||||
print_object_xml(obj.as_dict_key(), printer);
|
||||
printer->CloseElement(true);
|
||||
|
||||
if (obj.as_dict_obj().is_list()) {
|
||||
for (auto itr : obj.as_dict_obj().as_list()) {
|
||||
printer->OpenElement("value", true);
|
||||
print_object_xml(itr, printer);
|
||||
printer->CloseElement(true);
|
||||
}
|
||||
} else {
|
||||
printer->OpenElement("value", true);
|
||||
print_object_xml(obj.as_dict_obj(), printer);
|
||||
printer->CloseElement(true);
|
||||
}
|
||||
printer->CloseElement(true);
|
||||
break;
|
||||
default:
|
||||
printer->OpenElement("i4", true);
|
||||
printer->PushText(0);
|
||||
printer->CloseElement(true);
|
||||
}
|
||||
}
|
||||
|
||||
torrent::Object
|
||||
execute_command(std::string method_name, const tinyxml2::XMLElement* params_element) {
|
||||
CommandMap::iterator cmd_itr = commands.find(method_name.c_str());
|
||||
if (cmd_itr == commands.end() || !(cmd_itr->second.m_flags & CommandMap::flag_public_xmlrpc)) {
|
||||
throw xmlrpc_error(XMLRPC_NO_SUCH_METHOD_ERROR, "method '" + method_name + "' not defined");
|
||||
}
|
||||
torrent::Object params_raw = torrent::Object::create_list();
|
||||
torrent::Object::list_type& params = params_raw.as_list();
|
||||
rpc::target_type target = rpc::make_target();
|
||||
if (params_element != nullptr) {
|
||||
if (std::strncmp(params_element->Name(), "params", sizeof("params")) == 0) {
|
||||
// Parse out the target if available
|
||||
const auto* child = params_element->FirstChildElement("param");
|
||||
if (child != nullptr) {
|
||||
if (!(cmd_itr->second.m_flags & CommandMap::flag_no_target)) {
|
||||
XmlRpc::object_to_target(xml_value_to_object(child->FirstChildElement("value")), cmd_itr->second.m_flags, &target);
|
||||
child = child->NextSiblingElement("param");
|
||||
}
|
||||
// Parse out any other params
|
||||
while (child != nullptr) {
|
||||
params.push_back(xml_value_to_object(child->FirstChildElement("value")));
|
||||
child = child->NextSiblingElement("param");
|
||||
}
|
||||
}
|
||||
} else if (params_element->FirstChildElement("data") != nullptr) {
|
||||
// If it's not a <params>, it's probably a <array> passed in via system.multicall
|
||||
const auto* child = params_element->FirstChildElement("data")->FirstChildElement("value");
|
||||
if (child != nullptr) {
|
||||
if (!(cmd_itr->second.m_flags & CommandMap::flag_no_target)) {
|
||||
XmlRpc::object_to_target(xml_value_to_object(child), cmd_itr->second.m_flags, &target);
|
||||
child = child->NextSiblingElement("value");
|
||||
}
|
||||
while (child != nullptr) {
|
||||
params.push_back(xml_value_to_object(child));
|
||||
child = child->NextSiblingElement("value");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (params.empty() && (cmd_itr->second.m_flags & (CommandMap::flag_file_target | CommandMap::flag_tracker_target))) {
|
||||
throw xmlrpc_error(XMLRPC_TYPE_ERROR, "invalid parameters: too few");
|
||||
}
|
||||
return rpc::commands.call_command(cmd_itr, params_raw, target);
|
||||
}
|
||||
|
||||
void
|
||||
process_document(const tinyxml2::XMLDocument* doc, tinyxml2::XMLPrinter* printer) {
|
||||
if (doc->Error())
|
||||
throw xmlrpc_error(XMLRPC_PARSE_ERROR, doc->ErrorStr());
|
||||
if (doc->FirstChildElement("methodCall") == nullptr)
|
||||
throw xmlrpc_error(XMLRPC_PARSE_ERROR, "methodCall element not found");
|
||||
if (doc->FirstChildElement("methodCall")->FirstChildElement("methodName") == nullptr)
|
||||
throw xmlrpc_error(XMLRPC_PARSE_ERROR, "methodName element not found");
|
||||
auto method_name = doc->FirstChildElement("methodCall")->FirstChildElement("methodName")->GetText();
|
||||
torrent::Object result;
|
||||
|
||||
// Add a shim here for system.multicall to allow better code reuse, and
|
||||
// because system.multicall is one of the few methods that doesn't take a target
|
||||
if (method_name == std::string("system.multicall")) {
|
||||
result = torrent::Object::create_list();
|
||||
auto& result_list = result.as_list();
|
||||
auto parent_elements = element_access(doc->RootElement(), {"params", "param", "value", "array", "data"});
|
||||
for (auto child = parent_elements->FirstChildElement("value"); child; child = child->NextSiblingElement("value")) {
|
||||
auto sub_method_name = element_access(child, {"struct", "member", "value", "string"})->GetText();
|
||||
// If sub_params ends up a nullptr at the end of this if-chian,
|
||||
// execute_command will turn it into an empty list
|
||||
auto sub_params = element_access(child, {"struct", "member"});
|
||||
if (sub_params != nullptr)
|
||||
sub_params = sub_params->NextSiblingElement("member");
|
||||
if (sub_params != nullptr)
|
||||
sub_params = sub_params->FirstChildElement("value");
|
||||
if (sub_params != nullptr)
|
||||
sub_params = sub_params->FirstChildElement("array");
|
||||
try {
|
||||
auto sub_result = torrent::Object::create_list();
|
||||
sub_result.as_list().push_back(execute_command(sub_method_name, sub_params));
|
||||
result_list.push_back(sub_result);
|
||||
} catch (xmlrpc_error& e) {
|
||||
auto fault = torrent::Object::create_map();
|
||||
fault.as_map()["faultString"] = e.what();
|
||||
fault.as_map()["faultCode"] = e.type();
|
||||
result_list.push_back(fault);
|
||||
} catch (torrent::local_error& e) {
|
||||
auto fault = torrent::Object::create_map();
|
||||
fault.as_map()["faultString"] = e.what();
|
||||
fault.as_map()["faultCode"] = XMLRPC_INTERNAL_ERROR;
|
||||
result_list.push_back(fault);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = execute_command(method_name, doc->FirstChildElement("methodCall")->FirstChildElement("params"));
|
||||
}
|
||||
|
||||
printer->PushHeader(false, true);
|
||||
printer->OpenElement("methodResponse", true);
|
||||
printer->OpenElement("params", true);
|
||||
|
||||
printer->OpenElement("param", true);
|
||||
printer->OpenElement("value", true);
|
||||
print_object_xml(result, printer);
|
||||
printer->CloseElement(true);
|
||||
printer->CloseElement(true);
|
||||
|
||||
printer->CloseElement(true);
|
||||
printer->CloseElement(true);
|
||||
}
|
||||
|
||||
void
|
||||
print_xmlrpc_fault(int faultCode, std::string faultString, tinyxml2::XMLPrinter* printer) {
|
||||
printer->PushHeader(false, true);
|
||||
|
||||
printer->OpenElement("methodResponse", true);
|
||||
printer->OpenElement("fault", true);
|
||||
printer->OpenElement("struct", true);
|
||||
|
||||
printer->OpenElement("member", true);
|
||||
printer->OpenElement("name", true);
|
||||
printer->PushText("faultCode");
|
||||
printer->CloseElement(true);
|
||||
printer->OpenElement("value", true);
|
||||
printer->OpenElement("i4", true);
|
||||
printer->PushText(faultCode);
|
||||
printer->CloseElement(true);
|
||||
printer->CloseElement(true);
|
||||
printer->CloseElement(true);
|
||||
|
||||
printer->OpenElement("member", true);
|
||||
printer->OpenElement("name", true);
|
||||
printer->PushText("faultString");
|
||||
printer->CloseElement(true);
|
||||
printer->OpenElement("value", true);
|
||||
printer->OpenElement("string", true);
|
||||
printer->PushText(faultString.c_str());
|
||||
printer->CloseElement(true);
|
||||
printer->CloseElement(true);
|
||||
printer->CloseElement(true);
|
||||
|
||||
printer->CloseElement(true);
|
||||
printer->CloseElement(true);
|
||||
printer->CloseElement(true);
|
||||
}
|
||||
|
||||
bool
|
||||
XmlRpc::process(const char* inBuffer, uint32_t length, slot_write slotWrite) {
|
||||
if (length > m_sizeLimit) {
|
||||
tinyxml2::XMLPrinter printer(nullptr, true, 0);
|
||||
print_xmlrpc_fault(XMLRPC_LIMIT_EXCEEDED_ERROR, "Content size exceeds maximum XML-RPC limit", &printer);
|
||||
return slotWrite(printer.CStr(), printer.CStrSize() - 1);
|
||||
}
|
||||
tinyxml2::XMLDocument doc;
|
||||
doc.Parse(inBuffer, length);
|
||||
try {
|
||||
// This printer can't be reused in the 'catch' because while the
|
||||
// buffer can be cleared, the internal stack of opened elements
|
||||
// remains.
|
||||
tinyxml2::XMLPrinter printer(nullptr, true, 0);
|
||||
process_document(&doc, &printer);
|
||||
return slotWrite(printer.CStr(), printer.CStrSize() - 1);
|
||||
} catch (xmlrpc_error& e) {
|
||||
tinyxml2::XMLPrinter printer(nullptr, true, 0);
|
||||
print_xmlrpc_fault(e.type(), e.what(), &printer);
|
||||
return slotWrite(printer.CStr(), printer.CStrSize() - 1);
|
||||
} catch (torrent::local_error& e) {
|
||||
tinyxml2::XMLPrinter printer(nullptr, true, 0);
|
||||
print_xmlrpc_fault(XMLRPC_INTERNAL_ERROR, e.what(), &printer);
|
||||
return slotWrite(printer.CStr(), printer.CStrSize() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
XmlRpc::initialize() { m_isValid = true; }
|
||||
void
|
||||
XmlRpc::cleanup() {}
|
||||
|
||||
void
|
||||
XmlRpc::insert_command(const char*, const char*, const char*) {}
|
||||
void
|
||||
XmlRpc::set_dialect(int) {}
|
||||
|
||||
int64_t
|
||||
XmlRpc::size_limit() { return static_cast<int64_t>(m_sizeLimit); }
|
||||
void
|
||||
XmlRpc::set_size_limit(uint64_t size) { m_sizeLimit = size; }
|
||||
|
||||
bool
|
||||
XmlRpc::is_valid() const { return m_isValid; }
|
||||
|
||||
} // namespace rpc
|
||||
|
||||
#endif
|
||||
+32
-93
@@ -1,39 +1,3 @@
|
||||
// libTorrent - BitTorrent library
|
||||
// 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 <jaris@ifi.uio.no>
|
||||
//
|
||||
// Skomakerveien 33
|
||||
// 3185 Skoppum, NORWAY
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "thread_base.h"
|
||||
@@ -41,82 +5,58 @@
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <mutex>
|
||||
#include <rak/error_number.h>
|
||||
#include <torrent/exceptions.h>
|
||||
#include <torrent/torrent.h>
|
||||
#include <torrent/utils/log.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "globals.h"
|
||||
#include "control.h"
|
||||
#include "core/manager.h"
|
||||
|
||||
// Temporarly injected into config.h.
|
||||
/* temp hack */
|
||||
//#define lt_cacheline_aligned __attribute__((__aligned__(128)))
|
||||
|
||||
class lt_cacheline_aligned thread_queue_hack {
|
||||
class thread_queue_hack : private std::vector<ThreadBase::thread_base_func> {
|
||||
public:
|
||||
typedef ThreadBase::thread_base_func value_type;
|
||||
typedef ThreadBase::thread_base_func* iterator;
|
||||
static constexpr unsigned int max_size = 32;
|
||||
|
||||
static const unsigned int max_size = 32;
|
||||
using value_type = ThreadBase::thread_base_func;
|
||||
using base_type = std::vector<value_type>;
|
||||
|
||||
thread_queue_hack() { std::memset(this, 0, sizeof(thread_queue_hack)); }
|
||||
thread_queue_hack() { reserve(max_size); };
|
||||
|
||||
void lock() { while (!__sync_bool_compare_and_swap(&m_lock, 0, 1)) usleep(0); }
|
||||
void unlock() { __sync_bool_compare_and_swap(&m_lock, 1, 0); }
|
||||
|
||||
iterator begin() { return m_queue; }
|
||||
iterator max_capacity() { return m_queue + max_size; }
|
||||
|
||||
iterator end_and_lock() { lock(); return std::find(begin(), max_capacity(), (value_type)NULL); }
|
||||
|
||||
bool empty() const { return m_queue[0] == NULL; }
|
||||
bool empty() {
|
||||
std::lock_guard<std::mutex> lguard(m_lock);
|
||||
return base_type::empty();
|
||||
}
|
||||
|
||||
void push_back(value_type v) {
|
||||
iterator itr = end_and_lock();
|
||||
|
||||
if (itr == max_capacity())
|
||||
throw torrent::internal_error("Overflowed thread_queue.");
|
||||
|
||||
__sync_bool_compare_and_swap(itr, NULL, v);
|
||||
unlock();
|
||||
const std::lock_guard<std::mutex> lguard(m_lock);
|
||||
if (base_type::size() >= max_size)
|
||||
throw torrent::internal_error("Overflowed thread_queue max size of " + std::to_string(max_size) + ".");
|
||||
base_type::push_back(v);
|
||||
}
|
||||
|
||||
value_type* copy_and_clear(value_type* dest) {
|
||||
iterator itr = begin();
|
||||
lock();
|
||||
|
||||
while (*itr != NULL) *dest++ = *itr++;
|
||||
|
||||
clear_and_unlock();
|
||||
return dest;
|
||||
void copy_and_clear(base_type& target) {
|
||||
std::lock_guard<std::mutex> lguard(m_lock);
|
||||
target.assign(begin(), end());
|
||||
base_type::clear();
|
||||
}
|
||||
|
||||
void clear_and_unlock() {
|
||||
std::memset(this, 0, sizeof(thread_queue_hack));
|
||||
__sync_synchronize();
|
||||
}
|
||||
|
||||
private:
|
||||
int m_lock;
|
||||
value_type m_queue[max_size + 1];
|
||||
private:
|
||||
std::mutex m_lock;
|
||||
};
|
||||
|
||||
void throw_shutdown_exception() { throw torrent::shutdown_exception(); }
|
||||
void
|
||||
throw_shutdown_exception() { throw torrent::shutdown_exception(); }
|
||||
|
||||
ThreadBase::ThreadBase() {
|
||||
m_taskShutdown.slot() = std::bind(&throw_shutdown_exception);
|
||||
|
||||
m_threadQueue = new thread_queue_hack;
|
||||
m_threadQueue = std::make_unique<thread_queue_hack>();
|
||||
}
|
||||
|
||||
ThreadBase::~ThreadBase() {
|
||||
delete m_threadQueue;
|
||||
}
|
||||
// Defined in here and not the header so that the default destructor
|
||||
// can properly deduce how to destruct the unique_ptr<thread_queue_hack>
|
||||
ThreadBase::~ThreadBase() = default;
|
||||
|
||||
// Move to libtorrent...
|
||||
void
|
||||
@@ -137,12 +77,11 @@ ThreadBase::next_timeout_usec() {
|
||||
|
||||
void
|
||||
ThreadBase::call_queued_items() {
|
||||
thread_base_func result[thread_queue_hack::max_size];
|
||||
thread_base_func* first = result;
|
||||
thread_base_func* last = m_threadQueue->copy_and_clear((thread_base_func*)result);
|
||||
|
||||
while (first != last && *first)
|
||||
(*first++)(this);
|
||||
std::vector<thread_queue_hack::value_type> queue;
|
||||
m_threadQueue->copy_and_clear(queue);
|
||||
for (auto itr : queue) {
|
||||
itr(this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
+2
-1
@@ -37,6 +37,7 @@
|
||||
#ifndef RTORRENT_UTILS_THREAD_BASE_H
|
||||
#define RTORRENT_UTILS_THREAD_BASE_H
|
||||
|
||||
#include <memory>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <torrent/utils/thread_base.h>
|
||||
@@ -82,7 +83,7 @@ protected:
|
||||
|
||||
// Temporary hack to pass messages to a thread. This really needs to
|
||||
// be cleaned up and/or integrated into the priority queue itself.
|
||||
thread_queue_hack* m_threadQueue;
|
||||
std::unique_ptr<thread_queue_hack> m_threadQueue;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+6
-11
@@ -55,8 +55,8 @@ ThreadWorker::ThreadWorker() {
|
||||
}
|
||||
|
||||
ThreadWorker::~ThreadWorker() {
|
||||
if (m_safe.scgi)
|
||||
m_safe.scgi->deactivate();
|
||||
if (m_scgi)
|
||||
m_scgi.load()->deactivate();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -67,17 +67,12 @@ ThreadWorker::init_thread() {
|
||||
|
||||
bool
|
||||
ThreadWorker::set_scgi(rpc::SCgi* scgi) {
|
||||
if (!__sync_bool_compare_and_swap(&m_safe.scgi, NULL, scgi))
|
||||
rpc::SCgi* expected = nullptr;
|
||||
if (!m_scgi.compare_exchange_strong(expected, scgi))
|
||||
return false;
|
||||
|
||||
change_xmlrpc_log();
|
||||
|
||||
// The xmlrpc process call requires a global lock.
|
||||
// m_safe.scgi->set_slot_process(rak::mem_fn(&rpc::xmlrpc, &rpc::XmlRpc::process));
|
||||
|
||||
// Synchronize in order to ensure the worker thread sees the updated
|
||||
// SCgi object.
|
||||
__sync_synchronize();
|
||||
queue_item((thread_base_func)&start_scgi);
|
||||
return true;
|
||||
}
|
||||
@@ -93,10 +88,10 @@ void
|
||||
ThreadWorker::start_scgi(ThreadBase* baseThread) {
|
||||
ThreadWorker* thread = (ThreadWorker*)baseThread;
|
||||
|
||||
if (thread->m_safe.scgi == NULL)
|
||||
if (thread->scgi() == NULL)
|
||||
throw torrent::internal_error("Tried to start SCGI but object was not present.");
|
||||
|
||||
thread->m_safe.scgi->activate();
|
||||
thread->scgi()->activate();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
+3
-8
@@ -37,6 +37,7 @@
|
||||
#ifndef RTORRENT_THREAD_WORKER_H
|
||||
#define RTORRENT_THREAD_WORKER_H
|
||||
|
||||
#include <atomic>
|
||||
#include "thread_base.h"
|
||||
|
||||
#include <rak/priority_queue_default.h>
|
||||
@@ -57,7 +58,7 @@ public:
|
||||
|
||||
virtual void init_thread();
|
||||
|
||||
rpc::SCgi* scgi() { return m_safe.scgi; }
|
||||
rpc::SCgi* scgi() { return m_scgi; }
|
||||
bool set_scgi(rpc::SCgi* scgi);
|
||||
|
||||
void set_xmlrpc_log(const std::string& filename);
|
||||
@@ -70,13 +71,7 @@ private:
|
||||
|
||||
void change_xmlrpc_log();
|
||||
|
||||
struct lt_cacheline_aligned safe_type {
|
||||
safe_type() : scgi(NULL) {}
|
||||
|
||||
rpc::SCgi* scgi;
|
||||
};
|
||||
|
||||
safe_type m_safe;
|
||||
std::atomic<rpc::SCgi*> m_scgi{ nullptr };
|
||||
|
||||
// The following types shall only be modified while holding the
|
||||
// global lock.
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#include <string>
|
||||
|
||||
#include <torrent/exceptions.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
std::string
|
||||
remove_newlines(const std::string& str) {
|
||||
std::string result;
|
||||
for (auto &itr : str) {
|
||||
if (itr != '\n' || itr != '\n')
|
||||
result.push_back(itr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Modified from the public domain code in
|
||||
// https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64#C++_2
|
||||
constexpr static char base64_pad_character = '=';
|
||||
|
||||
std::string
|
||||
decode_base64(const std::string& input) {
|
||||
if (input.length() % 4) // Sanity check
|
||||
throw torrent::input_error("Invalid base64.");
|
||||
size_t padding = 0;
|
||||
if (input.length()) {
|
||||
if (input[input.length() - 1] == base64_pad_character)
|
||||
padding++;
|
||||
if (input[input.length() - 2] == base64_pad_character)
|
||||
padding++;
|
||||
}
|
||||
// Setup a vector to hold the result
|
||||
std::string decodedBytes;
|
||||
decodedBytes.reserve(((input.length() / 4) * 3) - padding);
|
||||
long temp = 0; // Holds decoded quanta
|
||||
std::string::const_iterator cursor = input.begin();
|
||||
while (cursor < input.end()) {
|
||||
for (size_t quantumPosition = 0; quantumPosition < 4; quantumPosition++) {
|
||||
temp <<= 6;
|
||||
if (*cursor >= 0x41 && *cursor <= 0x5A) // This area will need tweaking if
|
||||
temp |= *cursor - 0x41; // you are using an alternate alphabet
|
||||
else if (*cursor >= 0x61 && *cursor <= 0x7A)
|
||||
temp |= *cursor - 0x47;
|
||||
else if (*cursor >= 0x30 && *cursor <= 0x39)
|
||||
temp |= *cursor + 0x04;
|
||||
else if (*cursor == 0x2B)
|
||||
temp |= 0x3E; // change to 0x2D for URL alphabet
|
||||
else if (*cursor == 0x2F)
|
||||
temp |= 0x3F; // change to 0x5F for URL alphabet
|
||||
else if (*cursor == base64_pad_character) // pad
|
||||
{
|
||||
switch (input.end() - cursor) {
|
||||
case 1: // One pad character
|
||||
decodedBytes.push_back((temp >> 16) & 0x000000FF);
|
||||
decodedBytes.push_back((temp >> 8) & 0x000000FF);
|
||||
return decodedBytes;
|
||||
case 2: // Two pad characters
|
||||
decodedBytes.push_back((temp >> 10) & 0x000000FF);
|
||||
return decodedBytes;
|
||||
default:
|
||||
throw torrent::input_error("Invalid padding in base64.");
|
||||
}
|
||||
} else
|
||||
throw torrent::input_error("Invalid character in base64.");
|
||||
cursor++;
|
||||
}
|
||||
decodedBytes.push_back((temp >> 16) & 0x000000FF);
|
||||
decodedBytes.push_back((temp >> 8) & 0x000000FF);
|
||||
decodedBytes.push_back((temp)&0x000000FF);
|
||||
}
|
||||
return decodedBytes;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ rtorrentTest_SOURCES = \
|
||||
rpc/command_test.h \
|
||||
rpc/command_map_test.cc \
|
||||
rpc/command_map_test.h \
|
||||
rpc/xmlrpc_test.cc \
|
||||
rpc/xmlrpc_test.h \
|
||||
rpc/command_slot_test.cc \
|
||||
rpc/command_slot_test.h \
|
||||
rpc/object_storage_test.cc \
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
#include "config.h"
|
||||
#include <string>
|
||||
|
||||
#include "command_helpers.h"
|
||||
#include "rpc/command_map.h"
|
||||
|
||||
#include "xmlrpc_test.h"
|
||||
#include "control.h"
|
||||
#include "globals.h"
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(XmlrpcTest);
|
||||
|
||||
torrent::Object xmlrpc_cmd_test_reflect(rpc::target_type t, const torrent::Object& obj) { return obj; }
|
||||
|
||||
void initialize_command_dynamic();
|
||||
|
||||
#if defined(HAVE_XMLRPC_TINYXML2) && !defined(HAVE_XMLRPC_C)
|
||||
void
|
||||
XmlrpcTest::setUp() {
|
||||
m_commandItr = m_commands;
|
||||
m_xmlrpc = rpc::XmlRpc();
|
||||
m_xmlrpc.initialize();
|
||||
setlocale(LC_ALL, "");
|
||||
cachedTime = rak::timer::current();
|
||||
control = new Control;
|
||||
if (rpc::commands.find("xmlrpc_reflect") == rpc::commands.end()) {
|
||||
CMD2_ANY("xmlrpc_reflect", &xmlrpc_cmd_test_reflect);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
XmlrpcTest::test_basics() {
|
||||
std::ifstream file; file.open("rpc/xmlrpc_test_data.txt");
|
||||
CPPUNIT_ASSERT(file.good());
|
||||
std::vector<std::string> titles;
|
||||
std::vector<std::string> inputs;
|
||||
std::vector<std::string> outputs;
|
||||
std::string line;
|
||||
int index = 0;
|
||||
// Read file into inputs/outputs
|
||||
while (std::getline(file, line)) {
|
||||
if (line.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
if (line[0] == '#') {
|
||||
titles.push_back(line);
|
||||
continue;
|
||||
}
|
||||
if (index % 2) {
|
||||
outputs.push_back(line);
|
||||
} else {
|
||||
inputs.push_back(line);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
// Sanity check the above parser
|
||||
CPPUNIT_ASSERT_MESSAGE("Could not parse test data", inputs.size() > 0 && inputs.size() == outputs.size() && inputs.size() == titles.size());
|
||||
for (int i = 0; i < inputs.size(); i++) {
|
||||
std::string output;
|
||||
m_xmlrpc.process(inputs[i].c_str(), inputs[i].size(), [&output](const char* c, uint32_t l){ output.append(c, l); return true;});
|
||||
CPPUNIT_ASSERT_EQUAL_MESSAGE(titles[i], std::string(outputs[i]), output);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
XmlrpcTest::test_invalid_utf8() {
|
||||
// Surprisingly, this call doesn't fail. TinyXML-2 technically expects
|
||||
// valid UTF-8, but doesn't check strings, and Object strings are
|
||||
// just a series of bytes so it reflects just fine.
|
||||
std::string input = "<?xml version=\"1.0\"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><string>\xc3\x28</string></value></param></params></methodCall>";
|
||||
std::string expected = "<?xml version=\"1.0\"?><methodResponse><params><param><value><array><value><string>\xc3\x28</string></value></array></value></param></params></methodResponse>";
|
||||
std::string output;
|
||||
m_xmlrpc.process(input.c_str(), input.size(), [&output](const char* c, uint32_t l){ output.append(c, l); return true;});
|
||||
CPPUNIT_ASSERT_EQUAL(expected, output);
|
||||
}
|
||||
|
||||
void
|
||||
XmlrpcTest::test_size_limit() {
|
||||
std::string input = "<?xml version=\"1.0\"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><string>\xc3\x28</string></value></param></params></methodCall>";
|
||||
std::string expected = "<?xml version=\"1.0\"?><methodResponse><fault><struct><member><name>faultCode</name><value><i4>-509</i4></value></member><member><name>faultString</name><value><string>Content size exceeds maximum XML-RPC limit</string></value></member></struct></fault></methodResponse>";
|
||||
std::string output;
|
||||
m_xmlrpc.set_size_limit(1);
|
||||
m_xmlrpc.process(input.c_str(), input.size(), [&output](const char* c, uint32_t l){ output.append(c, l); return true;});
|
||||
CPPUNIT_ASSERT_EQUAL(expected, output);
|
||||
}
|
||||
#else
|
||||
void XmlrpcTest::test_invalid_utf8() {}
|
||||
void XmlrpcTest::test_basics() {}
|
||||
void XmlrpcTest::test_size_limit() {}
|
||||
void XmlrpcTest::setUp() {}
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
#include "rpc/command_map.h"
|
||||
#include "rpc/xmlrpc.h"
|
||||
|
||||
class XmlrpcTest : public CppUnit::TestFixture {
|
||||
CPPUNIT_TEST_SUITE(XmlrpcTest);
|
||||
CPPUNIT_TEST(test_basics);
|
||||
CPPUNIT_TEST(test_invalid_utf8);
|
||||
CPPUNIT_TEST(test_size_limit);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
static const int cmd_size = 256;
|
||||
|
||||
void setUp();
|
||||
void tearDown() {}
|
||||
|
||||
void test_basics();
|
||||
void test_invalid_utf8();
|
||||
void test_size_limit();
|
||||
|
||||
private:
|
||||
rpc::XmlRpc m_xmlrpc;
|
||||
|
||||
rpc::CommandMap m_map;
|
||||
|
||||
rpc::command_base m_commands[cmd_size];
|
||||
rpc::command_base* m_commandItr;
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
# Basic call
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><params><param><value><array/></value></param></params></methodResponse>
|
||||
|
||||
# Basic call without params
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><params><param><value><array/></value></param></params></methodResponse>
|
||||
|
||||
# UTF-8 string
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><string>чао</string></value></param></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><params><param><value><array><value><string>чао</string></value></array></value></param></params></methodResponse>
|
||||
|
||||
# emoji string
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><string>😊</string></value></param></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><params><param><value><array><value><string>😊</string></value></array></value></param></params></methodResponse>
|
||||
|
||||
# base64 data (which gets returned as a string)
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><base64>Zm9vYmFy</base64></value></param></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><params><param><value><array><value><string>foobar</string></value></array></value></param></params></methodResponse>
|
||||
|
||||
# i4 ints
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><i4>41</i4></value></param></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><params><param><value><array><value><i4>41</i4></value></array></value></param></params></methodResponse>
|
||||
|
||||
# i8 ints
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><i8>2247483647</i8></value></param></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><params><param><value><array><value><i8>2247483647</i8></value></array></value></param></params></methodResponse>
|
||||
|
||||
# negative i8 ints
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><i8>-2347483647</i8></value></param></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><params><param><value><array><value><i8>-2347483647</i8></value></array></value></param></params></methodResponse>
|
||||
|
||||
# Empty array
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><array><data><value><i8>2247483647</i8></value></data></array></value></param></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><params><param><value><array><value><array><value><i8>2247483647</i8></value></array></value></array></value></param></params></methodResponse>
|
||||
|
||||
# Simple array
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><array><data></data></array></value></param></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><params><param><value><array><value><array/></value></array></value></param></params></methodResponse>
|
||||
|
||||
# Empty struct
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><struct></struct></value></param></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><params><param><value><array><value><struct/></value></array></value></param></params></methodResponse>
|
||||
|
||||
# Simple struct
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><struct><member><name>lowerBound</name><value><i4>18</i4></value></member><member><name>upperBound</name><value><i4>139</i4></value></member></struct></value></param></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><params><param><value><array><value><struct><member><name>lowerBound</name><value><i4>18</i4></value></member><member><name>upperBound</name><value><i4>139</i4></value></member></struct></value></array></value></param></params></methodResponse>
|
||||
|
||||
# Invalid - missing method
|
||||
<?xml version="1.0"?><methodCall><methodName>no_such_method</methodName><params><param><value><i4>41</i4></value></param></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><fault><struct><member><name>faultCode</name><value><i4>-506</i4></value></member><member><name>faultString</name><value><string>method 'no_such_method' not defined</string></value></member></struct></fault></methodResponse>
|
||||
|
||||
# Invalid - i4 target
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><i4>41</i4></value></param></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><fault><struct><member><name>faultCode</name><value><i4>-500</i4></value></member><member><name>faultString</name><value><string>invalid parameters: target must be a string</string></value></member></struct></fault></methodResponse>
|
||||
|
||||
# Invalid - empty int tag
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><i4/></value></param></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><fault><struct><member><name>faultCode</name><value><i4>-501</i4></value></member><member><name>faultString</name><value><string>unable to parse empty integer</string></value></member></struct></fault></methodResponse>
|
||||
|
||||
# Invalid - empty int text
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><string></string></value></param><param><value><i4></i4></value></param></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><fault><struct><member><name>faultCode</name><value><i4>-501</i4></value></member><member><name>faultString</name><value><string>unable to parse empty integer</string></value></member></struct></fault></methodResponse>
|
||||
|
||||
# Invalid - broken XML
|
||||
thodCall><methodName>test_a</methodName><params><param><value><i4>41</i4></value></param></params></method
|
||||
<?xml version="1.0"?><methodResponse><fault><struct><member><name>faultCode</name><value><i4>-503</i4></value></member><member><name>faultString</name><value><string>Error=XML_ERROR_PARSING_ELEMENT ErrorID=6 (0x6) Line number=1: XMLElement name=method</string></value></member></struct></fault></methodResponse>
|
||||
|
||||
# Invalid - non-integer i4
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><i4>string value</i4></value></param></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><fault><struct><member><name>faultCode</name><value><i4>-501</i4></value></member><member><name>faultString</name><value><string>unable to parse integer value</string></value></member></struct></fault></methodResponse>
|
||||
|
||||
# Invalid - float i4
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><i4>3.14</i4></value></param></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><fault><struct><member><name>faultCode</name><value><i4>-501</i4></value></member><member><name>faultString</name><value><string>unable to parse integer value</string></value></member></struct></fault></methodResponse>
|
||||
|
||||
# Invalid - non-boolean boolean
|
||||
<?xml version="1.0"?><methodCall><methodName>xmlrpc_reflect</methodName><params><param><value><boolean>string value</boolean></value></param></params></methodCall>
|
||||
<?xml version="1.0"?><methodResponse><fault><struct><member><name>faultCode</name><value><i4>-501</i4></value></member><member><name>faultString</name><value><string>unknown boolean value: string value</string></value></member></struct></fault></methodResponse>
|
||||
@@ -17,7 +17,7 @@ void initialize_command_ui();
|
||||
|
||||
void
|
||||
CommandDynamicTest::setUp() {
|
||||
if (rpc::commands.empty()) {
|
||||
if (rpc::commands.find("method.insert") == rpc::commands.end()) {
|
||||
setlocale(LC_ALL, "");
|
||||
cachedTime = rak::timer::current();
|
||||
control = new Control;
|
||||
|
||||
Reference in New Issue
Block a user