Compare commits

...

97 Commits

Author SHA1 Message Date
rakshasa 68fdb86c72 Update configure.ac. 2024-12-26 16:50:18 +00:00
rakshasa 6b8a285e75 Release v0.15.0. 2024-12-26 16:29:16 +00:00
kannibalox 8f0331625a Correctly handle commands that are flagged as not using targets
Fixes #1346
2024-12-27 01:00:23 +09:00
stickz 3c65afcf8c tinyxml2: Resolve LTO issues
We can't have two classes with the same name in the rpc namespace. It causes ODR and lto-type-mismatches when compiling rTorrent with LTO.

This pull request addresses the problem by renaming the `xmlrpc_error` error class to `xmlrpc_error_c` in the xmlrpc_c file.
2024-12-27 00:55:29 +09:00
kannibalox cd9948a4a8 Remove/replace functional_fun.h functions 2024-12-22 19:17:36 +09:00
rakshasa 0d1b9e9d55 Removed cppunit ldflags from common flags. 2024-12-21 02:28:46 +09:00
rakshasa 6561c00ae3 Use std::array in partial_queue. 2024-12-21 02:23:58 +09:00
rakshasa b8cb828d96 Replaced depreacted rak/functional calls. 2024-12-15 00:54:20 +09:00
stickz e3be3b2c12 scgi: Fix Apple and Solaris compatibility
This is a follow up to #1310. Apple and Solaris do not support MSG_NOSIGNAL, so disable this flag for these platforms until we find a better solution. Other platforms remain unaffected.
2024-12-14 23:34:48 +09:00
Jari Sundell 0f93fa109c Fixed curl stack shutdown with active downloads. (#1338)
Fixed curl stack shutdown with active downloads.
2024-12-12 22:59:48 +09:00
Jari Sundell 7e1193acab Run unit test in workflow PR. 2024-12-08 01:32:46 +09:00
kannibalox 5792ed1ae2 Apply clang-format 2024-12-07 19:26:29 +09:00
kannibalox 8cfc71b936 Fix system.multicall parameter parsing 2024-12-07 19:26:29 +09:00
kannibalox f2b065a798 Fix typo 2024-12-07 19:26:29 +09:00
kannibalox f33c2560a3 Default to an empty list when not passed explicit <params> 2024-12-07 19:26:29 +09:00
kannibalox db1e78fa9d Update tests to fix typo 2024-12-07 19:26:29 +09:00
kannibalox 48c40ee0c6 Fix typo: methodReponse -> methodResponse 2024-12-07 19:26:29 +09:00
kannibalox 3aeb213dc4 Avoid <int> in tinyxml2 responses
It's part of the XML-RPC spec, but some clients only accept i4/i8, which
should be supported nearly universally.

Fixes #1330
2024-12-07 19:26:29 +09:00
trim21 5a200f5d8f fix: d.group.name should return group name instead of group index 2024-12-06 01:09:18 +09:00
rakshasa 713067ba94 Fixed minor syntax issues. 2024-12-05 04:00:54 +09:00
Jari Sundell fea2cbfac6 Use proper target namespace 2024-12-05 04:00:54 +09:00
kannibalox 6bf1731a1c Ensure compability check actually checks the type for files
Fixes https://github.com/rakshasa/rtorrent/issues/854
2024-12-05 04:00:54 +09:00
Jari Sundell f2caa2651d Added clang-tidy github action. (#1325)
Added clang-tidy github action.
2024-12-05 03:01:11 +09:00
kannibalox 511895a8b6 Add headers to enable compiling without the libtorrent change 2024-12-04 19:19:53 +09:00
kannibalox 4e64fbc78b Properly use compare_exchange_strong
In practice, calling `network.scgi.open_port` again doesn't trigger
this function.
2024-12-04 19:19:53 +09:00
kannibalox 93b3a34f28 Properly lock size check, and remove overall cacheline alignment 2024-12-04 19:19:53 +09:00
kannibalox ea1ca00f31 Remove cacheline alignment from atomic pointer 2024-12-04 19:19:53 +09:00
kannibalox 58b1fc4914 Ensure empty() is properly guarded 2024-12-04 19:19:53 +09:00
kannibalox 577818bde4 Remove cache alignment from atomic members 2024-12-04 19:19:53 +09:00
kannibalox 0ea7615d0a Convert __sync* built-ins to std::atomic
Also converts thread_queue_hack to a simpler reserved vector
2024-12-04 19:19:53 +09:00
PPN-SD 73c1233dc0 Fix calls ar directly
Signed-off-by: Nicolas PARLANT <nicolas.parlant@parhuet.fr>
2024-11-26 01:27:39 +09:00
rakshasa 11ddc35d02 Renamed tinyxml2.cpp file. 2024-11-25 18:44:28 +09:00
kannibalox 036916fd9f Fix typo in method name 2024-11-25 18:44:28 +09:00
kannibalox b3fabd2a2b Align argument name with header definition 2024-11-25 18:44:28 +09:00
kannibalox 4b7c3ffafe Use empty() instead of checking size
Also use a const pointer for a child element
2024-11-25 18:44:28 +09:00
kannibalox 730d43b6c7 Explicitly mark narrowing conversion 2024-11-25 18:44:28 +09:00
kannibalox af2f30c183 Remove unused variable 2024-11-25 18:44:28 +09:00
kannibalox 36fd837d27 Fix unnecessary narrowing with proper return type 2024-11-25 18:44:28 +09:00
kannibalox 8dd8d691c5 Explicitly check result of std::strncmp 2024-11-25 18:44:28 +09:00
kannibalox e367b162ab Share object_to_target between tinyxml2 and xmlrpc-c 2024-11-25 18:44:28 +09:00
kannibalox 49cdfee65a Avoid ambiguous value_* in variable names 2024-11-25 18:44:28 +09:00
kannibalox 5680db3598 Parse out target param first if available 2024-11-25 18:44:28 +09:00
kannibalox 4768eb8356 Fix test to match case change 2024-11-25 18:44:28 +09:00
kannibalox fa2c7d961b Remove unnecessary std::string() and use lowercase for error msgs 2024-11-25 18:44:28 +09:00
kannibalox d6dcbc4a69 Add size limit to XML-RPC documents, defaulting to max SCGI size 2024-11-25 18:44:28 +09:00
kannibalox cadfcb50d2 Print iterator for TYPE_DICT_KEY, not object 2024-11-25 18:44:28 +09:00
kannibalox 71a39cd8a0 Keep as_*() outside of loops 2024-11-25 18:44:28 +09:00
kannibalox 0658486966 Change base64 decoder name, and add util for removing newlines 2024-11-25 18:44:28 +09:00
kannibalox 687fbbb83d Formatting 2024-11-25 18:44:28 +09:00
kannibalox e355eb561f Clean up borrowed base64 code to match repo standards 2024-11-25 18:44:28 +09:00
kannibalox 902de0b258 Add test for empty int text 2024-11-25 18:44:28 +09:00
kannibalox 01e5df4167 Remove title printing from test 2024-11-25 18:44:28 +09:00
kannibalox 8d85ce5857 Add test case for empty integer 2024-11-25 18:44:28 +09:00
kannibalox f9c9fa2e3b Move int element converter to helper function
Also fix strncmp checks
2024-11-25 18:44:28 +09:00
kannibalox 337897784d Change rpc namespace to include static consts 2024-11-25 18:44:28 +09:00
kannibalox 782b1e4ea8 Skip converting value_element_type to std::string 2024-11-25 18:44:28 +09:00
kannibalox 3cfc213053 Fix missing header 2024-11-25 18:44:28 +09:00
kannibalox b789d83b70 Fix system.listMethods inclusion check 2024-11-25 18:44:28 +09:00
kannibalox 67babb65d6 Convert element_access to use initializer lists
Also remove uses element_access for single child accesses
2024-11-25 18:44:28 +09:00
kannibalox e0d5c95821 Use correct terminology in comment 2024-11-25 18:44:28 +09:00
kannibalox 7f4c5df5ee Use single namespace statement 2024-11-25 18:44:28 +09:00
kannibalox dd45684584 Move vendored tinyxml2 files to rpc namespace and directory 2024-11-25 18:44:28 +09:00
kannibalox fb8632e990 Change lingering camelCase to snake_case 2024-11-25 18:44:28 +09:00
kannibalox b043b7b39e Simplify iterator names 2024-11-25 18:44:28 +09:00
kannibalox 168093d256 Remove extra return 2024-11-25 18:44:28 +09:00
kannibalox 0c2f0b7715 Add test case for invalid floats 2024-11-25 18:44:28 +09:00
kannibalox 4a9d9952e1 Be explicit about the desired result 2024-11-25 18:44:28 +09:00
kannibalox fd1d5dbdb2 Add test case for bad boolean value 2024-11-25 18:44:28 +09:00
kannibalox dd0c62c7f7 Uncomment after testing 2024-11-25 18:44:28 +09:00
kannibalox 3a8e462bd3 Throw error if attempting to use both xmlrpc-c and tinyxml2 2024-11-25 18:44:28 +09:00
kannibalox b360a734f1 Use std::strtoll instead of std::stol 2024-11-25 18:44:28 +09:00
kannibalox c18df7a366 Use std::strncmp for string comparison 2024-11-25 18:44:28 +09:00
kannibalox a269f808e1 Fix swapped variable names 2024-11-25 18:44:28 +09:00
kannibalox 4444df2e97 Change internal error to type error 2024-11-25 18:44:28 +09:00
kannibalox 5a194ab037 Remove stack from exception message 2024-11-25 18:44:28 +09:00
kannibalox 1b43f8b591 Use snake case for variable names
Also use `||` instead of `or`, and throw an error if an unknown boolean
string is received
2024-11-25 18:44:28 +09:00
kannibalox d6427203a4 Refactor to only use a position variable 2024-11-25 18:44:28 +09:00
kannibalox 87c6422052 Allow using vendored tinyxml2 for XMLRPC
By default, builds will still not have XMLRPC enabled at all and the
configure flag `--with-xmlrpc-tinyxml2` must be specified. If both
xmlrpc-c and tinyxml2 are specified, xmlrpc-c takes precedence.

Basic benchmarks indicate tinyxml2 is 2x faster for small
requests/responses, and that only increases as response sizes get
larger.
2024-11-25 18:44:28 +09:00
rakshasa 6ab265ecd4 Fixed merge errors. 2024-11-14 00:33:03 +09:00
rakshasa 203c66a287 Merge branch 'kannibalox-feature/clang-format' 2024-11-13 10:53:28 +00:00
Jari Sundell c9fe2facfa Merge branch 'master' into feature/clang-format 2024-11-13 19:51:04 +09:00
kannibalox 5abcf52e55 Remove unused flag_delete_key 2024-11-13 19:22:34 +09:00
kannibalox f2f84dd97a Remove destructor definition entirely 2024-11-13 19:22:34 +09:00
kannibalox 34de2b4ac9 Convert CommandMap to use std::string instead of char*
This allows it to own its keys and clean them up automatically on
destruction.
2024-11-13 19:22:34 +09:00
rakshasa ef937863fd Removed unused code. 2024-11-13 18:59:57 +09:00
kannibalox 00fa4127e1 Respect existing line limits 2024-11-11 16:00:28 -05:00
kannibalox 1b0cb722e6 Remove non-naming checks 2024-11-10 09:18:59 -05:00
kannibalox af23ca8032 Convert some representative files 2024-11-10 09:17:12 -05:00
kannibalox c85f9d6318 Add clang tidy/format 2024-11-10 09:08:33 -05:00
Josh Ellithorpe ee120730cc Update README.md to spell Ethereum correctly 2024-10-29 22:44:09 +09:00
stickz b284be6a66 Resolve scgi software crash
This commit resolves a scgi software crash when the scgi socket is closed before the message can be sent. It instructs `::send()` not to send a SIGPIPE termination signal. Instead the value -1 is returned and handled bellow. The SCgiTask is closed and a new one is sent to complete the task.

```
Thread 3 "rtorrent scgi" received signal SIGPIPE, Broken pipe.
                                                             [Switching to Thread 0x7fffe635c6c0 (LWP 2443872)]
0x00007ffff7929a84 in send () from /lib/x86_64-linux-gnu/libc.so.6
```
2024-10-29 22:42:33 +09:00
kannibalox 6e976616a0 Simplify updating queue entries
The erase/insert pattern can be replaced by a single method that
updates an existing entry's timer, or inserting if it doesn't exist.

This has much less impact than the libtorrent change it's based on,
but might as well be consistent.
2024-10-20 18:33:33 +09:00
Adam Sampson 38b39bdafc Add ax_require_defined.m4 to scripts.
This is required now by ax_with_curses.m4, but wasn't added when the
script was updated.
2024-10-07 23:52:20 +09:00
Adam Sampson 48718bdac8 Remove unnecessary +x mode from some m4 files. 2024-10-07 23:52:20 +09:00
Rui Chen 5ce84929e4 fix: use fsync for osx builds
Signed-off-by: Rui Chen <rui@chenrui.dev>
2024-10-07 23:43:54 +09:00
stickz c1d076f182 Fixing static building
@rakshasa A major hosting provider with rTorrent has mentioned the following revision is required for static builds to pass.
2024-10-07 23:32:45 +09:00
Jari Sundell f929594763 Update README.md 2024-09-29 23:34:31 +09:00
62 changed files with 7427 additions and 2130 deletions
+33
View File
@@ -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
View File
@@ -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
+132
View File
@@ -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
-1
View File
@@ -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 \
+7 -1
View File
@@ -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
View File
@@ -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
-655
View File
@@ -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
View File
@@ -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
View File
@@ -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
+22 -4
View File
@@ -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
View File
Executable → Regular
View File
Executable → Regular
View File
+37
View File
@@ -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
View File
+12
View File
@@ -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++)
+5
View File
@@ -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)
+4 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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();
+3 -3
View File
@@ -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));
-1
View File
@@ -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
View File
@@ -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
View File
@@ -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
+4 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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};
};
}
+7 -42
View File
@@ -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);
+4 -36
View File
@@ -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;
-36
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+2 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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());
}
+1 -1
View File
@@ -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());
}
+2 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+6
View File
@@ -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
View File
@@ -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
View File
@@ -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;
};
}
+530
View File
@@ -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
+392
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+73
View File
@@ -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;
}
}
+2
View File
@@ -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 \
+92
View File
@@ -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
+30
View File
@@ -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;
};
+79
View File
@@ -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>
+1 -1
View File
@@ -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;