mirror of
https://github.com/rakshasa/rtorrent.git
synced 2026-10-05 05:39:22 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d8664e6b4c | |||
| 7e907616a2 | |||
| dcf13ab21a | |||
| 03c4467a33 | |||
| fc5ce92c46 | |||
| f1b22023a0 | |||
| 928f3e58a8 | |||
| 743363b405 |
@@ -1,65 +0,0 @@
|
|||||||
name: "Lint Commit Message Size"
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check-commit-bounds:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Validate Line Count, Width, and Spacing
|
|
||||||
run: |
|
|
||||||
# Fetch commit hashes unique to this PR branch
|
|
||||||
COMMITS=$(git log --no-merges --pretty=format:"%H" origin/${{ github.base_ref }}..HEAD)
|
|
||||||
|
|
||||||
MAX_LINES=3
|
|
||||||
MAX_CHARS=90
|
|
||||||
FAILED=0
|
|
||||||
|
|
||||||
for commit in $COMMITS; do
|
|
||||||
SUBJECT=$(git log --format="%s" -n 1 $commit)
|
|
||||||
|
|
||||||
# Extract clean commit message, trimming trailing blank lines
|
|
||||||
RAW_MSG=$(git log --format="%B" -n 1 $commit)
|
|
||||||
CLEAN_MSG=$(echo "$RAW_MSG" | awk '{msg[NR]=$0} END {while(NR>0 && msg[NR]=="") NR--; for(i=1;i<=NR;i++) print msg[i]}')
|
|
||||||
|
|
||||||
# 1. Check total line count
|
|
||||||
LINE_COUNT=$(echo "$CLEAN_MSG" | wc -l)
|
|
||||||
if [ "$LINE_COUNT" -gt "$MAX_LINES" ]; then
|
|
||||||
echo "❌ Error: Commit message has too many lines ($LINE_COUNT/$MAX_LINES)."
|
|
||||||
echo " Commit: '$SUBJECT'"
|
|
||||||
FAILED=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 2. Check for multiple consecutive blank lines
|
|
||||||
# This regex looks for 2 or more empty lines anywhere in the message
|
|
||||||
if echo "$CLEAN_MSG" | grep -Pz '(\r?\n){3,}'; then
|
|
||||||
echo "❌ Error: Commit message contains multiple consecutive line breaks."
|
|
||||||
echo " Commit: '$SUBJECT'"
|
|
||||||
FAILED=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 3. Check maximum width of any individual line
|
|
||||||
while IFS= read -r line; do
|
|
||||||
LINE_LENGTH=${#line}
|
|
||||||
if [ "$LINE_LENGTH" -gt "$MAX_CHARS" ]; then
|
|
||||||
echo "❌ Error: Line length exceeds limit ($LINE_LENGTH / $MAX_CHARS chars)."
|
|
||||||
echo " Offending line: '$line'"
|
|
||||||
FAILED=1
|
|
||||||
fi
|
|
||||||
done <<< "$CLEAN_MSG"
|
|
||||||
|
|
||||||
done
|
|
||||||
|
|
||||||
# Fail the job if any check failed
|
|
||||||
if [ "$FAILED" -ne 0 ]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "✅ All commit messages passed style rules!"
|
|
||||||
+4
-6
@@ -1,6 +1,6 @@
|
|||||||
m4_pattern_allow([PKG_CHECK_EXISTS])
|
m4_pattern_allow([PKG_CHECK_EXISTS])
|
||||||
|
|
||||||
AC_INIT([rtorrent],[0.16.24],[sundell.software@gmail.com])
|
AC_INIT([rtorrent],[0.16.17],[sundell.software@gmail.com])
|
||||||
|
|
||||||
AC_CONFIG_HEADERS([config.h])
|
AC_CONFIG_HEADERS([config.h])
|
||||||
AC_CONFIG_MACRO_DIRS([scripts])
|
AC_CONFIG_MACRO_DIRS([scripts])
|
||||||
@@ -14,7 +14,7 @@ AX_CXX_COMPILE_STDCXX(20, noext, mandatory)
|
|||||||
|
|
||||||
PKG_PROG_PKG_CONFIG
|
PKG_PROG_PKG_CONFIG
|
||||||
|
|
||||||
AC_DEFINE([API_VERSION], [28], [api version])
|
AC_DEFINE([API_VERSION], [23], [api version])
|
||||||
|
|
||||||
RAK_CHECK_CFLAGS
|
RAK_CHECK_CFLAGS
|
||||||
RAK_CHECK_CXXFLAGS
|
RAK_CHECK_CXXFLAGS
|
||||||
@@ -22,8 +22,6 @@ RAK_ENABLE_DEBUG
|
|||||||
RAK_ENABLE_EXTRA_DEBUG
|
RAK_ENABLE_EXTRA_DEBUG
|
||||||
RAK_ENABLE_WERROR
|
RAK_ENABLE_WERROR
|
||||||
|
|
||||||
TORRENT_CHECK_ATOMIC
|
|
||||||
|
|
||||||
AC_ARG_ENABLE(execinfo,
|
AC_ARG_ENABLE(execinfo,
|
||||||
AS_HELP_STRING([--disable-execinfo],
|
AS_HELP_STRING([--disable-execinfo],
|
||||||
[disable libexecinfo [[default=enable]]]),
|
[disable libexecinfo [[default=enable]]]),
|
||||||
@@ -49,7 +47,7 @@ fi
|
|||||||
|
|
||||||
PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
|
PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
|
||||||
PKG_CHECK_MODULES([ZLIB], [zlib])
|
PKG_CHECK_MODULES([ZLIB], [zlib])
|
||||||
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.24])
|
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.17])
|
||||||
|
|
||||||
AC_LANG_PUSH(C++)
|
AC_LANG_PUSH(C++)
|
||||||
TORRENT_WITH_XMLRPC_C
|
TORRENT_WITH_XMLRPC_C
|
||||||
@@ -70,7 +68,7 @@ AC_DEFINE(USER_AGENT, [std::string(PACKAGE "/" VERSION)], Http user agent)
|
|||||||
|
|
||||||
dnl Only update global build variables immediately before generating the output,
|
dnl Only update global build variables immediately before generating the output,
|
||||||
dnl to avoid affecting the global build environment for other autoconf checks.
|
dnl to avoid affecting the global build environment for other autoconf checks.
|
||||||
LIBS="$ATOMIC_LIBS $PTHREAD_LIBS $CURSES_LIB $CURSES_LIBS $ZLIB_LIBS $DEPENDENCIES_LIBS $LIBS"
|
LIBS="$PTHREAD_LIBS $CURSES_LIB $CURSES_LIBS $ZLIB_LIBS $DEPENDENCIES_LIBS $LIBS"
|
||||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS"
|
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS"
|
||||||
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS"
|
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS"
|
||||||
|
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
# Resolving paths
|
|
||||||
|
|
||||||
Commands that return a path have `.realpath` variants that resolve it to a
|
|
||||||
canonical one, with symlinks followed and any `.` or `..` components removed.
|
|
||||||
|
|
||||||
The intent is to make paths safer to hand to an external script.
|
|
||||||
A script called through `execute` receives whatever path rtorrent gives it, and
|
|
||||||
many scripts do no sanity checking of their own, so resolving the path before it
|
|
||||||
leaves rtorrent removes a class of surprises: a download directory that is a
|
|
||||||
symlink into somewhere unexpected, or a torrent whose name walks upwards out of
|
|
||||||
the directory it is supposed to live in.
|
|
||||||
|
|
||||||
# Instead of this
|
|
||||||
execute = ~/bin/on-finished, (d.base_path)
|
|
||||||
|
|
||||||
# Pass the resolved path
|
|
||||||
execute = ~/bin/on-finished, (d.base_path.realpath.or_throw)
|
|
||||||
|
|
||||||
## Available variants
|
|
||||||
|
|
||||||
Each command below comes in an `.or_empty` and an `.or_throw` form.
|
|
||||||
|
|
||||||
| Command | Resolves |
|
|
||||||
| --- | --- |
|
|
||||||
| `d.base_path.realpath.*` | `d.base_path` |
|
|
||||||
| `d.directory.realpath.*` | `d.directory` |
|
|
||||||
| `d.tied_to_file.realpath.*` | `d.tied_to_file` |
|
|
||||||
| `d.loaded_file.realpath.*` | `d.loaded_file` |
|
|
||||||
| `f.frozen_path.realpath.*` | `f.frozen_path` |
|
|
||||||
| `session.path.realpath.*` | `session.path` |
|
|
||||||
| `directory.default.realpath.*` | `directory.default` |
|
|
||||||
|
|
||||||
A leading `~` is expanded first, exactly as it is for `execute`, so
|
|
||||||
`~/downloads` resolves the same way it would on the command line.
|
|
||||||
|
|
||||||
## Paths that do not exist
|
|
||||||
|
|
||||||
Resolving requires the path to name an existing file or directory, which is
|
|
||||||
often not the case for a download whose data has not been written yet.
|
|
||||||
The two forms differ only in what they do about it.
|
|
||||||
|
|
||||||
`.or_empty` returns an empty string:
|
|
||||||
|
|
||||||
print = (d.base_path.realpath.or_empty) # ""
|
|
||||||
|
|
||||||
This keeps `d.multicall` usable over a view that mixes started and unstarted
|
|
||||||
downloads, since a single unresolvable path does not abort the whole call.
|
|
||||||
A script receiving one of these paths should still check that it is not empty
|
|
||||||
before acting on it.
|
|
||||||
|
|
||||||
`.or_throw` raises an error instead:
|
|
||||||
|
|
||||||
print = (d.base_path.realpath.or_throw) # Could not resolve path: '...'
|
|
||||||
|
|
||||||
Prefer this one wherever an unresolvable path means the command should not run
|
|
||||||
at all, such as a single `execute` on `event.download.finished`.
|
|
||||||
|
|
||||||
Note that a download only has a file list once it has been opened, so
|
|
||||||
`d.base_path` and `f.frozen_path` are empty at `event.download.inserted` time
|
|
||||||
and their `.realpath` variants resolve nothing.
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
# String functions
|
|
||||||
|
|
||||||
The `string.*` commands manipulate and inspect text from within the
|
|
||||||
configuration file.
|
|
||||||
They are pure functions with no side effects, so they can be nested freely and
|
|
||||||
are safe to call over RPC.
|
|
||||||
|
|
||||||
Every argument is converted to its string representation before use, which means
|
|
||||||
numbers can be passed where text is expected.
|
|
||||||
Commands that count characters, such as `string.length` and `string.substr`, count
|
|
||||||
utf-8 characters rather than bytes.
|
|
||||||
|
|
||||||
## string.length
|
|
||||||
|
|
||||||
# string.length = «text»
|
|
||||||
|
|
||||||
string.length = "héllo" # 5
|
|
||||||
|
|
||||||
Returns the number of utf-8 characters in the text.
|
|
||||||
|
|
||||||
## string.equals
|
|
||||||
|
|
||||||
# string.equals = «text», «other»[, ...]
|
|
||||||
|
|
||||||
string.equals = (d.name), "first.iso", "second.iso"
|
|
||||||
|
|
||||||
Returns `1` if the first argument equals any of the following arguments,
|
|
||||||
otherwise `0`.
|
|
||||||
|
|
||||||
## string.starts_with, string.ends_with
|
|
||||||
|
|
||||||
# string.starts_with = «text», «prefix»[, ...]
|
|
||||||
# string.ends_with = «text», «tail»[, ...]
|
|
||||||
|
|
||||||
string.starts_with = (t.url), "http://", "https://"
|
|
||||||
string.ends_with = (d.name), ".iso"
|
|
||||||
|
|
||||||
Returns `1` if the text begins, or ends, with any of the given prefixes or
|
|
||||||
tails.
|
|
||||||
|
|
||||||
## string.contains, string.contains_i
|
|
||||||
|
|
||||||
# string.contains = «haystack», «needle»[, ...]
|
|
||||||
# string.contains_i = «haystack», «needle»[, ...]
|
|
||||||
|
|
||||||
string.contains = (t.url), "retracker.local"
|
|
||||||
string.contains_i = (t.url), "RETRACKER.local"
|
|
||||||
|
|
||||||
Returns `1` if the haystack contains any of the needles.
|
|
||||||
The `_i` variant compares case-insensitively, and only handles ascii.
|
|
||||||
|
|
||||||
## string.substr
|
|
||||||
|
|
||||||
# string.substr = «text»[, «position»[, «count»[, «default»]]]
|
|
||||||
|
|
||||||
string.substr = "abcdef", 2, 3 # "cde"
|
|
||||||
string.substr = "abcdef", -2 # "ef"
|
|
||||||
string.substr = "abcdef", 10, 1, "?" # "?"
|
|
||||||
|
|
||||||
Extracts a part of the text, starting at `position` and spanning `count`
|
|
||||||
characters.
|
|
||||||
The position defaults to the start of the text, and the count to the rest of it.
|
|
||||||
A negative position is relative to the end of the text.
|
|
||||||
If the position falls outside the text, the default value is returned instead,
|
|
||||||
which is the empty string unless given.
|
|
||||||
|
|
||||||
## string.split
|
|
||||||
|
|
||||||
# string.split = «text», «delimiter»
|
|
||||||
|
|
||||||
string.split = "a.b.c", "." # {"a", "b", "c"}
|
|
||||||
string.split = "abc", "" # {"a", "b", "c"}
|
|
||||||
|
|
||||||
Splits the text into a list, keeping empty fields.
|
|
||||||
An empty delimiter splits the text into its utf-8 characters.
|
|
||||||
|
|
||||||
## string.join
|
|
||||||
|
|
||||||
# string.join = «delimiter»[, «object»[, ...]]
|
|
||||||
|
|
||||||
string.join = "-", (string.split, "a.b.c", ".") # "a-b-c"
|
|
||||||
|
|
||||||
Concatenates the objects, inserting the delimiter between them.
|
|
||||||
Lists are flattened, so the result of `string.split` can be joined back
|
|
||||||
together.
|
|
||||||
|
|
||||||
## string.lpad, string.rpad
|
|
||||||
|
|
||||||
# string.lpad = «text», «length»[, «padding»]
|
|
||||||
# string.rpad = «text», «length»[, «padding»]
|
|
||||||
|
|
||||||
string.lpad = 7, 3, 0 # "007"
|
|
||||||
string.rpad = "a", 3 # "a "
|
|
||||||
|
|
||||||
Pads the text at the start, or the end, until it is `length` characters long.
|
|
||||||
The padding defaults to a single space and is repeated as needed.
|
|
||||||
Text that is already long enough is returned unchanged.
|
|
||||||
|
|
||||||
## string.strip, string.lstrip, string.rstrip
|
|
||||||
|
|
||||||
# string.strip = «text»[, «strippable»[, ...]]
|
|
||||||
# string.lstrip = «text»[, «head»[, ...]]
|
|
||||||
# string.rstrip = «text»[, «tail»[, ...]]
|
|
||||||
|
|
||||||
string.strip = " padded " # "padded"
|
|
||||||
string.strip = "//path//", "/" # "path"
|
|
||||||
|
|
||||||
Removes characters from both ends of the text, or from only the start or the
|
|
||||||
end.
|
|
||||||
The arguments after the text form a set of utf-8 characters to remove.
|
|
||||||
Without them, whitespace is removed.
|
|
||||||
|
|
||||||
## string.map
|
|
||||||
|
|
||||||
# string.map = «text», {«old», «new»}[, ...]
|
|
||||||
|
|
||||||
string.map = (d.state), {0, "stopped"}, {1, "started"}
|
|
||||||
|
|
||||||
Returns the replacement of the first pair whose `old` value equals the whole
|
|
||||||
text.
|
|
||||||
If no pair matches, the text is returned unchanged.
|
|
||||||
|
|
||||||
## string.replace
|
|
||||||
|
|
||||||
# string.replace = «text», {«old», «new»}[, ...]
|
|
||||||
|
|
||||||
string.replace = "a-b-c", {"-", "+"} # "a+b+c"
|
|
||||||
|
|
||||||
Replaces every occurrence of `old` with `new`.
|
|
||||||
The pairs are applied in order, so a later pair operates on the result of the
|
|
||||||
earlier ones.
|
|
||||||
|
|
||||||
## Example: dropping unwanted trackers
|
|
||||||
|
|
||||||
The following disables every tracker that points at `retracker.local` as soon as
|
|
||||||
a download is inserted.
|
|
||||||
|
|
||||||
method.set_key = event.download.inserted, drop_retracker, \
|
|
||||||
((t.multicall, default, "branch=(string.contains,(t.url),retracker.local),((t.disable))"))
|
|
||||||
+9
-15
@@ -214,21 +214,15 @@ Add a preferred filename encoding to the list. The encodings are
|
|||||||
attempted in the order they are inserted, if none match the torrent
|
attempted in the order they are inserted, if none match the torrent
|
||||||
default is used.
|
default is used.
|
||||||
.TP
|
.TP
|
||||||
\fBencryption = \fIoption\fB,\fI\&...\fB\fR
|
\fBprotocol.encryption.handshake.set\fR (deny, allow, prefer, require)
|
||||||
Set how rtorrent should deal with encrypted Bittorrent connections. By
|
and \fBprotocol.encryption.stream.set\fR (deny, allow, prefer,
|
||||||
default, encryption is disabled, equivalent to specifying the option
|
require) control how rtorrent deals with encrypted Bittorrent
|
||||||
\fBnone\fR\&. Alternatively, any number of the following
|
connections. Handshake \fBallow\fR accepts plain or PE inbound and
|
||||||
options may be specified:
|
tries plain first outbound with one PE retry on failure.
|
||||||
|
\fBprefer\fR tries PE first with one plain retry. Stream \fBallow\fR
|
||||||
\fBallow_incoming\fR (allow incoming encrypted connections),
|
and \fBprefer\fR offer both handshake-only and RC4 on outgoing PE
|
||||||
\fBtry_outgoing\fR (use encryption for outgoing connections),
|
connections. The default is handshake=allow, stream=allow. Use
|
||||||
\fBrequire\fR (disable unencrypted handshakes),
|
\fBprotocol.encryption\fR to inspect the effective policy.
|
||||||
\fBrequire_RC4\fR (also disable plaintext transmission after the
|
|
||||||
initial encrypted handshake),
|
|
||||||
\fBenable_retry\fR (if the initial outgoing connection fails, retry
|
|
||||||
with encryption turned on if it was off or off if it was on),
|
|
||||||
\fBprefer_plaintext\fR (choose plaintext when peer offers a choice
|
|
||||||
between plaintext transmission and RC4 encryption, otherwise RC4 will be used).
|
|
||||||
.TP
|
.TP
|
||||||
\fBpeer_exchange = \fIyes | no\fB\fR
|
\fBpeer_exchange = \fIyes | no\fB\fR
|
||||||
Enable/disable peer exchange for torrents that aren't marked private. Disabled by default.
|
Enable/disable peer exchange for torrents that aren't marked private. Disabled by default.
|
||||||
|
|||||||
+11
-17
@@ -484,25 +484,19 @@ default is used.
|
|||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term>encryption = <replaceable>option</replaceable>,<replaceable>...</replaceable></term>
|
<term>protocol.encryption.handshake.set = <replaceable>policy</replaceable></term>
|
||||||
<listitem><para>
|
<listitem><para>
|
||||||
|
|
||||||
Set how rtorrent should deal with encrypted Bittorrent connections. By
|
Control how rtorrent deals with encrypted Bittorrent connections.
|
||||||
default, encryption is disabled, equivalent to specifying the option
|
<emphasis>protocol.encryption.handshake.set</emphasis> (deny, allow, prefer,
|
||||||
<emphasis>none</emphasis>. Alternatively, any number of the following
|
require) and <emphasis>protocol.encryption.stream.set</emphasis> (deny, allow,
|
||||||
options may be specified:
|
prefer, require). Handshake <emphasis>allow</emphasis> accepts plain or PE
|
||||||
|
inbound and tries plain first outbound with one PE retry on failure.
|
||||||
</para><para>
|
<emphasis>prefer</emphasis> tries PE first with one plain retry. Stream
|
||||||
|
<emphasis>allow</emphasis> and <emphasis>prefer</emphasis> offer both
|
||||||
<emphasis>allow_incoming</emphasis> (allow incoming encrypted connections),
|
handshake-only and RC4 on outgoing PE connections. The default is
|
||||||
<emphasis>try_outgoing</emphasis> (use encryption for outgoing connections),
|
handshake=allow, stream=allow. Use <emphasis>protocol.encryption</emphasis> to
|
||||||
<emphasis>require</emphasis> (disable unencrypted handshakes),
|
inspect the effective policy.
|
||||||
<emphasis>require_RC4</emphasis> (also disable plaintext transmission after the
|
|
||||||
initial encrypted handshake),
|
|
||||||
<emphasis>enable_retry</emphasis> (if the initial outgoing connection fails, retry
|
|
||||||
with encryption turned on if it was off or off if it was on),
|
|
||||||
<emphasis>prefer_plaintext</emphasis> (choose plaintext when peer offers a choice
|
|
||||||
between plaintext transmission and RC4 encryption, otherwise RC4 will be used).
|
|
||||||
|
|
||||||
</para></listitem>
|
</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|||||||
+34
-16
@@ -63,14 +63,6 @@
|
|||||||
#network.local_address.set = 127.0.0.1
|
#network.local_address.set = 127.0.0.1
|
||||||
#network.local_address.set = rakshasa.no
|
#network.local_address.set = rakshasa.no
|
||||||
|
|
||||||
# The port reported to the tracker, separate for ipv4/ipv6. A value of 0
|
|
||||||
# (the default) falls back to reporting the listening port. network.local_port.set
|
|
||||||
# sets both the ipv4 and ipv6 ports at once.
|
|
||||||
#
|
|
||||||
#network.local_port.set = 6881
|
|
||||||
#network.local_port.ipv4.set = 6881
|
|
||||||
#network.local_port.ipv6.set = 6882
|
|
||||||
|
|
||||||
# The IP address the listening socket and outgoing connections is
|
# The IP address the listening socket and outgoing connections is
|
||||||
# bound to.
|
# bound to.
|
||||||
#
|
#
|
||||||
@@ -79,11 +71,11 @@
|
|||||||
|
|
||||||
# Port range to use for listening.
|
# Port range to use for listening.
|
||||||
#
|
#
|
||||||
#network.listen.port.range.set = 6890-6999
|
#network.port_range.set = 6890-6999
|
||||||
|
|
||||||
# Start opening ports at a random position within the port range.
|
# Start opening ports at a random position within the port range.
|
||||||
#
|
#
|
||||||
#network.listen.port.random.set = no
|
#network.port_random.set = no
|
||||||
|
|
||||||
# Set RPC type
|
# Set RPC type
|
||||||
#network.rpc.use_xmlrpc.set = true
|
#network.rpc.use_xmlrpc.set = true
|
||||||
@@ -103,14 +95,40 @@
|
|||||||
#schedule2 = ip_tick,0,1800,ip=rakshasa
|
#schedule2 = ip_tick,0,1800,ip=rakshasa
|
||||||
#schedule2 = bind_tick,0,1800,bind=rakshasa
|
#schedule2 = bind_tick,0,1800,bind=rakshasa
|
||||||
|
|
||||||
# Encryption options, set to none (default) or any combination of the following:
|
# --- Protocol encryption (PE handshake / optional RC4 stream) ---
|
||||||
# allow_incoming, try_outgoing, require, require_RC4, enable_retry, prefer_plaintext
|
# Default at startup: handshake=allow stream=allow
|
||||||
#
|
#
|
||||||
# The example value allows incoming encrypted connections, starts unencrypted
|
# Handshake (PE handshake; incoming accepts plain or PE for allow/prefer):
|
||||||
# outgoing connections but retries with encryption if they fail, preferring
|
# deny - plain BT handshake only both ways; no retry
|
||||||
# plain-text to RC4 encryption after the encrypted handshake.
|
# allow - accept plain or PE inbound; plain first outbound with one PE retry
|
||||||
|
# prefer - accept plain or PE inbound; PE first outbound with one plain retry
|
||||||
|
# require - PE handshake required both ways; no retry
|
||||||
#
|
#
|
||||||
# protocol.encryption.set = allow_incoming,enable_retry,prefer_plaintext
|
# protocol.encryption.handshake.set = allow
|
||||||
|
#
|
||||||
|
# Stream (cipher negotiation after PE handshake succeeds):
|
||||||
|
# deny - offer handshake-only; require handshake-only (no RC4)
|
||||||
|
# allow - incoming: prefer handshake-only if offered, else RC4
|
||||||
|
# outgoing: offer both handshake-only and RC4
|
||||||
|
# prefer - incoming: pick RC4 if offered, else handshake-only
|
||||||
|
# outgoing: offer both handshake-only and RC4
|
||||||
|
# require - offer RC4 only; require RC4
|
||||||
|
#
|
||||||
|
# Outgoing retry summary with handshake=allow (plain first):
|
||||||
|
# stream=allow/prefer -> plain BT -> PE (both)
|
||||||
|
# stream=deny -> plain BT -> PE (handshake-only)
|
||||||
|
# stream=require -> plain BT -> PE (RC4-only)
|
||||||
|
#
|
||||||
|
# Outgoing retry summary with handshake=prefer (PE first):
|
||||||
|
# stream=allow/prefer -> PE (both) -> plain BT
|
||||||
|
# stream=deny -> PE (handshake-only) -> plain BT
|
||||||
|
# stream=require -> PE (RC4-only) -> plain BT
|
||||||
|
#
|
||||||
|
# Negotiated stream cipher is logged at connection_handshake level.
|
||||||
|
#
|
||||||
|
# protocol.encryption.stream.set = allow
|
||||||
|
#
|
||||||
|
# Use protocol.encryption to inspect the effective policy.
|
||||||
|
|
||||||
# Enable DHT support for trackerless torrents or when all trackers are down.
|
# Enable DHT support for trackerless torrents or when all trackers are down.
|
||||||
# May be set to "disable" (completely disable DHT), "off" (do not start DHT),
|
# May be set to "disable" (completely disable DHT), "off" (do not start DHT),
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ execute.throw = sh, -c, (cat,\
|
|||||||
"\"",(cfg.watch),"/start\" ")
|
"\"",(cfg.watch),"/start\" ")
|
||||||
|
|
||||||
# Listening port for incoming peer traffic (fixed; you can also randomize it)
|
# Listening port for incoming peer traffic (fixed; you can also randomize it)
|
||||||
network.listen.port.range.set = 50000-50000
|
network.port_range.set = 50000-50000
|
||||||
network.listen.port.random.set = no
|
network.port_random.set = no
|
||||||
|
|
||||||
# Tracker-less torrent and UDP tracker support
|
# Tracker-less torrent and UDP tracker support
|
||||||
# (conservative settings for 'private' trackers, change for 'public')
|
# (conservative settings for 'private' trackers, change for 'public')
|
||||||
@@ -42,7 +42,8 @@ throttle.min_peers.seed.set = 30
|
|||||||
throttle.max_peers.seed.set = 80
|
throttle.max_peers.seed.set = 80
|
||||||
trackers.numwant.set = 80
|
trackers.numwant.set = 80
|
||||||
|
|
||||||
protocol.encryption.set = allow_incoming,try_outgoing,enable_retry
|
protocol.encryption.handshake.set = allow
|
||||||
|
protocol.encryption.stream.set = prefer
|
||||||
|
|
||||||
# Limits for file handle resources, this is optimized for
|
# Limits for file handle resources, this is optimized for
|
||||||
# an `ulimit` of 1024 (a common default). You MUST leave
|
# an `ulimit` of 1024 (a common default). You MUST leave
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ rc.execute.throw(
|
|||||||
cfg.watch..'/start'}))
|
cfg.watch..'/start'}))
|
||||||
|
|
||||||
-- Listening port for incoming peer traffic (fixed; you can also randomize it)
|
-- Listening port for incoming peer traffic (fixed; you can also randomize it)
|
||||||
rc.network.listen.port.range = '50000-50000'
|
rc.network.port_range = '50000-50000'
|
||||||
rc.network.listen.port.random = false
|
rc.network.port_random = false
|
||||||
|
|
||||||
-- Tracker-less torrent and UDP tracker support
|
-- Tracker-less torrent and UDP tracker support
|
||||||
-- (conservative settings for 'private' trackers, change for 'public')
|
-- (conservative settings for 'private' trackers, change for 'public')
|
||||||
@@ -72,7 +72,8 @@ rc.throttle.min_peers.seed = 30
|
|||||||
rc.throttle.max_peers.seed = 80
|
rc.throttle.max_peers.seed = 80
|
||||||
rc.trackers.numwant = 80
|
rc.trackers.numwant = 80
|
||||||
|
|
||||||
rc.protocol.encryption.set('allow_incoming', 'try_outgoing', 'enable_retry')
|
rc.protocol.encryption.handshake.set('allow')
|
||||||
|
rc.protocol.encryption.stream.set('prefer')
|
||||||
|
|
||||||
-- Limits for file handle resources, this is optimized for
|
-- Limits for file handle resources, this is optimized for
|
||||||
-- an `ulimit` of 1024 (a common default). You MUST leave
|
-- an `ulimit` of 1024 (a common default). You MUST leave
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ echo "Client version: " `xmlrpc2scgi.py -p scgi://127.0.0.1:${PORT_NUMBER} syste
|
|||||||
echo
|
echo
|
||||||
echo "Generated by 'rtorrent/doc/scripts/print_option_string.sh' on `date -u`."
|
echo "Generated by 'rtorrent/doc/scripts/print_option_string.sh' on `date -u`."
|
||||||
|
|
||||||
for i in strings.choke_heuristics strings.choke_heuristics.upload strings.choke_heuristics.download strings.connection_type strings.encryption strings.ip_filter strings.ip_tos strings.log_group strings.tracker_event strings.tracker_mode; do
|
for i in strings.choke_heuristics strings.choke_heuristics.upload strings.choke_heuristics.download strings.connection_type strings.encryption.handshake strings.encryption.stream strings.ip_filter strings.ip_tos strings.log_group strings.tracker_event strings.tracker_mode; do
|
||||||
echo
|
echo
|
||||||
echo $i
|
echo $i
|
||||||
echo `echo $i | tr 'a-z_.' '-'`
|
echo `echo $i | tr 'a-z_.' '-'`
|
||||||
|
|||||||
+4
-4
@@ -17,6 +17,7 @@ libsub_root_a_SOURCES = \
|
|||||||
core/http_queue.h \
|
core/http_queue.h \
|
||||||
core/manager.cc \
|
core/manager.cc \
|
||||||
core/manager.h \
|
core/manager.h \
|
||||||
|
core/range_map.h \
|
||||||
core/view.cc \
|
core/view.cc \
|
||||||
core/view.h \
|
core/view.h \
|
||||||
core/view_manager.cc \
|
core/view_manager.cc \
|
||||||
@@ -170,10 +171,9 @@ libsub_root_a_SOURCES = \
|
|||||||
utils/functional.h \
|
utils/functional.h \
|
||||||
utils/gzip.cc \
|
utils/gzip.cc \
|
||||||
utils/gzip.h \
|
utils/gzip.h \
|
||||||
|
utils/list_focus.h \
|
||||||
utils/lockfile.cc \
|
utils/lockfile.cc \
|
||||||
utils/lockfile.h \
|
utils/lockfile.h \
|
||||||
utils/waitpid_queue.cc \
|
|
||||||
utils/waitpid_queue.h \
|
|
||||||
utils/watch_ready_queue.cc \
|
utils/watch_ready_queue.cc \
|
||||||
utils/watch_ready_queue.h \
|
utils/watch_ready_queue.h \
|
||||||
\
|
\
|
||||||
@@ -188,12 +188,12 @@ libsub_root_a_SOURCES = \
|
|||||||
command_local.cc \
|
command_local.cc \
|
||||||
command_logging.cc \
|
command_logging.cc \
|
||||||
command_network.cc \
|
command_network.cc \
|
||||||
|
encryption_config.cc \
|
||||||
|
encryption_config.h \
|
||||||
command_peer.cc \
|
command_peer.cc \
|
||||||
command_throttle.cc \
|
command_throttle.cc \
|
||||||
command_tracker.cc \
|
command_tracker.cc \
|
||||||
command_scheduler.cc \
|
command_scheduler.cc \
|
||||||
command_string.cc \
|
|
||||||
command_system.cc \
|
|
||||||
command_ui.cc \
|
command_ui.cc \
|
||||||
control.cc \
|
control.cc \
|
||||||
control.h \
|
control.h \
|
||||||
|
|||||||
+16
-33
@@ -142,6 +142,16 @@ apply_d_delete_tied(core::Download* download) {
|
|||||||
return torrent::Object();
|
return torrent::Object();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
apply_d_directory(core::Download* download, const std::string& name) {
|
||||||
|
if (!download->file_list()->is_multi_file())
|
||||||
|
download->set_root_directory(name);
|
||||||
|
else if (name.empty() || *name.rbegin() == '/')
|
||||||
|
download->set_root_directory(name + download->info()->name().str());
|
||||||
|
else
|
||||||
|
download->set_root_directory(name + "/" + download->info()->name().str());
|
||||||
|
}
|
||||||
|
|
||||||
torrent::Object
|
torrent::Object
|
||||||
apply_d_connection_type(core::Download* download, const std::string& name) {
|
apply_d_connection_type(core::Download* download, const std::string& name) {
|
||||||
torrent::Download::ConnectionType t =
|
torrent::Download::ConnectionType t =
|
||||||
@@ -402,21 +412,13 @@ p_multicall(core::Download* download, const torrent::Object::list_type& args) {
|
|||||||
auto resultRaw = torrent::Object::create_list();
|
auto resultRaw = torrent::Object::create_list();
|
||||||
auto& result = resultRaw.as_list();
|
auto& result = resultRaw.as_list();
|
||||||
|
|
||||||
auto* connection_list = download->connection_list();
|
for (const auto& connection : *download->connection_list()) {
|
||||||
const auto change_counter = connection_list->change_counter();
|
|
||||||
|
|
||||||
for (const auto& connection : *connection_list) {
|
|
||||||
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
|
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
|
||||||
|
|
||||||
for (auto cItr = ++args.begin(); cItr != args.end(); cItr++) {
|
for (auto cItr = ++args.begin(); cItr != args.end(); cItr++) {
|
||||||
const std::string& cmd = cItr->as_string();
|
const std::string& cmd = cItr->as_string();
|
||||||
|
|
||||||
row.push_back(rpc::parse_command(rpc::make_target(connection), cmd.c_str(), cmd.c_str() + cmd.size()).first);
|
row.push_back(rpc::parse_command(rpc::make_target(connection), cmd.c_str(), cmd.c_str() + cmd.size()).first);
|
||||||
|
|
||||||
// Erasing a peer frees it and swaps the last element into its place, so
|
|
||||||
// neither this peer nor the iteration survives a change to the list.
|
|
||||||
if (connection_list->change_counter() != change_counter)
|
|
||||||
throw torrent::input_error("Command changed the connection list during p.multicall.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,9 +441,6 @@ p_call_target(const torrent::Object::list_type& args) {
|
|||||||
const std::string& peer_id = itr++->as_string();
|
const std::string& peer_id = itr++->as_string();
|
||||||
const std::string& command_key = itr++->as_string();
|
const std::string& command_key = itr++->as_string();
|
||||||
|
|
||||||
if (download == nullptr)
|
|
||||||
throw torrent::input_error("invalid parameters: info-hash not found");
|
|
||||||
|
|
||||||
torrent::HashString hash;
|
torrent::HashString hash;
|
||||||
|
|
||||||
if (peer_id.size() != 40)
|
if (peer_id.size() != 40)
|
||||||
@@ -656,8 +655,6 @@ initialize_command_download() {
|
|||||||
CMD2_DL("d.base_path.as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_as_binary(); });
|
CMD2_DL("d.base_path.as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_as_binary(); });
|
||||||
CMD2_DL("d.base_path.or_base64", [](auto* download, auto) { return retrieve_d_base_path(download).object_utf8_or_base64(); });
|
CMD2_DL("d.base_path.or_base64", [](auto* download, auto) { return retrieve_d_base_path(download).object_utf8_or_base64(); });
|
||||||
CMD2_DL("d.base_path.or_as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_utf8_or_as_binary(); });
|
CMD2_DL("d.base_path.or_as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_utf8_or_as_binary(); });
|
||||||
CMD2_DL("d.base_path.realpath.or_empty", [](auto* download, auto) { return resolve_path(retrieve_d_base_path(download).str()); });
|
|
||||||
CMD2_DL("d.base_path.realpath.or_throw", [](auto* download, auto) { return resolve_path_or_throw(retrieve_d_base_path(download).str()); });
|
|
||||||
CMD2_DL("d.base_filename", [](auto* download, auto) { return retrieve_d_base_filename(download).str(); });
|
CMD2_DL("d.base_filename", [](auto* download, auto) { return retrieve_d_base_filename(download).str(); });
|
||||||
CMD2_DL("d.base_filename.hex", [](auto* download, auto) { return retrieve_d_base_filename(download).object_hex(); });
|
CMD2_DL("d.base_filename.hex", [](auto* download, auto) { return retrieve_d_base_filename(download).object_hex(); });
|
||||||
CMD2_DL("d.base_filename.base64", [](auto* download, auto) { return retrieve_d_base_filename(download).object_base64(); });
|
CMD2_DL("d.base_filename.base64", [](auto* download, auto) { return retrieve_d_base_filename(download).object_base64(); });
|
||||||
@@ -719,7 +716,7 @@ initialize_command_download() {
|
|||||||
CMD2_DL_V ("d.pause", std::bind(&core::DownloadList::pause_default, control->core()->download_list(), std::placeholders::_1));
|
CMD2_DL_V ("d.pause", std::bind(&core::DownloadList::pause_default, control->core()->download_list(), std::placeholders::_1));
|
||||||
CMD2_DL_V ("d.open", std::bind(&core::DownloadList::open_throw, control->core()->download_list(), std::placeholders::_1));
|
CMD2_DL_V ("d.open", std::bind(&core::DownloadList::open_throw, control->core()->download_list(), std::placeholders::_1));
|
||||||
CMD2_DL_V ("d.close", std::bind(&core::DownloadList::close_throw, control->core()->download_list(), std::placeholders::_1));
|
CMD2_DL_V ("d.close", std::bind(&core::DownloadList::close_throw, control->core()->download_list(), std::placeholders::_1));
|
||||||
CMD2_DL_V ("d.close.directly", std::bind(&core::DownloadList::close_files, control->core()->download_list(), std::placeholders::_1));
|
CMD2_DL_V ("d.close.directly", std::bind(&core::DownloadList::close_directly, control->core()->download_list(), std::placeholders::_1));
|
||||||
CMD2_DL_V ("d.erase", std::bind(&core::DownloadList::erase_ptr, control->core()->download_list(), std::placeholders::_1));
|
CMD2_DL_V ("d.erase", std::bind(&core::DownloadList::erase_ptr, control->core()->download_list(), std::placeholders::_1));
|
||||||
CMD2_DL_V ("d.check_hash", std::bind(&core::DownloadList::check_hash, control->core()->download_list(), std::placeholders::_1));
|
CMD2_DL_V ("d.check_hash", std::bind(&core::DownloadList::check_hash, control->core()->download_list(), std::placeholders::_1));
|
||||||
|
|
||||||
@@ -774,11 +771,6 @@ initialize_command_download() {
|
|||||||
CMD2_DL_VAR_STRING_PUBLIC("d.tied_to_file", "rtorrent", "tied_to_file");
|
CMD2_DL_VAR_STRING_PUBLIC("d.tied_to_file", "rtorrent", "tied_to_file");
|
||||||
CMD2_DL_VAR_STRING("d.loaded_file", "rtorrent", "loaded_file");
|
CMD2_DL_VAR_STRING("d.loaded_file", "rtorrent", "loaded_file");
|
||||||
|
|
||||||
CMD2_DL("d.tied_to_file.realpath.or_empty", [](auto* download, auto) { return resolve_path(rpc::convert_to_string(download_get_variable(download, "rtorrent", "tied_to_file"))); });
|
|
||||||
CMD2_DL("d.tied_to_file.realpath.or_throw", [](auto* download, auto) { return resolve_path_or_throw(rpc::convert_to_string(download_get_variable(download, "rtorrent", "tied_to_file"))); });
|
|
||||||
CMD2_DL("d.loaded_file.realpath.or_empty", [](auto* download, auto) { return resolve_path(rpc::convert_to_string(download_get_variable(download, "rtorrent", "loaded_file"))); });
|
|
||||||
CMD2_DL("d.loaded_file.realpath.or_throw", [](auto* download, auto) { return resolve_path_or_throw(rpc::convert_to_string(download_get_variable(download, "rtorrent", "loaded_file"))); });
|
|
||||||
|
|
||||||
// The "state_changed" variable is required to be a valid unix time
|
// The "state_changed" variable is required to be a valid unix time
|
||||||
// value, it indicates the last time the torrent changed its state,
|
// value, it indicates the last time the torrent changed its state,
|
||||||
// resume/pause.
|
// resume/pause.
|
||||||
@@ -886,10 +878,9 @@ initialize_command_download() {
|
|||||||
CMD2_DL_VALUE_V ("d.tracker.send_scrape", [](auto download, uint64_t arg) { download->tracker_controller().scrape_request(arg); });
|
CMD2_DL_VALUE_V ("d.tracker.send_scrape", [](auto download, uint64_t arg) { download->tracker_controller().scrape_request(arg); });
|
||||||
|
|
||||||
CMD2_DL ("d.directory", CMD2_ON_FL(root_dir));
|
CMD2_DL ("d.directory", CMD2_ON_FL(root_dir));
|
||||||
CMD2_DL ("d.directory.realpath.or_empty", [](auto* download, auto) { return resolve_path(download->file_list()->root_dir()); });
|
CMD2_DL_STRING_V("d.directory.set", std::bind(&apply_d_directory, std::placeholders::_1, std::placeholders::_2));
|
||||||
CMD2_DL ("d.directory.realpath.or_throw", [](auto* download, auto) { return resolve_path_or_throw(download->file_list()->root_dir()); });
|
CMD2_DL ("d.directory_base", CMD2_ON_FL(root_dir));
|
||||||
CMD2_DL_STRING_V("d.directory.set", [](auto* download, auto arg) { download->set_directory(arg); });
|
CMD2_DL_STRING_V("d.directory_base.set", std::bind(&core::Download::set_root_directory, std::placeholders::_1, std::placeholders::_2));
|
||||||
CMD2_DL_STRING_V("d.directory.base.set", [](auto* download, auto arg) { download->set_base_directory(arg); });
|
|
||||||
|
|
||||||
CMD2_DL ("d.priority", std::bind(&core::Download::priority, std::placeholders::_1));
|
CMD2_DL ("d.priority", std::bind(&core::Download::priority, std::placeholders::_1));
|
||||||
CMD2_DL ("d.priority_str", std::bind(&retrieve_d_priority_str, std::placeholders::_1));
|
CMD2_DL ("d.priority_str", std::bind(&retrieve_d_priority_str, std::placeholders::_1));
|
||||||
@@ -921,14 +912,6 @@ initialize_command_download() {
|
|||||||
rpc::rpc.mark_safe("d.local_id_html");
|
rpc::rpc.mark_safe("d.local_id_html");
|
||||||
rpc::rpc.mark_safe("d.bitfield");
|
rpc::rpc.mark_safe("d.bitfield");
|
||||||
rpc::rpc.mark_safe("d.base_path");
|
rpc::rpc.mark_safe("d.base_path");
|
||||||
rpc::rpc.mark_safe("d.base_path.realpath.or_empty");
|
|
||||||
rpc::rpc.mark_safe("d.base_path.realpath.or_throw");
|
|
||||||
rpc::rpc.mark_safe("d.directory.realpath.or_empty");
|
|
||||||
rpc::rpc.mark_safe("d.directory.realpath.or_throw");
|
|
||||||
rpc::rpc.mark_safe("d.tied_to_file.realpath.or_empty");
|
|
||||||
rpc::rpc.mark_safe("d.tied_to_file.realpath.or_throw");
|
|
||||||
rpc::rpc.mark_safe("d.loaded_file.realpath.or_empty");
|
|
||||||
rpc::rpc.mark_safe("d.loaded_file.realpath.or_throw");
|
|
||||||
rpc::rpc.mark_safe("d.base_path.hex");
|
rpc::rpc.mark_safe("d.base_path.hex");
|
||||||
rpc::rpc.mark_safe("d.base_path.base64");
|
rpc::rpc.mark_safe("d.base_path.base64");
|
||||||
rpc::rpc.mark_safe("d.base_path.base64_as_binary");
|
rpc::rpc.mark_safe("d.base_path.base64_as_binary");
|
||||||
@@ -950,6 +933,7 @@ initialize_command_download() {
|
|||||||
rpc::rpc.mark_safe("d.name.or_base64");
|
rpc::rpc.mark_safe("d.name.or_base64");
|
||||||
rpc::rpc.mark_safe("d.name.or_as_binary");
|
rpc::rpc.mark_safe("d.name.or_as_binary");
|
||||||
rpc::rpc.mark_safe("d.directory");
|
rpc::rpc.mark_safe("d.directory");
|
||||||
|
rpc::rpc.mark_safe("d.directory_base");
|
||||||
rpc::rpc.mark_safe("d.creation_date");
|
rpc::rpc.mark_safe("d.creation_date");
|
||||||
rpc::rpc.mark_safe("d.load_date");
|
rpc::rpc.mark_safe("d.load_date");
|
||||||
rpc::rpc.mark_safe("d.up.rate");
|
rpc::rpc.mark_safe("d.up.rate");
|
||||||
@@ -990,7 +974,6 @@ initialize_command_download() {
|
|||||||
rpc::rpc.mark_safe("d.size_pex");
|
rpc::rpc.mark_safe("d.size_pex");
|
||||||
rpc::rpc.mark_safe("d.completed_bytes");
|
rpc::rpc.mark_safe("d.completed_bytes");
|
||||||
rpc::rpc.mark_safe("d.complete");
|
rpc::rpc.mark_safe("d.complete");
|
||||||
rpc::rpc.mark_safe("d.timestamp.started");
|
|
||||||
rpc::rpc.mark_safe("d.timestamp.finished");
|
rpc::rpc.mark_safe("d.timestamp.finished");
|
||||||
rpc::rpc.mark_safe("d.bytes_done");
|
rpc::rpc.mark_safe("d.bytes_done");
|
||||||
rpc::rpc.mark_safe("d.peers_accounted");
|
rpc::rpc.mark_safe("d.peers_accounted");
|
||||||
|
|||||||
+14
-18
@@ -157,7 +157,7 @@ system_method_insert_object(const torrent::Object::list_type& args, int flags) {
|
|||||||
torrent::Object valueList = torrent::Object::create_list();
|
torrent::Object valueList = torrent::Object::create_list();
|
||||||
torrent::Object::list_type& valueListType = valueList.as_list();
|
torrent::Object::list_type& valueListType = valueList.as_list();
|
||||||
|
|
||||||
if (itrArgs != args.end() && (itrArgs)->is_list())
|
if ((itrArgs)->is_list())
|
||||||
valueListType = (itrArgs)->as_list();
|
valueListType = (itrArgs)->as_list();
|
||||||
|
|
||||||
control->object_storage()->insert_str(raw_key, valueList, flags);
|
control->object_storage()->insert_str(raw_key, valueList, flags);
|
||||||
@@ -444,20 +444,17 @@ initialize_command_dynamic() {
|
|||||||
|
|
||||||
CMD2_ANY ("catch", std::bind(&cmd_catch, std::placeholders::_1, std::placeholders::_2));
|
CMD2_ANY ("catch", std::bind(&cmd_catch, std::placeholders::_1, std::placeholders::_2));
|
||||||
|
|
||||||
CMD2_ANY_STRING ("enum.log_group", [](auto, const auto& str) { return torrent::option_find_string_str(torrent::OPTION_LOG_GROUP, str); });
|
CMD2_ANY ("strings.choke_heuristics", std::bind(&torrent::option_list_strings, torrent::OPTION_CHOKE_HEURISTICS));
|
||||||
|
CMD2_ANY ("strings.choke_heuristics.upload", std::bind(&torrent::option_list_strings, torrent::OPTION_CHOKE_HEURISTICS_UPLOAD));
|
||||||
CMD2_ANY ("strings.choke_heuristics", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS); });
|
CMD2_ANY ("strings.choke_heuristics.download", std::bind(&torrent::option_list_strings, torrent::OPTION_CHOKE_HEURISTICS_DOWNLOAD));
|
||||||
CMD2_ANY ("strings.choke_heuristics.upload", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS_UPLOAD); });
|
CMD2_ANY ("strings.connection_type", std::bind(&torrent::option_list_strings, torrent::OPTION_CONNECTION_TYPE));
|
||||||
CMD2_ANY ("strings.choke_heuristics.download", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS_DOWNLOAD); });
|
CMD2_ANY ("strings.encryption.handshake", std::bind(&torrent::option_list_strings, torrent::OPTION_ENCRYPTION_MODE));
|
||||||
CMD2_ANY ("strings.connection_type", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CONNECTION_TYPE); });
|
CMD2_ANY ("strings.encryption.stream", std::bind(&torrent::option_list_strings, torrent::OPTION_ENCRYPTION_MODE));
|
||||||
CMD2_ANY ("strings.encryption", [](auto, auto) { return torrent::Object::create_list(); });
|
CMD2_ANY ("strings.ip_filter", std::bind(&torrent::option_list_strings, torrent::OPTION_IP_FILTER));
|
||||||
CMD2_ANY ("strings.encryption.handshake", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_ENCRYPTION_HANDSHAKE); });
|
CMD2_ANY ("strings.ip_tos", std::bind(&torrent::option_list_strings, torrent::OPTION_IP_TOS));
|
||||||
CMD2_ANY ("strings.encryption.stream", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_ENCRYPTION_STREAM); });
|
CMD2_ANY ("strings.log_group", std::bind(&torrent::option_list_strings, torrent::OPTION_LOG_GROUP));
|
||||||
CMD2_ANY ("strings.ip_filter", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_IP_FILTER); });
|
CMD2_ANY ("strings.tracker_event", std::bind(&torrent::option_list_strings, torrent::OPTION_TRACKER_EVENT));
|
||||||
CMD2_ANY ("strings.ip_tos", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_IP_TOS); });
|
CMD2_ANY ("strings.tracker_mode", std::bind(&torrent::option_list_strings, torrent::OPTION_TRACKER_MODE));
|
||||||
CMD2_ANY ("strings.log_group", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_LOG_GROUP); });
|
|
||||||
CMD2_ANY ("strings.tracker_event", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_TRACKER_EVENT); });
|
|
||||||
CMD2_ANY ("strings.tracker_mode", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_TRACKER_MODE); });
|
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
#ifdef HAVE_XMLRPC_TINYXML2
|
#ifdef HAVE_XMLRPC_TINYXML2
|
||||||
@@ -472,13 +469,12 @@ initialize_command_dynamic() {
|
|||||||
rpc::rpc.mark_safe("method.rlookup");
|
rpc::rpc.mark_safe("method.rlookup");
|
||||||
rpc::rpc.mark_safe("catch");
|
rpc::rpc.mark_safe("catch");
|
||||||
|
|
||||||
rpc::rpc.mark_safe("enum.log_group");
|
|
||||||
|
|
||||||
rpc::rpc.mark_safe("strings.choke_heuristics");
|
rpc::rpc.mark_safe("strings.choke_heuristics");
|
||||||
rpc::rpc.mark_safe("strings.choke_heuristics.upload");
|
rpc::rpc.mark_safe("strings.choke_heuristics.upload");
|
||||||
rpc::rpc.mark_safe("strings.choke_heuristics.download");
|
rpc::rpc.mark_safe("strings.choke_heuristics.download");
|
||||||
rpc::rpc.mark_safe("strings.connection_type");
|
rpc::rpc.mark_safe("strings.connection_type");
|
||||||
rpc::rpc.mark_safe("strings.encryption");
|
rpc::rpc.mark_safe("strings.encryption.handshake");
|
||||||
|
rpc::rpc.mark_safe("strings.encryption.stream");
|
||||||
rpc::rpc.mark_safe("strings.ip_filter");
|
rpc::rpc.mark_safe("strings.ip_filter");
|
||||||
rpc::rpc.mark_safe("strings.ip_tos");
|
rpc::rpc.mark_safe("strings.ip_tos");
|
||||||
rpc::rpc.mark_safe("strings.log_group");
|
rpc::rpc.mark_safe("strings.log_group");
|
||||||
|
|||||||
+7
-29
@@ -51,7 +51,7 @@ apply_on_ratio(const torrent::Object& rawArgs) {
|
|||||||
!(max_ratio > 0 && total_upload * 100 > total_done * max_ratio))
|
!(max_ratio > 0 && total_upload * 100 > total_done * max_ratio))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
downloads.push_back(itr->get());
|
downloads.push_back(*itr);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ratio_command = "group." + group_name + ".ratio.command";
|
auto ratio_command = "group." + group_name + ".ratio.command";
|
||||||
@@ -128,7 +128,7 @@ apply_remove_untied() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
torrent::Object
|
torrent::Object
|
||||||
apply_schedule(const torrent::Object::list_type& args, bool if_absent) {
|
apply_schedule(const torrent::Object::list_type& args) {
|
||||||
if (args.size() != 4)
|
if (args.size() != 4)
|
||||||
throw torrent::input_error("Wrong number of arguments.");
|
throw torrent::input_error("Wrong number of arguments.");
|
||||||
|
|
||||||
@@ -138,9 +138,6 @@ apply_schedule(const torrent::Object::list_type& args, bool if_absent) {
|
|||||||
auto& arg2 = (itr++)->as_string();
|
auto& arg2 = (itr++)->as_string();
|
||||||
auto& arg3 = (itr++)->as_string();
|
auto& arg3 = (itr++)->as_string();
|
||||||
|
|
||||||
if (if_absent && control->command_scheduler()->find(arg1) != control->command_scheduler()->end())
|
|
||||||
return torrent::Object();
|
|
||||||
|
|
||||||
control->command_scheduler()->parse(arg1, arg2, arg3, *itr);
|
control->command_scheduler()->parse(arg1, arg2, arg3, *itr);
|
||||||
|
|
||||||
return torrent::Object();
|
return torrent::Object();
|
||||||
@@ -173,7 +170,7 @@ apply_close_low_diskspace(int64_t arg, uint32_t skip_priority) {
|
|||||||
|
|
||||||
torrent::FileList::cache_list cache;
|
torrent::FileList::cache_list cache;
|
||||||
|
|
||||||
for (const auto& download : *control->core()->download_list()) {
|
for (auto download : *control->core()->download_list()) {
|
||||||
if (!download->is_downloading())
|
if (!download->is_downloading())
|
||||||
continue;
|
continue;
|
||||||
if (download->priority() >= skip_priority)
|
if (download->priority() >= skip_priority)
|
||||||
@@ -240,25 +237,15 @@ d_multicall(const torrent::Object::list_type& args) {
|
|||||||
|
|
||||||
// Add some pre-parsing of the commands, so we don't spend time
|
// Add some pre-parsing of the commands, so we don't spend time
|
||||||
// parsing and searching command map for every single call.
|
// parsing and searching command map for every single call.
|
||||||
// Hold a reference to each download so a command that erases one does not
|
std::vector<core::Download*> dlist((*view_itr)->begin_visible(), (*view_itr)->end_visible());
|
||||||
// leave the rest of the loop dispatching on freed memory.
|
|
||||||
core::View::base_type dlist((*view_itr)->begin_visible(), (*view_itr)->end_visible());
|
|
||||||
|
|
||||||
torrent::Object resultRaw = torrent::Object::create_list();
|
torrent::Object resultRaw = torrent::Object::create_list();
|
||||||
torrent::Object::list_type& result = resultRaw.as_list();
|
torrent::Object::list_type& result = resultRaw.as_list();
|
||||||
|
|
||||||
for (const auto& download : dlist) {
|
for (auto download : dlist) {
|
||||||
if (download.use_count() == 1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
|
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
|
||||||
|
|
||||||
for (torrent::Object::list_const_iterator cItr = ++args.begin(); cItr != args.end(); cItr++) {
|
for (torrent::Object::list_const_iterator cItr = ++args.begin(); cItr != args.end(); cItr++) {
|
||||||
// A command may erase this download, which destroys the torrent object it
|
|
||||||
// wraps; the list dropping its reference is what tells us.
|
|
||||||
if (download.use_count() == 1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
auto& cmd = cItr->as_string();
|
auto& cmd = cItr->as_string();
|
||||||
row.push_back(rpc::parse_command(rpc::make_target(download), cmd.c_str(), cmd.c_str() + cmd.size()).first);
|
row.push_back(rpc::parse_command(rpc::make_target(download), cmd.c_str(), cmd.c_str() + cmd.size()).first);
|
||||||
}
|
}
|
||||||
@@ -281,12 +268,10 @@ d_multicall_filtered(const torrent::Object::list_type& args) {
|
|||||||
if (view_itr == viewManager->end())
|
if (view_itr == viewManager->end())
|
||||||
throw torrent::input_error("Could not find view '" + arg->as_string() + "'.");
|
throw torrent::input_error("Could not find view '" + arg->as_string() + "'.");
|
||||||
|
|
||||||
// Make a filtered copy of the current item list, holding a reference to each
|
// Make a filtered copy of the current item list
|
||||||
// download so a command that erases one cannot free it under us.
|
|
||||||
core::View::base_type dlist;
|
core::View::base_type dlist;
|
||||||
(*view_itr)->filter_by(*++arg, dlist);
|
(*view_itr)->filter_by(*++arg, dlist);
|
||||||
|
|
||||||
|
|
||||||
// Generate result by iterating over all items
|
// Generate result by iterating over all items
|
||||||
auto resultRaw = torrent::Object::create_list();
|
auto resultRaw = torrent::Object::create_list();
|
||||||
auto& result = resultRaw.as_list();
|
auto& result = resultRaw.as_list();
|
||||||
@@ -294,17 +279,11 @@ d_multicall_filtered(const torrent::Object::list_type& args) {
|
|||||||
++arg; // skip to first command
|
++arg; // skip to first command
|
||||||
|
|
||||||
for (const auto& item : dlist) {
|
for (const auto& item : dlist) {
|
||||||
if (item.use_count() == 1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Add empty row to result
|
// Add empty row to result
|
||||||
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
|
torrent::Object::list_type& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
|
||||||
|
|
||||||
// Call the provided commands and assemble their results
|
// Call the provided commands and assemble their results
|
||||||
for (torrent::Object::list_const_iterator command = arg; command != args.end(); command++) {
|
for (torrent::Object::list_const_iterator command = arg; command != args.end(); command++) {
|
||||||
if (item.use_count() == 1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
auto& cmdstr = command->as_string();
|
auto& cmdstr = command->as_string();
|
||||||
row.push_back(rpc::parse_command(rpc::make_target(item), cmdstr.c_str(), cmdstr.c_str() + cmdstr.size()).first);
|
row.push_back(rpc::parse_command(rpc::make_target(item), cmdstr.c_str(), cmdstr.c_str() + cmdstr.size()).first);
|
||||||
}
|
}
|
||||||
@@ -361,8 +340,7 @@ initialize_command_events() {
|
|||||||
CMD2_ANY ("close_untied", [](auto, auto) { return apply_close_untied(); });
|
CMD2_ANY ("close_untied", [](auto, auto) { return apply_close_untied(); });
|
||||||
CMD2_ANY ("remove_untied", [](auto, auto) { return apply_remove_untied(); });
|
CMD2_ANY ("remove_untied", [](auto, auto) { return apply_remove_untied(); });
|
||||||
|
|
||||||
CMD2_ANY_LIST ("schedule", [](auto, auto& args) { return apply_schedule(args, false); });
|
CMD2_ANY_LIST ("schedule", [](auto, auto& args) { return apply_schedule(args); });
|
||||||
CMD2_ANY_LIST ("schedule.if_absent", [](auto, auto& args) { return apply_schedule(args, true); });
|
|
||||||
CMD2_ANY_STRING_V("schedule.remove", [](auto, auto& str) { return control->command_scheduler()->erase_str(str); });
|
CMD2_ANY_STRING_V("schedule.remove", [](auto, auto& str) { return control->command_scheduler()->erase_str(str); });
|
||||||
|
|
||||||
CMD2_ANY_STRING_V("import", [](auto, auto& str) { return apply_import(str); });
|
CMD2_ANY_STRING_V("import", [](auto, auto& str) { return apply_import(str); });
|
||||||
|
|||||||
@@ -116,8 +116,6 @@ initialize_command_file() {
|
|||||||
CMD2_FILE("f.frozen_path.as_binary", [](auto* file, auto) { return file->frozen_path().object_as_binary(); });
|
CMD2_FILE("f.frozen_path.as_binary", [](auto* file, auto) { return file->frozen_path().object_as_binary(); });
|
||||||
CMD2_FILE("f.frozen_path.or_base64", [](auto* file, auto) { return file->frozen_path().object_utf8_or_base64(); });
|
CMD2_FILE("f.frozen_path.or_base64", [](auto* file, auto) { return file->frozen_path().object_utf8_or_base64(); });
|
||||||
CMD2_FILE("f.frozen_path.or_as_binary", [](auto* file, auto) { return file->frozen_path().object_utf8_or_as_binary(); });
|
CMD2_FILE("f.frozen_path.or_as_binary", [](auto* file, auto) { return file->frozen_path().object_utf8_or_as_binary(); });
|
||||||
CMD2_FILE("f.frozen_path.realpath.or_empty", [](auto* file, auto) { return resolve_path(file->frozen_path().str()); });
|
|
||||||
CMD2_FILE("f.frozen_path.realpath.or_throw", [](auto* file, auto) { return resolve_path_or_throw(file->frozen_path().str()); });
|
|
||||||
|
|
||||||
CMD2_FILE("f.match_depth_prev", std::bind(&torrent::File::match_depth_prev, std::placeholders::_1));
|
CMD2_FILE("f.match_depth_prev", std::bind(&torrent::File::match_depth_prev, std::placeholders::_1));
|
||||||
CMD2_FILE("f.match_depth_next", std::bind(&torrent::File::match_depth_next, std::placeholders::_1));
|
CMD2_FILE("f.match_depth_next", std::bind(&torrent::File::match_depth_next, std::placeholders::_1));
|
||||||
@@ -131,8 +129,6 @@ initialize_command_file() {
|
|||||||
rpc::rpc.mark_safe("f.path_components");
|
rpc::rpc.mark_safe("f.path_components");
|
||||||
rpc::rpc.mark_safe("f.path_depth");
|
rpc::rpc.mark_safe("f.path_depth");
|
||||||
rpc::rpc.mark_safe("f.frozen_path");
|
rpc::rpc.mark_safe("f.frozen_path");
|
||||||
rpc::rpc.mark_safe("f.frozen_path.realpath.or_empty");
|
|
||||||
rpc::rpc.mark_safe("f.frozen_path.realpath.or_throw");
|
|
||||||
rpc::rpc.mark_safe("f.frozen_path.hex");
|
rpc::rpc.mark_safe("f.frozen_path.hex");
|
||||||
rpc::rpc.mark_safe("f.frozen_path.base64");
|
rpc::rpc.mark_safe("f.frozen_path.base64");
|
||||||
rpc::rpc.mark_safe("f.frozen_path.base64_as_binary");
|
rpc::rpc.mark_safe("f.frozen_path.base64_as_binary");
|
||||||
|
|||||||
+9
-15
@@ -1,8 +1,5 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <torrent/download/resource_manager.h>
|
#include <torrent/download/resource_manager.h>
|
||||||
#include <torrent/download/choke_group.h>
|
#include <torrent/download/choke_group.h>
|
||||||
#include <torrent/download/choke_queue.h>
|
#include <torrent/download/choke_queue.h>
|
||||||
@@ -114,7 +111,7 @@ apply_cg_all_update_balance(bool is_up) {
|
|||||||
//
|
//
|
||||||
#else
|
#else
|
||||||
|
|
||||||
std::vector<std::unique_ptr<torrent::choke_group>> cg_list_hack;
|
std::vector<torrent::choke_group*> cg_list_hack;
|
||||||
|
|
||||||
int64_t
|
int64_t
|
||||||
cg_get_index(const torrent::Object& raw_args) {
|
cg_get_index(const torrent::Object& raw_args) {
|
||||||
@@ -124,7 +121,7 @@ cg_get_index(const torrent::Object& raw_args) {
|
|||||||
|
|
||||||
if (arg.is_string()) {
|
if (arg.is_string()) {
|
||||||
if (!rpc::parse_whole_value_nothrow(arg.as_string().c_str(), &index)) {
|
if (!rpc::parse_whole_value_nothrow(arg.as_string().c_str(), &index)) {
|
||||||
auto itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](const auto& cg) { return arg.as_string() == cg->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())
|
if (itr == cg_list_hack.end())
|
||||||
throw torrent::input_error("Choke group not found.");
|
throw torrent::input_error("Choke group not found.");
|
||||||
@@ -152,7 +149,7 @@ cg_get_group(const torrent::Object& raw_args) {
|
|||||||
if ((size_t)index >= cg_list_hack.size())
|
if ((size_t)index >= cg_list_hack.size())
|
||||||
throw torrent::input_error("Choke group not found.");
|
throw torrent::input_error("Choke group not found.");
|
||||||
|
|
||||||
return cg_list_hack.at(index).get();
|
return cg_list_hack.at(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t cg_d_group(core::Download* download) { return download->group(); }
|
int64_t cg_d_group(core::Download* download) { return download->group(); }
|
||||||
@@ -165,7 +162,7 @@ torrent::Object
|
|||||||
apply_cg_list() {
|
apply_cg_list() {
|
||||||
torrent::Object::list_type result;
|
torrent::Object::list_type result;
|
||||||
|
|
||||||
for (const auto& itr : cg_list_hack)
|
for (auto itr : cg_list_hack)
|
||||||
result.push_back(itr->name());
|
result.push_back(itr->name());
|
||||||
|
|
||||||
return torrent::Object::from_list(result);
|
return torrent::Object::from_list(result);
|
||||||
@@ -183,10 +180,10 @@ apply_cg_insert(const std::string& arg) {
|
|||||||
if (rpc::parse_whole_value_nothrow(arg.c_str(), &dummy))
|
if (rpc::parse_whole_value_nothrow(arg.c_str(), &dummy))
|
||||||
throw torrent::input_error("Cannot use a value string as choke group name.");
|
throw torrent::input_error("Cannot use a value string as choke group name.");
|
||||||
|
|
||||||
if (arg.empty() || std::any_of(cg_list_hack.begin(), cg_list_hack.end(), [&arg](const auto& cg) { return arg == cg->name(); }))
|
if (arg.empty() || std::any_of(cg_list_hack.begin(), cg_list_hack.end(), [&arg](auto cg) { return arg == cg->name(); }))
|
||||||
throw torrent::input_error("Duplicate name for choke group.");
|
throw torrent::input_error("Duplicate name for choke group.");
|
||||||
|
|
||||||
cg_list_hack.push_back(std::make_unique<torrent::choke_group>());
|
cg_list_hack.push_back(new torrent::choke_group());
|
||||||
cg_list_hack.back()->set_name(arg);
|
cg_list_hack.back()->set_name(arg);
|
||||||
|
|
||||||
cg_list_hack.back()->up_queue()->set_heuristics(torrent::HEURISTICS_UPLOAD_LEECH);
|
cg_list_hack.back()->up_queue()->set_heuristics(torrent::HEURISTICS_UPLOAD_LEECH);
|
||||||
@@ -197,7 +194,7 @@ apply_cg_insert(const std::string& arg) {
|
|||||||
|
|
||||||
torrent::Object
|
torrent::Object
|
||||||
apply_cg_index_of(const std::string& arg) {
|
apply_cg_index_of(const std::string& arg) {
|
||||||
auto itr = std::find_if(cg_list_hack.begin(), cg_list_hack.end(), [&arg](const auto& cg) { return arg == cg->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())
|
if (itr == cg_list_hack.end())
|
||||||
throw torrent::input_error("Choke group not found.");
|
throw torrent::input_error("Choke group not found.");
|
||||||
@@ -209,7 +206,7 @@ torrent::Object
|
|||||||
apply_cg_all_update_balance(bool is_up) {
|
apply_cg_all_update_balance(bool is_up) {
|
||||||
LT_LOG_SUBSYSTEM("apply update balance: hack is_up:%i", (int)is_up);
|
LT_LOG_SUBSYSTEM("apply update balance: hack is_up:%i", (int)is_up);
|
||||||
|
|
||||||
for (const auto& itr : cg_list_hack) {
|
for (auto itr : cg_list_hack) {
|
||||||
if (is_up)
|
if (is_up)
|
||||||
itr->up_queue()->balance();
|
itr->up_queue()->balance();
|
||||||
else
|
else
|
||||||
@@ -233,9 +230,6 @@ apply_cg_max_set(const torrent::Object::list_type& args, bool is_up) {
|
|||||||
int64_t second_arg = 0;
|
int64_t second_arg = 0;
|
||||||
rpc::parse_whole_value(args.back().as_string().c_str(), &second_arg);
|
rpc::parse_whole_value(args.back().as_string().c_str(), &second_arg);
|
||||||
|
|
||||||
if (second_arg < -1 || second_arg > std::numeric_limits<uint32_t>::max())
|
|
||||||
throw torrent::input_error("Max unchoked must be between -1 and 4294967295.");
|
|
||||||
|
|
||||||
if (is_up)
|
if (is_up)
|
||||||
cg_get_group(args.front())->up_queue()->set_max_unchoked(second_arg);
|
cg_get_group(args.front())->up_queue()->set_max_unchoked(second_arg);
|
||||||
else
|
else
|
||||||
@@ -352,7 +346,7 @@ initialize_command_groups() {
|
|||||||
#else
|
#else
|
||||||
apply_cg_insert("default");
|
apply_cg_insert("default");
|
||||||
|
|
||||||
CMD_ANY ("choke_group.size", [](auto, auto) { return (int64_t)cg_list_hack.size(); });
|
CMD_ANY ("choke_group.size", std::bind(&std::vector<torrent::choke_group*>::size, cg_list_hack));
|
||||||
CMD_ANY_STRING ("choke_group.index_of", std::bind(&apply_cg_index_of, std::placeholders::_2));
|
CMD_ANY_STRING ("choke_group.index_of", std::bind(&apply_cg_index_of, std::placeholders::_2));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ void initialize_command_groups();
|
|||||||
void initialize_command_throttle();
|
void initialize_command_throttle();
|
||||||
void initialize_command_tracker();
|
void initialize_command_tracker();
|
||||||
void initialize_command_scheduler();
|
void initialize_command_scheduler();
|
||||||
void initialize_command_string();
|
|
||||||
void initialize_command_system();
|
|
||||||
void initialize_command_ui();
|
void initialize_command_ui();
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -39,6 +37,4 @@ initialize_commands() {
|
|||||||
initialize_command_throttle();
|
initialize_command_throttle();
|
||||||
initialize_command_tracker();
|
initialize_command_tracker();
|
||||||
initialize_command_scheduler();
|
initialize_command_scheduler();
|
||||||
initialize_command_string();
|
|
||||||
initialize_command_system();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,12 @@
|
|||||||
#ifndef RTORRENT_UTILS_COMMAND_HELPERS_H
|
#ifndef RTORRENT_UTILS_COMMAND_HELPERS_H
|
||||||
#define RTORRENT_UTILS_COMMAND_HELPERS_H
|
#define RTORRENT_UTILS_COMMAND_HELPERS_H
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <limits>
|
|
||||||
#include <string>
|
|
||||||
#include <torrent/exceptions.h>
|
|
||||||
|
|
||||||
#include "rpc/command.h"
|
#include "rpc/command.h"
|
||||||
#include "rpc/parse_commands.h"
|
#include "rpc/parse_commands.h"
|
||||||
#include "rpc/object_storage.h"
|
#include "rpc/object_storage.h"
|
||||||
|
|
||||||
void initialize_commands();
|
void initialize_commands();
|
||||||
|
|
||||||
inline uint16_t
|
|
||||||
checked_port_value(int64_t value, const char* label) {
|
|
||||||
if (value < 0 || value > std::numeric_limits<uint16_t>::max())
|
|
||||||
throw torrent::input_error(std::string("Invalid ") + label + " port number.");
|
|
||||||
|
|
||||||
return static_cast<uint16_t>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Aliases with CMD_* for the below
|
// Aliases with CMD_* for the below
|
||||||
//
|
//
|
||||||
|
|||||||
+3
-11
@@ -131,7 +131,7 @@ ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address
|
|||||||
// copy everything up to '#' to address_copy and work from there
|
// copy everything up to '#' to address_copy and work from there
|
||||||
while(address[address_start_index] != '#' && address[address_start_index] != '\r' &&
|
while(address[address_start_index] != '#' && address[address_start_index] != '\r' &&
|
||||||
address[address_start_index] != '\n' && address[address_start_index] != '\0' &&
|
address[address_start_index] != '\n' && address[address_start_index] != '\0' &&
|
||||||
address_start_index < 4095 ) {
|
address_start_index < 4096 ) {
|
||||||
|
|
||||||
address_copy[address_start_index] = address[address_start_index];
|
address_copy[address_start_index] = address[address_start_index];
|
||||||
address_start_index++;
|
address_start_index++;
|
||||||
@@ -206,9 +206,6 @@ ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address
|
|||||||
if (mask_bits == 0) {
|
if (mask_bits == 0) {
|
||||||
mask = 0;
|
mask = 0;
|
||||||
end_mask = ~(uint32_t)0;
|
end_mask = ~(uint32_t)0;
|
||||||
} else if (mask_bits == 32) {
|
|
||||||
mask = ~(uint32_t)0;
|
|
||||||
end_mask = 0;
|
|
||||||
} else {
|
} else {
|
||||||
mask = (~mask) << (32-mask_bits);
|
mask = (~mask) << (32-mask_bits);
|
||||||
end_mask = (~end_mask) >> mask_bits;
|
end_mask = (~end_mask) >> mask_bits;
|
||||||
@@ -319,11 +316,6 @@ apply_ipv4_filter_load(const torrent::Object::list_type& args) {
|
|||||||
ipv4_filter_parse(buffer, value);
|
ipv4_filter_parse(buffer, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.fail() && !file.eof()) {
|
|
||||||
lineNumber++;
|
|
||||||
throw torrent::input_error("Exceeded max line length.");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (torrent::input_error& e) {
|
} catch (torrent::input_error& e) {
|
||||||
snprintf(buffer, 2048, "Error in ip filter file: %s:%u: %s", filename.c_str(), lineNumber, e.what());
|
snprintf(buffer, 2048, "Error in ip filter file: %s:%u: %s", filename.c_str(), lineNumber, e.what());
|
||||||
|
|
||||||
@@ -344,8 +336,8 @@ apply_ipv4_filter_dump() {
|
|||||||
torrent::Object raw_result = torrent::Object::create_list();
|
torrent::Object raw_result = torrent::Object::create_list();
|
||||||
torrent::Object::list_type& result = raw_result.as_list();
|
torrent::Object::list_type& result = raw_result.as_list();
|
||||||
|
|
||||||
const torrent::ipv4_table::range_map_type& range_map = torrent::PeerList::ipv4_filter()->range_map;
|
torrent::ipv4_table::range_map_type range_map = torrent::PeerList::ipv4_filter()->range_map;
|
||||||
torrent::ipv4_table::range_map_type::const_iterator iter = range_map.begin();
|
torrent::ipv4_table::range_map_type::iterator iter = range_map.begin();
|
||||||
|
|
||||||
while(iter != range_map.end()) {
|
while(iter != range_map.end()) {
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
|
|||||||
+210
-16
@@ -1,16 +1,33 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <torrent/torrent.h>
|
||||||
|
#include <torrent/data/file_manager.h>
|
||||||
|
#include <torrent/data/chunk_utils.h>
|
||||||
|
#include <torrent/runtime/runtime.h>
|
||||||
#include <torrent/runtime/memory_manager.h>
|
#include <torrent/runtime/memory_manager.h>
|
||||||
#include <torrent/utils/log.h>
|
#include <torrent/runtime/socket_manager.h>
|
||||||
|
#include <torrent/utils/chrono.h>
|
||||||
|
#include <torrent/utils/option_strings.h>
|
||||||
|
|
||||||
#include "command_helpers.h"
|
|
||||||
#include "control.h"
|
|
||||||
#include "core/download.h"
|
#include "core/download.h"
|
||||||
|
#include "core/download_list.h"
|
||||||
#include "core/manager.h"
|
#include "core/manager.h"
|
||||||
#include "rpc/parse_commands.h"
|
#include "rpc/parse_commands.h"
|
||||||
#include "rpc/lua.h"
|
|
||||||
#include "rpc/scgi.h"
|
#include "rpc/scgi.h"
|
||||||
#include "session/session_manager.h"
|
#include "session/session_manager.h"
|
||||||
|
#include "utils/file_status_cache.h"
|
||||||
|
|
||||||
|
#include "globals.h"
|
||||||
|
#include "rpc/lua.h"
|
||||||
|
#include "control.h"
|
||||||
|
#include "command_helpers.h"
|
||||||
|
|
||||||
typedef torrent::ChunkManager CM_t;
|
typedef torrent::ChunkManager CM_t;
|
||||||
typedef torrent::FileManager FM_t;
|
typedef torrent::FileManager FM_t;
|
||||||
@@ -26,6 +43,49 @@ apply_pieces_stats_total_size() {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
torrent::Object
|
||||||
|
system_env(const torrent::Object::string_type& arg) {
|
||||||
|
if (arg.empty())
|
||||||
|
throw torrent::input_error("system.env: Missing variable name.");
|
||||||
|
|
||||||
|
char* val = getenv(arg.c_str());
|
||||||
|
return std::string(val ? val : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
torrent::Object
|
||||||
|
system_hostname() {
|
||||||
|
char buffer[1024];
|
||||||
|
|
||||||
|
if (gethostname(buffer, 1023) == -1)
|
||||||
|
throw torrent::input_error("Unable to read hostname.");
|
||||||
|
|
||||||
|
// if (shorten)
|
||||||
|
// *std::find(buffer, buffer + 1023, '.') = '\0';
|
||||||
|
|
||||||
|
return std::string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
torrent::Object
|
||||||
|
system_get_cwd() {
|
||||||
|
char* buffer = getcwd(NULL, 0);
|
||||||
|
|
||||||
|
if (buffer == NULL)
|
||||||
|
throw torrent::input_error("Unable to read cwd.");
|
||||||
|
|
||||||
|
torrent::Object result = torrent::Object(std::string(buffer));
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
torrent::Object
|
||||||
|
system_set_cwd(const torrent::Object::string_type& rawArgs) {
|
||||||
|
if (::chdir(rawArgs.c_str()) != 0)
|
||||||
|
throw torrent::input_error("Could not change current working directory.");
|
||||||
|
|
||||||
|
return torrent::Object();
|
||||||
|
}
|
||||||
|
|
||||||
inline torrent::Object::list_const_iterator
|
inline torrent::Object::list_const_iterator
|
||||||
post_increment(torrent::Object::list_const_iterator& itr, const torrent::Object::list_const_iterator& last) {
|
post_increment(torrent::Object::list_const_iterator& itr, const torrent::Object::list_const_iterator& last) {
|
||||||
if (itr == last)
|
if (itr == last)
|
||||||
@@ -57,7 +117,7 @@ group_insert(const torrent::Object::list_type& args) {
|
|||||||
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.enable", "simple",
|
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.enable", "simple",
|
||||||
"schedule=group." + name + ".ratio,5,60,on_ratio=" + name));
|
"schedule=group." + name + ".ratio,5,60,on_ratio=" + name));
|
||||||
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.disable", "simple",
|
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.disable", "simple",
|
||||||
"schedule.remove=group." + name + ".ratio"));
|
"schedule_remove=group." + name + ".ratio"));
|
||||||
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.command", "simple",
|
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.command", "simple",
|
||||||
"d.try_close= ;d.ignore_commands.set=1"));
|
"d.try_close= ;d.ignore_commands.set=1"));
|
||||||
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".view", "string", view));
|
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".view", "string", view));
|
||||||
@@ -77,19 +137,142 @@ group_insert(const torrent::Object::list_type& args) {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const int file_print_use_space = 0x1;
|
||||||
|
static const int file_print_delim_space = 0x2;
|
||||||
|
|
||||||
|
void
|
||||||
|
file_print_list(torrent::Object::list_const_iterator first, torrent::Object::list_const_iterator last, FILE* output, int flags) {
|
||||||
|
while (first != last) {
|
||||||
|
switch (first->type()) {
|
||||||
|
case torrent::Object::TYPE_STRING:
|
||||||
|
fprintf(output, (const char*)" %s" + !(flags & file_print_use_space), first->as_string().c_str());
|
||||||
|
break;
|
||||||
|
case torrent::Object::TYPE_VALUE:
|
||||||
|
fprintf(output, (const char*)" %" PRIi64 + !(flags & file_print_use_space), first->as_value());
|
||||||
|
break;
|
||||||
|
case torrent::Object::TYPE_LIST:
|
||||||
|
file_print_list(first->as_list().begin(), first->as_list().end(), output, 0);
|
||||||
|
break;
|
||||||
|
case torrent::Object::TYPE_NONE:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw torrent::input_error("Invalid type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
flags |= (flags & file_print_delim_space) >> 1;
|
||||||
|
first++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
torrent::Object
|
||||||
|
cmd_file_append(const torrent::Object::list_type& args) {
|
||||||
|
if (args.empty())
|
||||||
|
throw torrent::input_error("Invalid number of arguments.");
|
||||||
|
|
||||||
|
FILE* output = fopen(args.front().as_string().c_str(), "a");
|
||||||
|
|
||||||
|
if (output == nullptr)
|
||||||
|
throw torrent::input_error("Could not append to file '" + args.front().as_string() + "': " + std::strerror(errno));
|
||||||
|
|
||||||
|
try {
|
||||||
|
file_print_list(++args.begin(), args.end(), output, file_print_delim_space);
|
||||||
|
fprintf(output, "\n");
|
||||||
|
} catch (...) {
|
||||||
|
fclose(output);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
fclose(output);
|
||||||
|
return torrent::Object();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
initialize_command_local() {
|
initialize_command_local() {
|
||||||
core::DownloadList* dList = control->core()->download_list();
|
core::DownloadList* dList = control->core()->download_list();
|
||||||
|
torrent::FileManager* fileManager = torrent::file_manager();
|
||||||
|
|
||||||
|
if (rpc::call_command_value("method.use_deprecated") == 1) {
|
||||||
|
CMD_ANY_LIST ("file.append", std::bind(&cmd_file_append, std::placeholders::_2));
|
||||||
|
}
|
||||||
|
|
||||||
|
CMD_ANY ("system.hostname", std::bind(&system_hostname));
|
||||||
|
CMD_ANY ("system.pid", std::bind(&getpid));
|
||||||
|
|
||||||
|
CMD_VAR_C_STRING("system.api_version", (int64_t)API_VERSION);
|
||||||
|
CMD_VAR_C_STRING("system.client_version", PACKAGE_VERSION);
|
||||||
|
CMD_VAR_C_STRING("system.library_version", torrent::runtime::version());
|
||||||
|
|
||||||
|
CMD_VAR_VALUE ("system.file.allocate", 0);
|
||||||
|
CMD_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30);
|
||||||
|
CMD_VAR_VALUE ("system.file.split_size", -1);
|
||||||
|
CMD_VAR_STRING ("system.file.split_suffix", ".part");
|
||||||
|
|
||||||
|
CMD_ANY ("system.file_status_cache.size", std::bind(&utils::FileStatusCache::size,
|
||||||
|
(utils::FileStatusCache::base_type*)control->core()->file_status_cache()));
|
||||||
|
CMD_ANY_V ("system.file_status_cache.prune", std::bind(&utils::FileStatusCache::prune, control->core()->file_status_cache()));
|
||||||
|
|
||||||
|
CMD_VAR_BOOL ("file.prioritize_toc", 0);
|
||||||
|
CMD_VAR_LIST ("file.prioritize_toc.first");
|
||||||
|
CMD_VAR_LIST ("file.prioritize_toc.last");
|
||||||
|
|
||||||
|
CMD_ANY ("system.files.advise_random", std::bind(&FM_t::advise_random, fileManager));
|
||||||
|
CMD_ANY_VALUE_V ("system.files.advise_random.set", std::bind(&FM_t::set_advise_random, fileManager, std::placeholders::_2));
|
||||||
|
CMD_ANY ("system.files.advise_random.hashing", std::bind(&FM_t::advise_random_hashing, fileManager));
|
||||||
|
CMD_ANY_VALUE_V ("system.files.advise_random.hashing.set", std::bind(&FM_t::set_advise_random_hashing, fileManager, std::placeholders::_2));
|
||||||
|
CMD_ANY ("system.files.session.fdatasync", [](auto, auto) { return session_thread::manager()->use_fsyncdisk(); });
|
||||||
|
CMD_ANY_VALUE_V ("system.files.session.fdatasync.set", [](auto, auto& value) { return session_thread::manager()->set_use_fsyncdisk(value); });
|
||||||
|
|
||||||
|
CMD_ANY ("system.files.opened_counter", std::bind(&FM_t::files_opened_counter, fileManager));
|
||||||
|
CMD_ANY ("system.files.closed_counter", std::bind(&FM_t::files_closed_counter, fileManager));
|
||||||
|
CMD_ANY ("system.files.failed_counter", std::bind(&FM_t::files_failed_counter, fileManager));
|
||||||
|
|
||||||
|
CMD_ANY_STRING ("system.env", [](auto, auto& str) { return system_env(str); });
|
||||||
|
|
||||||
|
CMD_ANY ("system.time", [](auto, auto) { return torrent::this_thread::cached_seconds().count(); });
|
||||||
|
CMD_ANY ("system.time_seconds", [](auto, auto) { return torrent::utils::cast_seconds(torrent::utils::time_since_epoch()).count(); });
|
||||||
|
CMD_ANY ("system.time_usec", [](auto, auto) { return torrent::utils::time_since_epoch().count(); });
|
||||||
|
|
||||||
|
CMD_ANY_VALUE_V ("system.umask.set", [](auto, auto& value) { return ::umask(value); });
|
||||||
|
|
||||||
|
CMD_VAR_BOOL ("system.daemon", false);
|
||||||
|
|
||||||
|
CMD_ANY_V ("system.shutdown.normal", [](auto, auto) { control->receive_normal_shutdown(); });
|
||||||
|
CMD_ANY_V ("system.shutdown.quick", [](auto, auto) { control->receive_quick_shutdown(); });
|
||||||
|
|
||||||
|
CMD_REDIRECT_NO_EXPORT("system.shutdown", "system.shutdown.normal");
|
||||||
|
|
||||||
|
CMD_ANY ("system.cwd", [](auto, auto) { return system_get_cwd(); });
|
||||||
|
CMD_ANY_STRING ("system.cwd.set", [](auto, auto& str) { return system_set_cwd(str); });
|
||||||
|
|
||||||
|
CMD_ANY ("system.sockets.size", [](auto, auto) { return torrent::runtime::socket_manager()->size(); });
|
||||||
|
CMD_ANY ("system.sockets.max_size", [](auto, auto) { return torrent::runtime::socket_manager()->max_size(); });
|
||||||
|
CMD_ANY_VALUE_V ("system.sockets.max_size.set", [](auto, auto& value) { return torrent::runtime::socket_manager()->set_max_size_and_adjust(value); });
|
||||||
|
CMD_ANY_V ("system.sockets.adjust_alloc", [](auto, auto) { torrent::runtime::socket_manager()->adjust_allocation(); });
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
|
||||||
|
auto category = static_cast<torrent::runtime::socket_manager_category_t>(i);
|
||||||
|
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
|
||||||
|
|
||||||
|
CMD_ANY (category_name + ".size", [category](auto, auto) { return torrent::runtime::socket_manager()->category_managed_size(category); });
|
||||||
|
CMD_ANY (category_name + ".max_size", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_size(category); });
|
||||||
|
CMD_ANY (category_name + ".min_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_min_allocation(category); });
|
||||||
|
CMD_ANY (category_name + ".max_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_allocation(category); });
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CMD_ANY_VALUE_V(category_name + ".min_alloc.set", [category](auto, auto& value) { torrent::runtime::socket_manager()->set_category_min_allocation(category, value); });
|
||||||
|
CMD_ANY_VALUE_V(category_name + ".max_alloc.set", [category](auto, auto& value) { torrent::runtime::socket_manager()->set_category_max_allocation(category, value); });
|
||||||
|
}
|
||||||
|
|
||||||
CMD_ANY ("pieces.sync.always_safe", [](auto, auto) { return torrent::runtime::memory_manager()->safe_sync(); });
|
CMD_ANY ("pieces.sync.always_safe", [](auto, auto) { return torrent::runtime::memory_manager()->safe_sync(); });
|
||||||
CMD_ANY_VALUE_V ("pieces.sync.always_safe.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_safe_sync(value); });
|
CMD_ANY_VALUE_V ("pieces.sync.always_safe.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_safe_sync(value); });
|
||||||
CMD_ANY ("pieces.sync.safe_free_diskspace", [](auto, auto) { return torrent::runtime::memory_manager()->sync_safe_free_diskspace(); });
|
CMD_ANY ("pieces.sync.safe_free_diskspace", [](auto, auto) { return torrent::runtime::memory_manager()->sync_safe_free_diskspace(); });
|
||||||
CMD_ANY ("pieces.sync.timeout", [](auto, auto) { return torrent::runtime::memory_manager()->timeout_sync().count(); });
|
CMD_ANY ("pieces.sync.timeout", [](auto, auto) { return torrent::runtime::memory_manager()->timeout_sync().count(); });
|
||||||
CMD_ANY_VALUE_V ("pieces.sync.timeout.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_timeout_sync(value); });
|
CMD_ANY_VALUE_V ("pieces.sync.timeout.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_timeout_sync(value); });
|
||||||
|
// CMD_ANY ("pieces.sync.timeout_safe", [](auto, auto) { return torrent::runtime::memory_manager()->timeout_safe_sync(); });
|
||||||
|
// CMD_ANY_VALUE_V ("pieces.sync.timeout_safe.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_timeout_safe_sync(value); });
|
||||||
CMD_ANY ("pieces.sync.timeout_safe", [](auto, auto) { return 0; });
|
CMD_ANY ("pieces.sync.timeout_safe", [](auto, auto) { return 0; });
|
||||||
CMD_ANY_VALUE_V ("pieces.sync.timeout_safe.set", [](auto, auto) {
|
CMD_ANY_VALUE_V ("pieces.sync.timeout_safe.set", [](auto, auto) { });
|
||||||
lt_log_print(torrent::LOG_WARN, "pieces.sync.timeout_safe.set is no longer supported and does nothing.");
|
|
||||||
});
|
|
||||||
CMD_ANY ("pieces.sync.queue_size", [](auto, auto) { return torrent::runtime::memory_manager()->sync_queue_block_count(); });
|
CMD_ANY ("pieces.sync.queue_size", [](auto, auto) { return torrent::runtime::memory_manager()->sync_queue_block_count(); });
|
||||||
|
|
||||||
CMD_ANY ("pieces.preload.type", [](auto, auto) { return torrent::runtime::memory_manager()->preload_type(); });
|
CMD_ANY ("pieces.preload.type", [](auto, auto) { return torrent::runtime::memory_manager()->preload_type(); });
|
||||||
@@ -113,13 +296,9 @@ initialize_command_local() {
|
|||||||
CMD_VAR_BOOL ("pieces.hash.on_completion", true);
|
CMD_VAR_BOOL ("pieces.hash.on_completion", true);
|
||||||
|
|
||||||
CMD_VAR_STRING ("directory.default", "./");
|
CMD_VAR_STRING ("directory.default", "./");
|
||||||
CMD_ANY ("directory.default.realpath.or_empty", [](auto, auto) { return resolve_path(rpc::call_command_string("directory.default")); });
|
|
||||||
CMD_ANY ("directory.default.realpath.or_throw", [](auto, auto) { return resolve_path_or_throw(rpc::call_command_string("directory.default")); });
|
|
||||||
|
|
||||||
CMD_VAR_STRING ("session.name", "");
|
CMD_VAR_STRING ("session.name", "");
|
||||||
CMD_ANY ("session.path", [](auto, auto) { return session_thread::manager()->path(); });
|
CMD_ANY ("session.path", [](auto, auto) { return session_thread::manager()->path(); });
|
||||||
CMD_ANY ("session.path.realpath.or_empty", [](auto, auto) { return resolve_path(session_thread::manager()->path()); });
|
|
||||||
CMD_ANY ("session.path.realpath.or_throw", [](auto, auto) { return resolve_path_or_throw(session_thread::manager()->path()); });
|
|
||||||
CMD_ANY_STRING_V("session.path.set", [](auto, auto& str) { return session_thread::manager()->set_path(str); });
|
CMD_ANY_STRING_V("session.path.set", [](auto, auto& str) { return session_thread::manager()->set_path(str); });
|
||||||
CMD_ANY ("session.use_lock", [](auto, auto) { return session_thread::manager()->use_lock(); });
|
CMD_ANY ("session.use_lock", [](auto, auto) { return session_thread::manager()->use_lock(); });
|
||||||
CMD_ANY_VALUE_V ("session.use_lock.set", [](auto, auto& value) { return session_thread::manager()->set_use_lock(value); });
|
CMD_ANY_VALUE_V ("session.use_lock.set", [](auto, auto& value) { return session_thread::manager()->set_use_lock(value); });
|
||||||
@@ -164,12 +343,27 @@ initialize_command_local() {
|
|||||||
|
|
||||||
CMD_ANY_LIST ("group.insert", std::bind(&group_insert, std::placeholders::_2));
|
CMD_ANY_LIST ("group.insert", std::bind(&group_insert, std::placeholders::_2));
|
||||||
|
|
||||||
|
rpc::rpc.mark_safe("system.api_version");
|
||||||
|
rpc::rpc.mark_safe("system.client_version");
|
||||||
|
rpc::rpc.mark_safe("system.library_version");
|
||||||
|
rpc::rpc.mark_safe("system.file.max_size");
|
||||||
|
rpc::rpc.mark_safe("system.file.split_size");
|
||||||
|
rpc::rpc.mark_safe("system.file.split_suffix");
|
||||||
|
|
||||||
|
rpc::rpc.mark_safe("system.sockets.size");
|
||||||
|
rpc::rpc.mark_safe("system.sockets.max_size");
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
|
||||||
|
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
|
||||||
|
|
||||||
|
rpc::rpc.mark_safe(category_name + ".size");
|
||||||
|
rpc::rpc.mark_safe(category_name + ".max_size");
|
||||||
|
rpc::rpc.mark_safe(category_name + ".min_alloc");
|
||||||
|
rpc::rpc.mark_safe(category_name + ".max_alloc");
|
||||||
|
}
|
||||||
|
|
||||||
rpc::rpc.mark_safe("directory.default");
|
rpc::rpc.mark_safe("directory.default");
|
||||||
rpc::rpc.mark_safe("session.path");
|
rpc::rpc.mark_safe("session.path");
|
||||||
rpc::rpc.mark_safe("session.path.realpath.or_empty");
|
|
||||||
rpc::rpc.mark_safe("session.path.realpath.or_throw");
|
|
||||||
rpc::rpc.mark_safe("directory.default.realpath.or_empty");
|
|
||||||
rpc::rpc.mark_safe("directory.default.realpath.or_throw");
|
|
||||||
rpc::rpc.mark_safe("session.use_lock");
|
rpc::rpc.mark_safe("session.use_lock");
|
||||||
rpc::rpc.mark_safe("session.on_completion");
|
rpc::rpc.mark_safe("session.on_completion");
|
||||||
|
|
||||||
|
|||||||
+2
-34
@@ -1,7 +1,6 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <iterator>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <torrent/data/chunk_utils.h>
|
#include <torrent/data/chunk_utils.h>
|
||||||
@@ -16,7 +15,6 @@
|
|||||||
#include "core/download.h"
|
#include "core/download.h"
|
||||||
#include "core/download_list.h"
|
#include "core/download_list.h"
|
||||||
#include "core/manager.h"
|
#include "core/manager.h"
|
||||||
#include "rpc/parse.h"
|
|
||||||
#include "rpc/parse_commands.h"
|
#include "rpc/parse_commands.h"
|
||||||
|
|
||||||
torrent::Object
|
torrent::Object
|
||||||
@@ -40,32 +38,6 @@ apply_log_add_output(const torrent::Object::list_type& args) {
|
|||||||
return torrent::Object();
|
return torrent::Object();
|
||||||
}
|
}
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
apply_log_print(const torrent::Object::list_type& args) {
|
|
||||||
if (args.size() < 2)
|
|
||||||
throw torrent::input_error("Invalid number of arguments.");
|
|
||||||
|
|
||||||
torrent::Object::value_type group;
|
|
||||||
|
|
||||||
if (args.front().is_value())
|
|
||||||
group = args.front().as_value();
|
|
||||||
else if (args.front().is_string())
|
|
||||||
group = torrent::option_find_string_str(torrent::OPTION_LOG_GROUP, args.front().as_string());
|
|
||||||
else
|
|
||||||
throw torrent::input_error("Invalid log group.");
|
|
||||||
|
|
||||||
if (group < 0 || group >= torrent::LOG_GROUP_MAX_SIZE)
|
|
||||||
throw torrent::input_error("Invalid log group.");
|
|
||||||
|
|
||||||
std::string message;
|
|
||||||
|
|
||||||
for (auto itr = std::next(args.begin()); itr != args.end(); ++itr)
|
|
||||||
rpc::print_object_std(&message, &*itr, 0);
|
|
||||||
|
|
||||||
torrent::log_groups[group].internal_print(message);
|
|
||||||
return torrent::Object();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Deprecated.
|
// TODO: Deprecated.
|
||||||
torrent::Object
|
torrent::Object
|
||||||
apply_log(const torrent::Object::string_type& arg, int logType) {
|
apply_log(const torrent::Object::string_type& arg, int logType) {
|
||||||
@@ -122,10 +94,7 @@ log_vmmap_dump(const std::string& str) {
|
|||||||
fprintf(log_file, "%8p-%8p [%5llxk]\n", all_mapping.ptr, (char*)all_mapping.ptr + all_mapping.length, (long long unsigned int)(all_mapping.length / 1024));
|
fprintf(log_file, "%8p-%8p [%5llxk]\n", all_mapping.ptr, (char*)all_mapping.ptr + all_mapping.length, (long long unsigned int)(all_mapping.length / 1024));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The buffered output only reaches the kernel here.
|
fclose(log_file);
|
||||||
if (fclose(log_file) != 0)
|
|
||||||
throw torrent::input_error("Could not write log file: " + str);
|
|
||||||
|
|
||||||
return torrent::Object();
|
return torrent::Object();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,12 +107,11 @@ initialize_command_logging() {
|
|||||||
CMD2_ANY_LIST ("log.open_gz_file_pid", std::bind(&apply_log_open, log_flag_append_pid | log_flag_use_gz, std::placeholders::_2));
|
CMD2_ANY_LIST ("log.open_gz_file_pid", std::bind(&apply_log_open, log_flag_append_pid | log_flag_use_gz, std::placeholders::_2));
|
||||||
CMD2_ANY_LIST ("log.append_file", std::bind(&apply_log_open, log_flag_append_file, std::placeholders::_2));
|
CMD2_ANY_LIST ("log.append_file", std::bind(&apply_log_open, log_flag_append_file, std::placeholders::_2));
|
||||||
CMD2_ANY_LIST ("log.append_file.flush", std::bind(&apply_log_open, log_flag_append_file | log_flag_flush, std::placeholders::_2));
|
CMD2_ANY_LIST ("log.append_file.flush", std::bind(&apply_log_open, log_flag_append_file | log_flag_flush, std::placeholders::_2));
|
||||||
CMD2_ANY_LIST ("log.append_gz_file", std::bind(&apply_log_open, log_flag_append_file | log_flag_use_gz, std::placeholders::_2));
|
CMD2_ANY_LIST ("log.append_gz_file", std::bind(&apply_log_open, log_flag_append_file, std::placeholders::_2));
|
||||||
|
|
||||||
CMD2_ANY_STRING_V("log.close", std::bind(&torrent::log_close_output_str, std::placeholders::_2));
|
CMD2_ANY_STRING_V("log.close", std::bind(&torrent::log_close_output_str, std::placeholders::_2));
|
||||||
|
|
||||||
CMD2_ANY_LIST ("log.add_output", std::bind(&apply_log_add_output, std::placeholders::_2));
|
CMD2_ANY_LIST ("log.add_output", std::bind(&apply_log_add_output, std::placeholders::_2));
|
||||||
CMD2_ANY_LIST ("log.print", std::bind(&apply_log_print, std::placeholders::_2));
|
|
||||||
|
|
||||||
CMD2_ANY_STRING ("log.execute", std::bind(&apply_log, std::placeholders::_2, 0));
|
CMD2_ANY_STRING ("log.execute", std::bind(&apply_log, std::placeholders::_2, 0));
|
||||||
CMD2_ANY_STRING ("log.vmmap.dump", std::bind(&log_vmmap_dump, std::placeholders::_2));
|
CMD2_ANY_STRING ("log.vmmap.dump", std::bind(&log_vmmap_dump, std::placeholders::_2));
|
||||||
|
|||||||
+12
-145
@@ -19,6 +19,7 @@
|
|||||||
#include <torrent/utils/log.h>
|
#include <torrent/utils/log.h>
|
||||||
#include <torrent/utils/option_strings.h>
|
#include <torrent/utils/option_strings.h>
|
||||||
|
|
||||||
|
#include "encryption_config.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "control.h"
|
#include "control.h"
|
||||||
#include "command_helpers.h"
|
#include "command_helpers.h"
|
||||||
@@ -34,118 +35,6 @@
|
|||||||
#include <systemd/sd-daemon.h>
|
#include <systemd/sd-daemon.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
listen_port_range() {
|
|
||||||
auto port_range = torrent::runtime::client_config()->listen_port_range();
|
|
||||||
|
|
||||||
return std::to_string(port_range.first) + "-" + std::to_string(port_range.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
set_listen_port_range(const std::string& arg) {
|
|
||||||
unsigned int port_first{}, port_last{};
|
|
||||||
|
|
||||||
if (std::sscanf(arg.c_str(), "%i-%i", &port_first, &port_last) != 2)
|
|
||||||
throw torrent::input_error("Invalid port_range argument.");
|
|
||||||
|
|
||||||
if (port_first >= (1 << 16) || port_last >= (1 << 16))
|
|
||||||
throw torrent::input_error("Port range out-of-bounds.");
|
|
||||||
|
|
||||||
torrent::runtime::client_config()->set_listen_port_range(port_first, port_last);
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
get_encryption() {
|
|
||||||
auto encryption_modes = torrent::runtime::network_config()->encryption_modes();
|
|
||||||
|
|
||||||
return torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_HANDSHAKE, encryption_modes.first) + "," +
|
|
||||||
torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_STREAM, encryption_modes.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
get_handshake_encryption() {
|
|
||||||
auto encryption_modes = torrent::runtime::network_config()->encryption_modes();
|
|
||||||
|
|
||||||
return torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_MODE, encryption_modes.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
get_stream_encryption() {
|
|
||||||
auto encryption_modes = torrent::runtime::network_config()->encryption_modes();
|
|
||||||
|
|
||||||
return torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_MODE, encryption_modes.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
apply_obsolete_encryption(const torrent::Object::list_type& args) {
|
|
||||||
torrent::encryption_mode handshake_mode{torrent::ENCRYPTION_MODE_ALLOW};
|
|
||||||
torrent::encryption_mode stream_mode{torrent::ENCRYPTION_MODE_ALLOW};
|
|
||||||
|
|
||||||
for (auto& itr : args) {
|
|
||||||
auto arg = itr.as_string();
|
|
||||||
|
|
||||||
if (arg == "none") {
|
|
||||||
handshake_mode = torrent::ENCRYPTION_MODE_DENY;
|
|
||||||
stream_mode = torrent::ENCRYPTION_MODE_DENY;
|
|
||||||
break;
|
|
||||||
|
|
||||||
} else if (arg == "allow_incoming") {
|
|
||||||
} else if (arg == "try_outgoing") {
|
|
||||||
} else if (arg == "require") {
|
|
||||||
handshake_mode = torrent::ENCRYPTION_MODE_REQUIRE;
|
|
||||||
|
|
||||||
} else if (arg == "require_RC4" || arg == "require_rc4") {
|
|
||||||
handshake_mode = torrent::ENCRYPTION_MODE_REQUIRE;
|
|
||||||
stream_mode = torrent::ENCRYPTION_MODE_REQUIRE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
} else if (arg == "enable_retry") {
|
|
||||||
} else if (arg == "prefer_plaintext") {
|
|
||||||
} else {
|
|
||||||
throw torrent::input_error("Invalid encryption option: '" + arg + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lt_log_print(torrent::LOG_WARN, "Obsolete encryption options used, use 'handshake_{deny,allow,prefer,require}, stream_{deny,allow,prefer,require}' instead.");
|
|
||||||
|
|
||||||
torrent::runtime::network_config()->set_encryption_modes(handshake_mode, stream_mode);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
apply_encryption(const torrent::Object::list_type& args) {
|
|
||||||
if (args.empty())
|
|
||||||
throw torrent::input_error("No encryption options specified.");
|
|
||||||
|
|
||||||
torrent::encryption_mode encryption_mode, handshake_mode, stream_mode;
|
|
||||||
|
|
||||||
if (args.size() == 1) {
|
|
||||||
try {
|
|
||||||
encryption_mode = static_cast<torrent::encryption_mode>(torrent::option_find_string_str(torrent::OPTION_ENCRYPTION_MODE, args.front().as_string()));
|
|
||||||
|
|
||||||
} catch (torrent::input_error& e) {
|
|
||||||
return apply_obsolete_encryption(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::runtime::network_config()->set_encryption_modes(encryption_mode, encryption_mode);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.size() != 2)
|
|
||||||
return apply_obsolete_encryption(args);
|
|
||||||
|
|
||||||
try {
|
|
||||||
handshake_mode = static_cast<torrent::encryption_mode>(torrent::option_find_string_str(torrent::OPTION_ENCRYPTION_HANDSHAKE, args.front().as_string()));
|
|
||||||
stream_mode = static_cast<torrent::encryption_mode>(torrent::option_find_string_str(torrent::OPTION_ENCRYPTION_STREAM, args.back().as_string()));
|
|
||||||
|
|
||||||
} catch (torrent::input_error& e) {
|
|
||||||
return apply_obsolete_encryption(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::runtime::network_config()->set_encryption_modes(handshake_mode, stream_mode);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
torrent::Object
|
||||||
apply_tos(const torrent::Object::string_type& arg) {
|
apply_tos(const torrent::Object::string_type& arg) {
|
||||||
rpc::command_base::value_type value;
|
rpc::command_base::value_type value;
|
||||||
@@ -317,7 +206,7 @@ initialize_command_network() {
|
|||||||
auto nw_config = torrent::runtime::network_config();
|
auto nw_config = torrent::runtime::network_config();
|
||||||
|
|
||||||
CMD_ANY ("network.listen.port", [](auto, auto) { return torrent::runtime::network_manager()->listen_port(); });
|
CMD_ANY ("network.listen.port", [](auto, auto) { return torrent::runtime::network_manager()->listen_port(); });
|
||||||
CMD_ANY_VALUE_V ("network.listen.port.set", [](auto, auto& value) { return torrent::runtime::network_manager()->set_listen_port(checked_port_value(value, "listen")); });
|
CMD_ANY_VALUE_V ("network.listen.port.set", [](auto, auto& value) { return torrent::runtime::network_manager()->set_listen_port(value); });
|
||||||
CMD_ANY ("network.listen.port.random", [](auto, auto) { return torrent::runtime::client_config()->listen_port_random(); });
|
CMD_ANY ("network.listen.port.random", [](auto, auto) { return torrent::runtime::client_config()->listen_port_random(); });
|
||||||
CMD_ANY_VALUE_V ("network.listen.port.random.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_listen_port_random(value); });
|
CMD_ANY_VALUE_V ("network.listen.port.random.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_listen_port_random(value); });
|
||||||
CMD_ANY ("network.listen.port.range", [](auto, auto) { return listen_port_range(); });
|
CMD_ANY ("network.listen.port.range", [](auto, auto) { return listen_port_range(); });
|
||||||
@@ -325,13 +214,9 @@ initialize_command_network() {
|
|||||||
CMD_ANY ("network.listen.backlog", [](auto, auto) { return torrent::runtime::network_config()->listen_backlog(); });
|
CMD_ANY ("network.listen.backlog", [](auto, auto) { return torrent::runtime::network_config()->listen_backlog(); });
|
||||||
CMD_ANY_VALUE_V ("network.listen.backlog.set", [](auto, auto& value) { return torrent::runtime::network_config()->set_listen_backlog(value); });
|
CMD_ANY_VALUE_V ("network.listen.backlog.set", [](auto, auto& value) { return torrent::runtime::network_config()->set_listen_backlog(value); });
|
||||||
|
|
||||||
CMD_ANY ("protocol.pex", [](auto, auto) { return torrent::runtime::client_config()->is_pex_enabled(); });
|
CMD_VAR_BOOL ("protocol.pex", true);
|
||||||
CMD_ANY_VALUE_V ("protocol.pex.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_pex_enabled(value); });
|
|
||||||
|
|
||||||
CMD_ANY_LIST ("protocol.encryption", [](auto, auto) { return get_encryption(); });
|
encryption_config::initialize_commands();
|
||||||
CMD_ANY_LIST ("protocol.encryption.set", [](auto, auto& args) { return apply_encryption(args); });
|
|
||||||
CMD_ANY_LIST ("protocol.encryption.handshake", [](auto, auto) { return get_handshake_encryption(); });
|
|
||||||
CMD_ANY_LIST ("protocol.encryption.stream", [](auto, auto) { return get_stream_encryption(); });
|
|
||||||
|
|
||||||
CMD_VAR_STRING ("protocol.connection.leech", "leech");
|
CMD_VAR_STRING ("protocol.connection.leech", "leech");
|
||||||
CMD_VAR_STRING ("protocol.connection.seed", "seed");
|
CMD_VAR_STRING ("protocol.connection.seed", "seed");
|
||||||
@@ -366,31 +251,18 @@ initialize_command_network() {
|
|||||||
CMD_ANY_STRING ("network.tos.set", [](auto, auto& str) { return apply_tos(str); });
|
CMD_ANY_STRING ("network.tos.set", [](auto, auto& str) { return apply_tos(str); });
|
||||||
|
|
||||||
CMD_ANY ("network.bind_address", [nw_config](auto, auto) { return nw_config->bind_address_best_match_str(); });
|
CMD_ANY ("network.bind_address", [nw_config](auto, auto) { return nw_config->bind_address_best_match_str(); });
|
||||||
CMD_ANY_STRING_V("network.bind_address.set", [nw_config](auto, auto& str) { return nw_config->set_bind_address(str); });
|
CMD_ANY_STRING_V("network.bind_address.set", [nw_config](auto, auto& str) { return nw_config->set_bind_address_str(str); });
|
||||||
CMD_ANY ("network.bind_address.ipv4", [nw_config](auto, auto) { return nw_config->bind_inet_address_str(); });
|
CMD_ANY ("network.bind_address.ipv4", [nw_config](auto, auto) { return nw_config->bind_inet_address_str(); });
|
||||||
CMD_ANY_STRING_V("network.bind_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet_address(str); });
|
CMD_ANY_STRING_V("network.bind_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet_address_str(str); });
|
||||||
CMD_ANY ("network.bind_address.ipv6", [nw_config](auto, auto) { return nw_config->bind_inet6_address_str(); });
|
CMD_ANY ("network.bind_address.ipv6", [nw_config](auto, auto) { return nw_config->bind_inet6_address_str(); });
|
||||||
CMD_ANY_STRING_V("network.bind_address.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet6_address(str); });
|
CMD_ANY_STRING_V("network.bind_address.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet6_address_str(str); });
|
||||||
|
|
||||||
CMD_ANY_STRING_V("network.bind_device.set", [nw_config](auto, auto& str) { return nw_config->set_bind_device_name(str); });
|
|
||||||
CMD_ANY ("network.bind_device.ipv4", [nw_config](auto, auto) { return nw_config->bind_inet_device_name(); });
|
|
||||||
CMD_ANY_STRING_V("network.bind_device.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet_device_name(str); });
|
|
||||||
CMD_ANY ("network.bind_device.ipv6", [nw_config](auto, auto) { return nw_config->bind_inet6_device_name(); });
|
|
||||||
CMD_ANY_STRING_V("network.bind_device.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet6_device_name(str); });
|
|
||||||
|
|
||||||
CMD_ANY ("network.local_address", [nw_config](auto, auto) { return nw_config->local_address_best_match_str(); });
|
CMD_ANY ("network.local_address", [nw_config](auto, auto) { return nw_config->local_address_best_match_str(); });
|
||||||
CMD_ANY_STRING_V("network.local_address.set", [nw_config](auto, auto& str) { return nw_config->set_local_address(str); });
|
CMD_ANY_STRING_V("network.local_address.set", [nw_config](auto, auto& str) { return nw_config->set_local_address_str(str); });
|
||||||
CMD_ANY ("network.local_address.ipv4", [nw_config](auto, auto) { return nw_config->local_inet_address_str(); });
|
CMD_ANY ("network.local_address.ipv4", [nw_config](auto, auto) { return nw_config->local_inet_address_str(); });
|
||||||
CMD_ANY_STRING_V("network.local_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet_address(str); });
|
CMD_ANY_STRING_V("network.local_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet_address_str(str); });
|
||||||
CMD_ANY ("network.local_address.ipv6", [nw_config](auto, auto) { return nw_config->local_inet6_address_str(); });
|
CMD_ANY ("network.local_address.ipv6", [nw_config](auto, auto) { return nw_config->local_inet6_address_str(); });
|
||||||
CMD_ANY_STRING_V("network.local_address.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet6_address(str); });
|
CMD_ANY_STRING_V("network.local_address.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet6_address_str(str); });
|
||||||
|
|
||||||
CMD_ANY ("network.local_port", [nw_config](auto, auto) { return nw_config->local_port_best_match(); });
|
|
||||||
CMD_ANY_VALUE_V ("network.local_port.set", [nw_config](auto, auto& value) { return nw_config->set_local_port(checked_port_value(value, "local")); });
|
|
||||||
CMD_ANY ("network.local_port.ipv4", [nw_config](auto, auto) { return nw_config->local_inet_port(); });
|
|
||||||
CMD_ANY_VALUE_V ("network.local_port.ipv4.set", [nw_config](auto, auto& value) { return nw_config->set_local_inet_port(checked_port_value(value, "local ipv4")); });
|
|
||||||
CMD_ANY ("network.local_port.ipv6", [nw_config](auto, auto) { return nw_config->local_inet6_port(); });
|
|
||||||
CMD_ANY_VALUE_V ("network.local_port.ipv6.set", [nw_config](auto, auto& value) { return nw_config->set_local_inet6_port(checked_port_value(value, "local ipv6")); });
|
|
||||||
|
|
||||||
CMD_ANY ("network.proxy.global", [](auto, auto) { return torrent::runtime::proxy_manager()->proxy_url(); });
|
CMD_ANY ("network.proxy.global", [](auto, auto) { return torrent::runtime::proxy_manager()->proxy_url(); });
|
||||||
CMD_ANY_STRING_V("network.proxy.global.set", [](auto, auto& str) { return torrent::runtime::proxy_manager()->set_proxy_url(str); });
|
CMD_ANY_STRING_V("network.proxy.global.set", [](auto, auto& str) { return torrent::runtime::proxy_manager()->set_proxy_url(str); });
|
||||||
@@ -415,10 +287,8 @@ initialize_command_network() {
|
|||||||
CMD_ANY ("network.xmlrpc.size_limit", [](auto, auto) { return rpc::rpc.size_limit(); });
|
CMD_ANY ("network.xmlrpc.size_limit", [](auto, auto) { return rpc::rpc.size_limit(); });
|
||||||
CMD_ANY_VALUE_V ("network.xmlrpc.size_limit.set", [](auto, auto& arg) { return rpc::rpc.set_size_limit(arg); });
|
CMD_ANY_VALUE_V ("network.xmlrpc.size_limit.set", [](auto, auto& arg) { return rpc::rpc.set_size_limit(arg); });
|
||||||
|
|
||||||
CMD_ANY ("network.rpc.use_xmlrpc", [](auto, auto) { return rpc::rpc.use_xmlrpc(); });
|
CMD_VAR_BOOL ("network.rpc.use_xmlrpc", true);
|
||||||
CMD_ANY_VALUE_V ("network.rpc.use_xmlrpc.set", [](auto, auto& arg) { return rpc::rpc.set_use_xmlrpc(arg); });
|
CMD_VAR_BOOL ("network.rpc.use_jsonrpc", true);
|
||||||
CMD_ANY ("network.rpc.use_jsonrpc", [](auto, auto) { return rpc::rpc.use_jsonrpc(); });
|
|
||||||
CMD_ANY_VALUE_V ("network.rpc.use_jsonrpc.set", [](auto, auto& arg) { return rpc::rpc.set_use_jsonrpc(arg); });
|
|
||||||
|
|
||||||
CMD_ANY ("network.block.ipv4", [nw_config](auto, auto) { return nw_config->is_block_ipv4(); });
|
CMD_ANY ("network.block.ipv4", [nw_config](auto, auto) { return nw_config->is_block_ipv4(); });
|
||||||
CMD_ANY_VALUE_V ("network.block.ipv4.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv4(value); });
|
CMD_ANY_VALUE_V ("network.block.ipv4.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv4(value); });
|
||||||
@@ -450,9 +320,6 @@ initialize_command_network() {
|
|||||||
rpc::rpc.mark_safe("network.receive_buffer.size");
|
rpc::rpc.mark_safe("network.receive_buffer.size");
|
||||||
rpc::rpc.mark_safe("network.bind_address");
|
rpc::rpc.mark_safe("network.bind_address");
|
||||||
rpc::rpc.mark_safe("network.local_address");
|
rpc::rpc.mark_safe("network.local_address");
|
||||||
rpc::rpc.mark_safe("network.local_port");
|
|
||||||
rpc::rpc.mark_safe("network.local_port.ipv4");
|
|
||||||
rpc::rpc.mark_safe("network.local_port.ipv6");
|
|
||||||
rpc::rpc.mark_safe("network.xmlrpc.size_limit");
|
rpc::rpc.mark_safe("network.xmlrpc.size_limit");
|
||||||
rpc::rpc.mark_safe("network.open_sockets");
|
rpc::rpc.mark_safe("network.open_sockets");
|
||||||
|
|
||||||
|
|||||||
@@ -28,10 +28,9 @@ cmd_scheduler_simple_removed(core::Download* download) {
|
|||||||
control->core()->download_list()->pause(download);
|
control->core()->download_list()->pause(download);
|
||||||
|
|
||||||
core::View* viewActive = *control->view_manager()->find("active");
|
core::View* viewActive = *control->view_manager()->find("active");
|
||||||
unsigned int numActive = viewActive->size_visible();
|
|
||||||
int64_t maxActive = rpc::call_command("scheduler.max_active", torrent::Object()).as_value();
|
int64_t maxActive = rpc::call_command("scheduler.max_active", torrent::Object()).as_value();
|
||||||
|
|
||||||
if ((int64_t)numActive >= maxActive)
|
if ((int64_t)viewActive->size_visible() >= maxActive)
|
||||||
return torrent::Object();
|
return torrent::Object();
|
||||||
|
|
||||||
// The 'started' view contains all the views we may choose amongst.
|
// The 'started' view contains all the views we may choose amongst.
|
||||||
@@ -42,9 +41,6 @@ cmd_scheduler_simple_removed(core::Download* download) {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
control->core()->download_list()->resume(*itr);
|
control->core()->download_list()->resume(*itr);
|
||||||
|
|
||||||
if ((int64_t)++numActive >= maxActive)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return torrent::Object();
|
return torrent::Object();
|
||||||
|
|||||||
@@ -1,365 +0,0 @@
|
|||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cctype>
|
|
||||||
#include <iterator>
|
|
||||||
#include <set>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
#include <torrent/exceptions.h>
|
|
||||||
#include <torrent/object.h>
|
|
||||||
|
|
||||||
#include "rpc/parse.h"
|
|
||||||
#include "rpc/rpc_manager.h"
|
|
||||||
|
|
||||||
#include "globals.h"
|
|
||||||
#include "control.h"
|
|
||||||
#include "command_helpers.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
constexpr int64_t max_string_pad_size = 1 << 14;
|
|
||||||
|
|
||||||
const std::string whitespace_characters = " \t\n\r\f\v";
|
|
||||||
|
|
||||||
// The byte offset of every utf-8 character in 'text', terminated by the offset
|
|
||||||
// past the last character. Bytes that are not valid utf-8 lead bytes are
|
|
||||||
// treated as single characters.
|
|
||||||
std::vector<size_t>
|
|
||||||
utf8_offsets(const std::string& text) {
|
|
||||||
std::vector<size_t> offsets;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < text.size(); i++)
|
|
||||||
if (i == 0 || (static_cast<unsigned char>(text[i]) & 0xC0) != 0x80)
|
|
||||||
offsets.push_back(i);
|
|
||||||
|
|
||||||
offsets.push_back(text.size());
|
|
||||||
return offsets;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t
|
|
||||||
utf8_length(const std::string& text) {
|
|
||||||
int64_t result = 0;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < text.size(); i++)
|
|
||||||
if (i == 0 || (static_cast<unsigned char>(text[i]) & 0xC0) != 0x80)
|
|
||||||
result++;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
utf8_substr(const std::string& text, const std::vector<size_t>& offsets, size_t first, size_t last) {
|
|
||||||
return text.substr(offsets[first], offsets[last] - offsets[first]);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::set<std::string>
|
|
||||||
utf8_character_set(const std::string& text) {
|
|
||||||
auto offsets = utf8_offsets(text);
|
|
||||||
std::set<std::string> result;
|
|
||||||
|
|
||||||
for (size_t i = 0; i + 1 < offsets.size(); i++)
|
|
||||||
result.insert(utf8_substr(text, offsets, i, i + 1));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
ascii_lowercase(std::string text) {
|
|
||||||
std::transform(text.begin(), text.end(), text.begin(), [](unsigned char c) { return std::tolower(c); });
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A 'max_count' of zero means the command accepts any number of arguments.
|
|
||||||
void
|
|
||||||
verify_argument_count(const char* name, const torrent::Object::list_type& args, size_t min_count, size_t max_count) {
|
|
||||||
if (args.size() < min_count || (max_count != 0 && args.size() > max_count))
|
|
||||||
throw torrent::input_error(std::string(name) + ": invalid number of arguments.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const torrent::Object&
|
|
||||||
argument_at(const torrent::Object::list_type& args, size_t index) {
|
|
||||||
return *std::next(args.begin(), index);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
flatten_argument(const torrent::Object& arg, std::vector<std::string>* dest) {
|
|
||||||
if (!arg.is_list()) {
|
|
||||||
dest->push_back(rpc::convert_to_string(arg));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& child : arg.as_list())
|
|
||||||
flatten_argument(child, dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The {old, new} pairs shared by 'string.map' and 'string.replace'.
|
|
||||||
std::pair<std::string, std::string>
|
|
||||||
argument_to_pair(const char* name, const torrent::Object& arg) {
|
|
||||||
if (!arg.is_list() || arg.as_list().size() != 2)
|
|
||||||
throw torrent::input_error(std::string(name) + ": arguments after the text must be {old, new} pairs.");
|
|
||||||
|
|
||||||
return {rpc::convert_to_string(arg.as_list().front()), rpc::convert_to_string(arg.as_list().back())};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compares the first argument against every remaining argument, returning true
|
|
||||||
// as soon as one of them matches.
|
|
||||||
torrent::Object
|
|
||||||
apply_string_predicate(const char* name, const torrent::Object::list_type& args, bool (*predicate)(const std::string&, const std::string&)) {
|
|
||||||
verify_argument_count(name, args, 2, 0);
|
|
||||||
|
|
||||||
auto text = rpc::convert_to_string(args.front());
|
|
||||||
|
|
||||||
for (auto itr = std::next(args.begin()); itr != args.end(); itr++)
|
|
||||||
if (predicate(text, rpc::convert_to_string(*itr)))
|
|
||||||
return int64_t(1);
|
|
||||||
|
|
||||||
return int64_t(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
text_equals(const std::string& text, const std::string& other) {
|
|
||||||
return text == other;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
text_starts_with(const std::string& text, const std::string& prefix) {
|
|
||||||
return text.size() >= prefix.size() && text.compare(0, prefix.size(), prefix) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
text_ends_with(const std::string& text, const std::string& tail) {
|
|
||||||
return text.size() >= tail.size() && text.compare(text.size() - tail.size(), tail.size(), tail) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
text_contains(const std::string& text, const std::string& needle) {
|
|
||||||
return text.find(needle) != std::string::npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
text_contains_i(const std::string& text, const std::string& needle) {
|
|
||||||
return ascii_lowercase(text).find(ascii_lowercase(needle)) != std::string::npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
apply_string_length(const torrent::Object::list_type& args) {
|
|
||||||
verify_argument_count("string.length", args, 1, 1);
|
|
||||||
|
|
||||||
return utf8_length(rpc::convert_to_string(args.front()));
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
apply_string_substr(const torrent::Object::list_type& args) {
|
|
||||||
verify_argument_count("string.substr", args, 1, 4);
|
|
||||||
|
|
||||||
auto text = rpc::convert_to_string(args.front());
|
|
||||||
auto offsets = utf8_offsets(text);
|
|
||||||
auto text_length = static_cast<int64_t>(offsets.size() - 1);
|
|
||||||
|
|
||||||
auto position = args.size() > 1 ? rpc::convert_to_value(argument_at(args, 1)) : 0;
|
|
||||||
auto fallback = args.size() > 3 ? rpc::convert_to_string(argument_at(args, 3)) : std::string();
|
|
||||||
|
|
||||||
// Negative positions are relative to the end of the string.
|
|
||||||
if (position < 0)
|
|
||||||
position += text_length;
|
|
||||||
|
|
||||||
if (position < 0 || position > text_length)
|
|
||||||
return fallback;
|
|
||||||
|
|
||||||
auto last = text_length;
|
|
||||||
|
|
||||||
if (args.size() > 2) {
|
|
||||||
auto count = rpc::convert_to_value(argument_at(args, 2));
|
|
||||||
|
|
||||||
if (count < 0)
|
|
||||||
throw torrent::input_error("string.substr: the character count cannot be negative.");
|
|
||||||
|
|
||||||
last = std::min(text_length, position + std::min(count, text_length));
|
|
||||||
}
|
|
||||||
|
|
||||||
return utf8_substr(text, offsets, position, last);
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
apply_string_split(const torrent::Object::list_type& args) {
|
|
||||||
verify_argument_count("string.split", args, 2, 2);
|
|
||||||
|
|
||||||
auto text = rpc::convert_to_string(args.front());
|
|
||||||
auto delim = rpc::convert_to_string(args.back());
|
|
||||||
auto result = torrent::Object::create_list();
|
|
||||||
|
|
||||||
// An empty delimiter splits the text into its utf-8 characters.
|
|
||||||
if (delim.empty()) {
|
|
||||||
auto offsets = utf8_offsets(text);
|
|
||||||
|
|
||||||
for (size_t i = 0; i + 1 < offsets.size(); i++)
|
|
||||||
result.as_list().push_back(utf8_substr(text, offsets, i, i + 1));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t first = 0;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
auto pos = text.find(delim, first);
|
|
||||||
|
|
||||||
if (pos == std::string::npos) {
|
|
||||||
result.as_list().push_back(text.substr(first));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.as_list().push_back(text.substr(first, pos - first));
|
|
||||||
first = pos + delim.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
apply_string_join(const torrent::Object::list_type& args) {
|
|
||||||
verify_argument_count("string.join", args, 1, 0);
|
|
||||||
|
|
||||||
auto delim = rpc::convert_to_string(args.front());
|
|
||||||
|
|
||||||
std::vector<std::string> parts;
|
|
||||||
|
|
||||||
for (auto itr = std::next(args.begin()); itr != args.end(); itr++)
|
|
||||||
flatten_argument(*itr, &parts);
|
|
||||||
|
|
||||||
std::string result;
|
|
||||||
|
|
||||||
for (auto itr = parts.begin(); itr != parts.end(); itr++) {
|
|
||||||
if (itr != parts.begin())
|
|
||||||
result += delim;
|
|
||||||
|
|
||||||
result += *itr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
apply_string_pad(const char* name, const torrent::Object::list_type& args, bool pad_start) {
|
|
||||||
verify_argument_count(name, args, 2, 3);
|
|
||||||
|
|
||||||
auto text = rpc::convert_to_string(args.front());
|
|
||||||
auto pad_size = rpc::convert_to_value(argument_at(args, 1));
|
|
||||||
auto padding = args.size() > 2 ? rpc::convert_to_string(argument_at(args, 2)) : std::string(" ");
|
|
||||||
|
|
||||||
auto text_length = utf8_length(text);
|
|
||||||
|
|
||||||
if (pad_size <= text_length || padding.empty())
|
|
||||||
return text;
|
|
||||||
|
|
||||||
if (pad_size - text_length > max_string_pad_size)
|
|
||||||
throw torrent::input_error(std::string(name) + ": padding is too large.");
|
|
||||||
|
|
||||||
auto padding_offsets = utf8_offsets(padding);
|
|
||||||
auto padding_length = static_cast<int64_t>(padding_offsets.size() - 1);
|
|
||||||
|
|
||||||
std::string result;
|
|
||||||
result.reserve(pad_size - text_length);
|
|
||||||
|
|
||||||
for (int64_t i = 0; i < pad_size - text_length; i++) {
|
|
||||||
auto index = static_cast<size_t>(i % padding_length);
|
|
||||||
result += utf8_substr(padding, padding_offsets, index, index + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pad_start ? result + text : text + result;
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
apply_string_strip(const char* name, const torrent::Object::list_type& args, bool strip_start, bool strip_end) {
|
|
||||||
verify_argument_count(name, args, 1, 0);
|
|
||||||
|
|
||||||
auto text = rpc::convert_to_string(args.front());
|
|
||||||
|
|
||||||
std::string strippable;
|
|
||||||
|
|
||||||
for (auto itr = std::next(args.begin()); itr != args.end(); itr++)
|
|
||||||
strippable += rpc::convert_to_string(*itr);
|
|
||||||
|
|
||||||
if (args.size() == 1)
|
|
||||||
strippable = whitespace_characters;
|
|
||||||
|
|
||||||
auto characters = utf8_character_set(strippable);
|
|
||||||
auto offsets = utf8_offsets(text);
|
|
||||||
|
|
||||||
size_t first = 0;
|
|
||||||
size_t last = offsets.size() - 1;
|
|
||||||
|
|
||||||
while (strip_start && first < last && characters.count(utf8_substr(text, offsets, first, first + 1)) != 0)
|
|
||||||
first++;
|
|
||||||
|
|
||||||
while (strip_end && last > first && characters.count(utf8_substr(text, offsets, last - 1, last)) != 0)
|
|
||||||
last--;
|
|
||||||
|
|
||||||
return utf8_substr(text, offsets, first, last);
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
apply_string_map(const torrent::Object::list_type& args) {
|
|
||||||
verify_argument_count("string.map", args, 2, 0);
|
|
||||||
|
|
||||||
auto text = rpc::convert_to_string(args.front());
|
|
||||||
|
|
||||||
for (auto itr = std::next(args.begin()); itr != args.end(); itr++) {
|
|
||||||
auto pair = argument_to_pair("string.map", *itr);
|
|
||||||
|
|
||||||
if (text == pair.first)
|
|
||||||
return pair.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
apply_string_replace(const torrent::Object::list_type& args) {
|
|
||||||
verify_argument_count("string.replace", args, 2, 0);
|
|
||||||
|
|
||||||
auto text = rpc::convert_to_string(args.front());
|
|
||||||
|
|
||||||
for (auto itr = std::next(args.begin()); itr != args.end(); itr++) {
|
|
||||||
auto pair = argument_to_pair("string.replace", *itr);
|
|
||||||
|
|
||||||
if (pair.first.empty())
|
|
||||||
throw torrent::input_error("string.replace: the replaced text cannot be empty.");
|
|
||||||
|
|
||||||
for (auto pos = text.find(pair.first); pos != std::string::npos; pos = text.find(pair.first, pos + pair.second.size()))
|
|
||||||
text.replace(pos, pair.first.size(), pair.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
initialize_command_string() {
|
|
||||||
// clang-format off
|
|
||||||
CMD2_ANY_LIST("string.length", [](auto, const auto& args) { return apply_string_length(args); });
|
|
||||||
CMD2_ANY_LIST("string.substr", [](auto, const auto& args) { return apply_string_substr(args); });
|
|
||||||
CMD2_ANY_LIST("string.split", [](auto, const auto& args) { return apply_string_split(args); });
|
|
||||||
CMD2_ANY_LIST("string.join", [](auto, const auto& args) { return apply_string_join(args); });
|
|
||||||
CMD2_ANY_LIST("string.map", [](auto, const auto& args) { return apply_string_map(args); });
|
|
||||||
CMD2_ANY_LIST("string.replace", [](auto, const auto& args) { return apply_string_replace(args); });
|
|
||||||
|
|
||||||
CMD2_ANY_LIST("string.equals", [](auto, const auto& args) { return apply_string_predicate("string.equals", args, &text_equals); });
|
|
||||||
CMD2_ANY_LIST("string.starts_with", [](auto, const auto& args) { return apply_string_predicate("string.starts_with", args, &text_starts_with); });
|
|
||||||
CMD2_ANY_LIST("string.ends_with", [](auto, const auto& args) { return apply_string_predicate("string.ends_with", args, &text_ends_with); });
|
|
||||||
CMD2_ANY_LIST("string.contains", [](auto, const auto& args) { return apply_string_predicate("string.contains", args, &text_contains); });
|
|
||||||
CMD2_ANY_LIST("string.contains_i", [](auto, const auto& args) { return apply_string_predicate("string.contains_i", args, &text_contains_i); });
|
|
||||||
|
|
||||||
CMD2_ANY_LIST("string.lpad", [](auto, const auto& args) { return apply_string_pad("string.lpad", args, true); });
|
|
||||||
CMD2_ANY_LIST("string.rpad", [](auto, const auto& args) { return apply_string_pad("string.rpad", args, false); });
|
|
||||||
|
|
||||||
CMD2_ANY_LIST("string.strip", [](auto, const auto& args) { return apply_string_strip("string.strip", args, true, true); });
|
|
||||||
CMD2_ANY_LIST("string.lstrip", [](auto, const auto& args) { return apply_string_strip("string.lstrip", args, true, false); });
|
|
||||||
CMD2_ANY_LIST("string.rstrip", [](auto, const auto& args) { return apply_string_strip("string.rstrip", args, false, true); });
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
for (const auto name : {"string.length", "string.substr", "string.split", "string.join", "string.map", "string.replace",
|
|
||||||
"string.equals", "string.starts_with", "string.ends_with", "string.contains", "string.contains_i",
|
|
||||||
"string.lpad", "string.rpad", "string.strip", "string.lstrip", "string.rstrip"})
|
|
||||||
rpc::rpc.mark_safe(name);
|
|
||||||
}
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <torrent/data/file_manager.h>
|
|
||||||
#include <torrent/runtime/client_config.h>
|
|
||||||
#include <torrent/runtime/memory_manager.h>
|
|
||||||
#include <torrent/runtime/socket_manager.h>
|
|
||||||
#include <torrent/runtime/runtime.h>
|
|
||||||
#include <torrent/utils/chrono.h>
|
|
||||||
#include <torrent/utils/option_strings.h>
|
|
||||||
|
|
||||||
#include "command_helpers.h"
|
|
||||||
#include "control.h"
|
|
||||||
#include "globals.h"
|
|
||||||
#include "core/manager.h"
|
|
||||||
#include "session/session_manager.h"
|
|
||||||
#include "utils/file_status_cache.h"
|
|
||||||
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
system_env(const torrent::Object::string_type& arg) {
|
|
||||||
if (arg.empty())
|
|
||||||
throw torrent::input_error("system.env: Missing variable name.");
|
|
||||||
|
|
||||||
char* val = getenv(arg.c_str());
|
|
||||||
return std::string(val ? val : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
system_hostname() {
|
|
||||||
char buffer[1024];
|
|
||||||
|
|
||||||
if (gethostname(buffer, 1023) == -1)
|
|
||||||
throw torrent::input_error("Unable to read hostname.");
|
|
||||||
|
|
||||||
// if (shorten)
|
|
||||||
// *std::find(buffer, buffer + 1023, '.') = '\0';
|
|
||||||
|
|
||||||
return std::string(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
system_get_cwd() {
|
|
||||||
char* buffer = getcwd(NULL, 0);
|
|
||||||
|
|
||||||
if (buffer == NULL)
|
|
||||||
throw torrent::input_error("Unable to read cwd.");
|
|
||||||
|
|
||||||
torrent::Object result = torrent::Object(std::string(buffer));
|
|
||||||
free(buffer);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
system_set_cwd(const torrent::Object::string_type& rawArgs) {
|
|
||||||
if (::chdir(rawArgs.c_str()) != 0)
|
|
||||||
throw torrent::input_error("Could not change current working directory.");
|
|
||||||
|
|
||||||
return torrent::Object();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t
|
|
||||||
checked_socket_value(int64_t value, const char* label) {
|
|
||||||
if (value < 0 || value > std::numeric_limits<uint32_t>::max())
|
|
||||||
throw torrent::input_error(std::string("Invalid ") + label + " value.");
|
|
||||||
|
|
||||||
return static_cast<uint32_t>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
initialize_command_system() {
|
|
||||||
CMD_ANY ("system.hostname", std::bind(&system_hostname));
|
|
||||||
CMD_ANY ("system.pid", std::bind(&getpid));
|
|
||||||
|
|
||||||
CMD_VAR_C_STRING("system.api_version", (int64_t)API_VERSION);
|
|
||||||
CMD_VAR_C_STRING("system.client_version", PACKAGE_VERSION);
|
|
||||||
CMD_VAR_C_STRING("system.library_version", torrent::runtime::version());
|
|
||||||
|
|
||||||
CMD_ANY ("system.torrent_name.use_sanitized", [](auto, auto) { return torrent::runtime::client_config()->torrent_name_use_sanitized(); });
|
|
||||||
CMD_ANY_VALUE_V ("system.torrent_name.use_sanitized.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_torrent_name_use_sanitized(value); });
|
|
||||||
|
|
||||||
CMD_VAR_VALUE ("system.file.allocate", 0);
|
|
||||||
CMD_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30);
|
|
||||||
CMD_VAR_VALUE ("system.file.split_size", -1);
|
|
||||||
CMD_VAR_STRING ("system.file.split_suffix", ".part");
|
|
||||||
|
|
||||||
CMD_ANY ("system.file_name.replace_slash", [](auto, auto) { return torrent::runtime::client_config()->file_name_replace_slash(); });
|
|
||||||
CMD_ANY_STRING_V("system.file_name.replace_slash.set", [](auto, auto& str) { return torrent::runtime::client_config()->set_file_name_replace_slash(str); });
|
|
||||||
|
|
||||||
CMD_ANY ("system.file_status_cache.size", [](auto, auto) { return control->core()->file_status_cache()->size(); });
|
|
||||||
CMD_ANY_V ("system.file_status_cache.prune", [](auto, auto) { return control->core()->file_status_cache()->prune(); });
|
|
||||||
|
|
||||||
CMD_VAR_BOOL ("file.prioritize_toc", 0);
|
|
||||||
CMD_VAR_LIST ("file.prioritize_toc.first");
|
|
||||||
CMD_VAR_LIST ("file.prioritize_toc.last");
|
|
||||||
|
|
||||||
CMD_ANY ("system.files.advise_random", [](auto, auto) { return torrent::file_manager()->advise_random(); });
|
|
||||||
CMD_ANY_VALUE_V ("system.files.advise_random.set", [](auto, auto& value) { return torrent::file_manager()->set_advise_random(value); });
|
|
||||||
CMD_ANY ("system.files.advise_random.hashing", [](auto, auto) { return torrent::file_manager()->advise_random_hashing(); });
|
|
||||||
CMD_ANY_VALUE_V ("system.files.advise_random.hashing.set", [](auto, auto& value) { return torrent::file_manager()->set_advise_random_hashing(value); });
|
|
||||||
CMD_ANY ("system.files.session.fdatasync", [](auto, auto) { return session_thread::manager()->use_fsyncdisk(); });
|
|
||||||
CMD_ANY_VALUE_V ("system.files.session.fdatasync.set", [](auto, auto& value) { return session_thread::manager()->set_use_fsyncdisk(value); });
|
|
||||||
|
|
||||||
CMD_ANY ("system.files.close_idle", [](auto, auto) { return torrent::file_manager()->close_idle_timeout().count(); });
|
|
||||||
CMD_ANY_VALUE_V ("system.files.close_idle.set", [](auto, auto& value) { return torrent::file_manager()->set_close_idle_timeout(value * 1s); });
|
|
||||||
|
|
||||||
CMD_ANY ("system.files.opened_counter", [](auto, auto) { return torrent::file_manager()->files_opened_counter(); });
|
|
||||||
CMD_ANY ("system.files.closed_counter", [](auto, auto) { return torrent::file_manager()->files_closed_counter(); });
|
|
||||||
CMD_ANY ("system.files.failed_counter", [](auto, auto) { return torrent::file_manager()->files_failed_counter(); });
|
|
||||||
|
|
||||||
CMD_ANY_STRING ("system.env", [](auto, auto& str) { return system_env(str); });
|
|
||||||
|
|
||||||
CMD_ANY ("system.time", [](auto, auto) { return torrent::this_thread::cached_seconds().count(); });
|
|
||||||
CMD_ANY ("system.time_seconds", [](auto, auto) { return torrent::utils::cast_seconds(torrent::utils::time_since_epoch()).count(); });
|
|
||||||
CMD_ANY ("system.time_usec", [](auto, auto) { return torrent::utils::time_since_epoch().count(); });
|
|
||||||
|
|
||||||
CMD_ANY_VALUE_V ("system.umask.set", [](auto, auto& value) { return ::umask(value); });
|
|
||||||
|
|
||||||
CMD_VAR_BOOL ("system.daemon", false);
|
|
||||||
|
|
||||||
CMD_ANY_V ("system.shutdown.normal", [](auto, auto) { control->receive_normal_shutdown(); });
|
|
||||||
CMD_ANY_V ("system.shutdown.quick", [](auto, auto) { control->receive_quick_shutdown(); });
|
|
||||||
|
|
||||||
CMD_REDIRECT_NO_EXPORT("system.shutdown", "system.shutdown.normal");
|
|
||||||
|
|
||||||
CMD_ANY ("system.cwd", [](auto, auto) { return system_get_cwd(); });
|
|
||||||
CMD_ANY_STRING ("system.cwd.set", [](auto, auto& str) { return system_set_cwd(str); });
|
|
||||||
|
|
||||||
CMD_ANY ("system.sockets.size", [](auto, auto) { return torrent::runtime::socket_manager()->size(); });
|
|
||||||
CMD_ANY ("system.sockets.max_size", [](auto, auto) { return torrent::runtime::socket_manager()->max_size(); });
|
|
||||||
CMD_ANY_VALUE_V ("system.sockets.max_size.set", [](auto, auto& value) {
|
|
||||||
return torrent::runtime::socket_manager()->set_max_size_and_adjust(checked_socket_value(value, "socket max size"));
|
|
||||||
});
|
|
||||||
CMD_ANY_V ("system.sockets.adjust_alloc", [](auto, auto) { torrent::runtime::socket_manager()->adjust_allocation(); });
|
|
||||||
CMD_ANY ("system.sockets.reserved_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->reserved_allocation(); });
|
|
||||||
CMD_ANY ("system.sockets.available_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->available_allocation(); });
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
|
|
||||||
auto category = static_cast<torrent::runtime::socket_manager_category_t>(i);
|
|
||||||
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
|
|
||||||
|
|
||||||
CMD_ANY (category_name + ".size", [category](auto, auto) { return torrent::runtime::socket_manager()->category_managed_size(category); });
|
|
||||||
CMD_ANY (category_name + ".max_size", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_size(category); });
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
CMD_ANY (category_name + ".min_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->generic_min_allocation(); });
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
CMD_ANY (category_name + ".max_alloc.limit", [category](auto, auto) { return torrent::runtime::socket_manager()->category_alloc_limit(category); });
|
|
||||||
CMD_ANY (category_name + ".min_alloc.limit", [category](auto, auto) { return torrent::runtime::socket_manager()->category_alloc_minimum(category); });
|
|
||||||
CMD_ANY (category_name + ".min_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_min_allocation(category); });
|
|
||||||
CMD_ANY (category_name + ".max_alloc", [category](auto, auto) { return torrent::runtime::socket_manager()->category_max_allocation(category); });
|
|
||||||
CMD_ANY_VALUE_V(category_name + ".min_alloc.set", [category](auto, auto& value) {
|
|
||||||
torrent::runtime::socket_manager()->set_category_min_allocation(category, checked_socket_value(value, "socket min alloc"));
|
|
||||||
});
|
|
||||||
CMD_ANY_VALUE_V(category_name + ".max_alloc.set", [category](auto, auto& value) {
|
|
||||||
torrent::runtime::socket_manager()->set_category_max_allocation(category, checked_socket_value(value, "socket max alloc"));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
rpc::rpc.mark_safe("system.api_version");
|
|
||||||
rpc::rpc.mark_safe("system.client_version");
|
|
||||||
rpc::rpc.mark_safe("system.library_version");
|
|
||||||
rpc::rpc.mark_safe("system.time");
|
|
||||||
rpc::rpc.mark_safe("system.time_seconds");
|
|
||||||
rpc::rpc.mark_safe("system.time_usec");
|
|
||||||
rpc::rpc.mark_safe("system.file.allocate");
|
|
||||||
rpc::rpc.mark_safe("system.file.max_size");
|
|
||||||
rpc::rpc.mark_safe("system.file.split_size");
|
|
||||||
rpc::rpc.mark_safe("system.file.split_suffix");
|
|
||||||
|
|
||||||
rpc::rpc.mark_safe("system.sockets.size");
|
|
||||||
rpc::rpc.mark_safe("system.sockets.max_size");
|
|
||||||
rpc::rpc.mark_safe("system.sockets.reserved_alloc");
|
|
||||||
rpc::rpc.mark_safe("system.sockets.available_alloc");
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
|
|
||||||
auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
|
|
||||||
|
|
||||||
rpc::rpc.mark_safe(category_name + ".size");
|
|
||||||
rpc::rpc.mark_safe(category_name + ".max_size");
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
rpc::rpc.mark_safe(category_name + ".min_alloc");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
rpc::rpc.mark_safe(category_name + ".max_alloc.limit");
|
|
||||||
rpc::rpc.mark_safe(category_name + ".min_alloc.limit");
|
|
||||||
rpc::rpc.mark_safe(category_name + ".min_alloc");
|
|
||||||
rpc::rpc.mark_safe(category_name + ".max_alloc");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+60
-29
@@ -1,9 +1,10 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <limits>
|
#include <cstdio>
|
||||||
#include <torrent/throttle.h>
|
#include <torrent/throttle.h>
|
||||||
#include <torrent/rate.h>
|
#include <torrent/rate.h>
|
||||||
#include <torrent/download/resource_manager.h>
|
#include <torrent/download/resource_manager.h>
|
||||||
|
#include <torrent/net/socket_address.h>
|
||||||
|
|
||||||
#include "core/manager.h"
|
#include "core/manager.h"
|
||||||
#include "ui/root.h"
|
#include "ui/root.h"
|
||||||
@@ -14,6 +15,59 @@
|
|||||||
#include "control.h"
|
#include "control.h"
|
||||||
#include "command_helpers.h"
|
#include "command_helpers.h"
|
||||||
|
|
||||||
|
std::pair<uint32_t, uint32_t>
|
||||||
|
parse_address_range(const torrent::Object::list_type& args, torrent::Object::list_type::const_iterator itr) {
|
||||||
|
unsigned int prefixWidth, ret;
|
||||||
|
char dummy;
|
||||||
|
char host[1024];
|
||||||
|
torrent::sa_unique_ptr sa;
|
||||||
|
|
||||||
|
ret = std::sscanf(itr->as_string().c_str(), "%1023[^/]/%d%c", host, &prefixWidth, &dummy);
|
||||||
|
|
||||||
|
if (ret < 1)
|
||||||
|
throw torrent::input_error("Invalid address/prefix.");
|
||||||
|
|
||||||
|
try {
|
||||||
|
sa = torrent::sa_copy(torrent::sa_lookup_address(host, AF_INET).get());
|
||||||
|
|
||||||
|
} catch (torrent::input_error& e) {
|
||||||
|
throw torrent::input_error("Could not resolve host: " + std::string(e.what()));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t begin, end;
|
||||||
|
|
||||||
|
auto sa_addr = htonl(reinterpret_cast<sockaddr_in*>(sa.get())->sin_addr.s_addr);
|
||||||
|
|
||||||
|
begin = end = sa_addr;
|
||||||
|
|
||||||
|
if (ret == 2) {
|
||||||
|
if (++itr != args.end())
|
||||||
|
throw torrent::input_error("Cannot specify both network and range end.");
|
||||||
|
|
||||||
|
uint32_t netmask = std::numeric_limits<uint32_t>::max() << (32 - prefixWidth);
|
||||||
|
|
||||||
|
if (prefixWidth >= 32 || sa_addr & ~netmask)
|
||||||
|
throw torrent::input_error("Invalid address/prefix.");
|
||||||
|
|
||||||
|
end = sa_addr | ~netmask;
|
||||||
|
|
||||||
|
} else if (++itr != args.end()) {
|
||||||
|
try {
|
||||||
|
sa = torrent::sa_copy(torrent::sa_lookup_address(itr->as_string(), AF_INET).get());
|
||||||
|
|
||||||
|
} catch (torrent::input_error& e) {
|
||||||
|
throw torrent::input_error("Could not resolve host: " + std::string(e.what()));
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_addr = htonl(reinterpret_cast<sockaddr_in*>(sa.get())->sin_addr.s_addr);
|
||||||
|
end = sa_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert to [begin, end) making sure the end doesn't overflow
|
||||||
|
// (this precludes 255.255.255.255 from ever matching, but that's not a real IP anyway)
|
||||||
|
return std::make_pair((uint32_t)begin, (uint32_t)std::max(end, end + 1));
|
||||||
|
}
|
||||||
|
|
||||||
torrent::Object
|
torrent::Object
|
||||||
apply_throttle(const torrent::Object::list_type& args, bool up) {
|
apply_throttle(const torrent::Object::list_type& args, bool up) {
|
||||||
auto arg_itr = args.begin();
|
auto arg_itr = args.begin();
|
||||||
@@ -29,16 +83,11 @@ apply_throttle(const torrent::Object::list_type& args, bool up) {
|
|||||||
throw torrent::input_error("Missing throttle rate for '" + name + "'.");
|
throw torrent::input_error("Missing throttle rate for '" + name + "'.");
|
||||||
|
|
||||||
int64_t rate;
|
int64_t rate;
|
||||||
|
rpc::parse_whole_value_nothrow(arg_itr->as_string().c_str(), &rate);
|
||||||
if (!rpc::parse_whole_value_nothrow(arg_itr->as_string().c_str(), &rate))
|
|
||||||
throw torrent::input_error("Invalid throttle rate for '" + name + "'.");
|
|
||||||
|
|
||||||
if (rate < 0)
|
if (rate < 0)
|
||||||
throw torrent::input_error("Throttle rate must be non-negative.");
|
throw torrent::input_error("Throttle rate must be non-negative.");
|
||||||
|
|
||||||
if (rate > (std::numeric_limits<int64_t>::max() >> 10))
|
|
||||||
throw torrent::input_error("Throttle rate is too large.");
|
|
||||||
|
|
||||||
auto itr = control->core()->throttles().find(name);
|
auto itr = control->core()->throttles().find(name);
|
||||||
|
|
||||||
if (itr == control->core()->throttles().end())
|
if (itr == control->core()->throttles().end())
|
||||||
@@ -89,24 +138,6 @@ throttle_update(const char* variable, int64_t value) {
|
|||||||
return torrent::Object();
|
return torrent::Object();
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int
|
|
||||||
throttle_rate_to_kb(int64_t rate) {
|
|
||||||
if (rate < 0 || rate > std::numeric_limits<unsigned int>::max() - 1)
|
|
||||||
throw torrent::input_error("Throttle rate must be between 0 and 4294967294.");
|
|
||||||
|
|
||||||
return static_cast<unsigned int>(rate >> 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
set_up_throttle_i64(ui::Root* root, int64_t rate) {
|
|
||||||
root->set_up_throttle(throttle_rate_to_kb(rate));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
set_down_throttle_i64(ui::Root* root, int64_t rate) {
|
|
||||||
root->set_down_throttle(throttle_rate_to_kb(rate));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
initialize_command_throttle() {
|
initialize_command_throttle() {
|
||||||
CMD2_ANY ("throttle.unchoked_uploads", std::bind(&torrent::ResourceManager::currently_upload_unchoked, torrent::resource_manager()));
|
CMD2_ANY ("throttle.unchoked_uploads", std::bind(&torrent::ResourceManager::currently_upload_unchoked, torrent::resource_manager()));
|
||||||
@@ -143,13 +174,13 @@ initialize_command_throttle() {
|
|||||||
CMD2_ANY ("throttle.global_up.rate", std::bind(&torrent::Rate::rate, torrent::up_rate()));
|
CMD2_ANY ("throttle.global_up.rate", std::bind(&torrent::Rate::rate, torrent::up_rate()));
|
||||||
CMD2_ANY ("throttle.global_up.total", std::bind(&torrent::Rate::total, torrent::up_rate()));
|
CMD2_ANY ("throttle.global_up.total", std::bind(&torrent::Rate::total, torrent::up_rate()));
|
||||||
CMD2_ANY ("throttle.global_up.max_rate", std::bind(&torrent::Throttle::max_rate, torrent::up_throttle_global()));
|
CMD2_ANY ("throttle.global_up.max_rate", std::bind(&torrent::Throttle::max_rate, torrent::up_throttle_global()));
|
||||||
CMD2_ANY_VALUE_V ("throttle.global_up.max_rate.set", std::bind(&set_up_throttle_i64, control->ui(), std::placeholders::_2));
|
CMD2_ANY_VALUE_V ("throttle.global_up.max_rate.set", std::bind(&ui::Root::set_up_throttle_i64, control->ui(), std::placeholders::_2));
|
||||||
CMD2_ANY_VALUE_KB("throttle.global_up.max_rate.set_kb", std::bind(&set_up_throttle_i64, control->ui(), std::placeholders::_2));
|
CMD2_ANY_VALUE_KB("throttle.global_up.max_rate.set_kb", std::bind(&ui::Root::set_up_throttle_i64, control->ui(), std::placeholders::_2));
|
||||||
CMD2_ANY ("throttle.global_down.rate", std::bind(&torrent::Rate::rate, torrent::down_rate()));
|
CMD2_ANY ("throttle.global_down.rate", std::bind(&torrent::Rate::rate, torrent::down_rate()));
|
||||||
CMD2_ANY ("throttle.global_down.total", std::bind(&torrent::Rate::total, torrent::down_rate()));
|
CMD2_ANY ("throttle.global_down.total", std::bind(&torrent::Rate::total, torrent::down_rate()));
|
||||||
CMD2_ANY ("throttle.global_down.max_rate", std::bind(&torrent::Throttle::max_rate, torrent::down_throttle_global()));
|
CMD2_ANY ("throttle.global_down.max_rate", std::bind(&torrent::Throttle::max_rate, torrent::down_throttle_global()));
|
||||||
CMD2_ANY_VALUE_V ("throttle.global_down.max_rate.set", std::bind(&set_down_throttle_i64, control->ui(), std::placeholders::_2));
|
CMD2_ANY_VALUE_V ("throttle.global_down.max_rate.set", std::bind(&ui::Root::set_down_throttle_i64, control->ui(), std::placeholders::_2));
|
||||||
CMD2_ANY_VALUE_KB("throttle.global_down.max_rate.set_kb", std::bind(&set_down_throttle_i64, control->ui(), std::placeholders::_2));
|
CMD2_ANY_VALUE_KB("throttle.global_down.max_rate.set_kb", std::bind(&ui::Root::set_down_throttle_i64, control->ui(), std::placeholders::_2));
|
||||||
|
|
||||||
// Temporary names, need to change this to accept real rates rather
|
// Temporary names, need to change this to accept real rates rather
|
||||||
// than kB.
|
// than kB.
|
||||||
|
|||||||
@@ -68,11 +68,11 @@ apply_dht_add_node(const std::string& arg) {
|
|||||||
torrent::Object
|
torrent::Object
|
||||||
apply_enable_trackers(int64_t arg) {
|
apply_enable_trackers(int64_t arg) {
|
||||||
if (arg == 0) {
|
if (arg == 0) {
|
||||||
for (const auto& download : *control->core()->download_list())
|
for (auto download : *control->core()->download_list())
|
||||||
download->tracker_controller().for_each([](auto& tracker) { tracker.disable(); });
|
download->tracker_controller().for_each([](auto& tracker) { tracker.disable(); });
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
for (const auto& download : *control->core()->download_list())
|
for (auto download : *control->core()->download_list())
|
||||||
download->tracker_controller().for_each([](auto& tracker) { tracker.enable(); });
|
download->tracker_controller().for_each([](auto& tracker) { tracker.enable(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ initialize_command_tracker() {
|
|||||||
lt_log_print(torrent::LOG_DHT_ERROR, "dht.port.set is no longer supported, use dht.override_port.set", 0);
|
lt_log_print(torrent::LOG_DHT_ERROR, "dht.port.set is no longer supported, use dht.override_port.set", 0);
|
||||||
});
|
});
|
||||||
CMD2_ANY ("dht.override_port", [](auto, auto) { return torrent::runtime::network_config()->override_dht_port(); });
|
CMD2_ANY ("dht.override_port", [](auto, auto) { return torrent::runtime::network_config()->override_dht_port(); });
|
||||||
CMD2_ANY_VALUE_V ("dht.override_port.set", [](auto, auto& value) { return torrent::runtime::network_manager()->set_dht_port(checked_port_value(value, "DHT override")); });
|
CMD2_ANY_VALUE_V ("dht.override_port.set", [](auto, auto& value) { return torrent::runtime::network_manager()->set_dht_port(value); });
|
||||||
|
|
||||||
CMD2_ANY_STRING ("dht.add_node", [](auto, auto& str) { return apply_dht_add_node(str); });
|
CMD2_ANY_STRING ("dht.add_node", [](auto, auto& str) { return apply_dht_add_node(str); });
|
||||||
CMD2_ANY ("dht.statistics", [](auto, auto) { return control->dht_manager()->dht_statistics(); });
|
CMD2_ANY ("dht.statistics", [](auto, auto) { return control->dht_manager()->dht_statistics(); });
|
||||||
|
|||||||
+4
-22
@@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <cerrno>
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <limits>
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <torrent/utils/log.h>
|
#include <torrent/utils/log.h>
|
||||||
|
|
||||||
@@ -127,20 +125,11 @@ apply_value([[maybe_unused]] rpc::target_type target, const torrent::Object::lis
|
|||||||
if (args.front().is_value()) {
|
if (args.front().is_value()) {
|
||||||
val = args.front().as_value();
|
val = args.front().as_value();
|
||||||
} else {
|
} else {
|
||||||
int64_t base = args.size() > 1 ? args.back().is_value() ?
|
int base = args.size() > 1 ? args.back().is_value() ?
|
||||||
args.back().as_value() : strtoll(args.back().as_string().c_str(), NULL, 10) : 10;
|
args.back().as_value() : strtol(args.back().as_string().c_str(), NULL, 10) : 10;
|
||||||
|
|
||||||
if (base != 0 && (base < 2 || base > 36))
|
|
||||||
throw torrent::input_error("'value' base must be 0 or between 2 and 36!");
|
|
||||||
|
|
||||||
char* endptr = 0;
|
char* endptr = 0;
|
||||||
errno = 0;
|
|
||||||
|
|
||||||
val = strtoll(args.front().as_string().c_str(), &endptr, base);
|
val = strtoll(args.front().as_string().c_str(), &endptr, base);
|
||||||
|
|
||||||
if (errno == ERANGE)
|
|
||||||
throw torrent::input_error("Number out of range: " + args.front().as_string());
|
|
||||||
|
|
||||||
while (*endptr == ' ' || *endptr == '\n') ++endptr;
|
while (*endptr == ' ' || *endptr == '\n') ++endptr;
|
||||||
if (*endptr)
|
if (*endptr)
|
||||||
throw torrent::input_error("Junk at end of number: " + args.front().as_string());
|
throw torrent::input_error("Junk at end of number: " + args.front().as_string());
|
||||||
@@ -418,9 +407,8 @@ apply_to_time(const torrent::Object& rawArgs, int flags) {
|
|||||||
torrent::Object
|
torrent::Object
|
||||||
apply_to_elapsed_time(const torrent::Object& rawArgs) {
|
apply_to_elapsed_time(const torrent::Object& rawArgs) {
|
||||||
auto cached_seconds = torrent::this_thread::cached_seconds().count();
|
auto cached_seconds = torrent::this_thread::cached_seconds().count();
|
||||||
auto value = rawArgs.as_value();
|
|
||||||
|
|
||||||
uint64_t arg = value >= 0 && value <= cached_seconds ? cached_seconds - value : 0;
|
uint64_t arg = cached_seconds - rawArgs.as_value();
|
||||||
|
|
||||||
char buffer[48];
|
char buffer[48];
|
||||||
snprintf(buffer, 48, "%2d:%02d:%02d", (int)(arg / 3600), (int)((arg / 60) % 60), (int)(arg % 60));
|
snprintf(buffer, 48, "%2d:%02d:%02d", (int)(arg / 3600), (int)((arg / 60) % 60), (int)(arg % 60));
|
||||||
@@ -686,14 +674,9 @@ apply_math_basic(const char* name, Comp op, const torrent::Object::list_type& ar
|
|||||||
throw torrent::input_error(std::string(name) + ": Wrong argument type");
|
throw torrent::input_error(std::string(name) + ": Wrong argument type");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (divides && itr != args.begin()) {
|
if (divides && !rhs && itr != args.begin())
|
||||||
if (rhs == 0)
|
|
||||||
throw torrent::input_error(std::string(name) + ": Division by zero!");
|
throw torrent::input_error(std::string(name) + ": Division by zero!");
|
||||||
|
|
||||||
if (val == std::numeric_limits<int64_t>::min() && rhs == -1)
|
|
||||||
throw torrent::input_error(std::string(name) + ": Division overflow!");
|
|
||||||
}
|
|
||||||
|
|
||||||
val = itr == args.begin() ? rhs : op(val, rhs);
|
val = itr == args.begin() ? rhs : op(val, rhs);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -933,7 +916,6 @@ initialize_command_ui() {
|
|||||||
|
|
||||||
rpc::rpc.mark_safe("view.set_visible");
|
rpc::rpc.mark_safe("view.set_visible");
|
||||||
rpc::rpc.mark_safe("view.set_not_visible");
|
rpc::rpc.mark_safe("view.set_not_visible");
|
||||||
rpc::rpc.mark_safe("view.list");
|
|
||||||
|
|
||||||
rpc::rpc.mark_safe("cat");
|
rpc::rpc.mark_safe("cat");
|
||||||
rpc::rpc.mark_safe("if");
|
rpc::rpc.mark_safe("if");
|
||||||
|
|||||||
+3
-5
@@ -6,7 +6,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <torrent/torrent.h>
|
#include <torrent/torrent.h>
|
||||||
#include <torrent/system/scheduler.h>
|
#include <torrent/utils/scheduler.h>
|
||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
class Root;
|
class Root;
|
||||||
@@ -107,8 +107,8 @@ private:
|
|||||||
mode_t m_umask;
|
mode_t m_umask;
|
||||||
std::string m_workingDirectory;
|
std::string m_workingDirectory;
|
||||||
|
|
||||||
torrent::system::SchedulerEntry m_task_shutdown;
|
torrent::utils::SchedulerEntry m_task_shutdown;
|
||||||
torrent::system::SchedulerEntry m_task_shutdown_clear_requests;
|
torrent::utils::SchedulerEntry m_task_shutdown_clear_requests;
|
||||||
|
|
||||||
int m_clear_requests_count{};
|
int m_clear_requests_count{};
|
||||||
|
|
||||||
@@ -118,6 +118,4 @@ private:
|
|||||||
std::atomic<bool> m_shutdown_quick{};
|
std::atomic<bool> m_shutdown_quick{};
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Control* control;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+2
-11
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <torrent/exceptions.h>
|
|
||||||
#include <torrent/object.h>
|
#include <torrent/object.h>
|
||||||
#include <torrent/object_stream.h>
|
#include <torrent/object_stream.h>
|
||||||
#include <torrent/rate.h>
|
#include <torrent/rate.h>
|
||||||
@@ -63,15 +62,8 @@ DhtManager::load_dht_cache() {
|
|||||||
LT_LOG("could not open cache file (path:%s)", cache_filename.c_str());
|
LT_LOG("could not open cache file (path:%s)", cache_filename.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
torrent::runtime::network_manager()->dht_controller()->initialize(cache);
|
torrent::runtime::network_manager()->dht_controller()->initialize(cache);
|
||||||
|
|
||||||
} catch (const torrent::base_error& e) {
|
|
||||||
LT_LOG_ERROR("cache file rejected, discarding (path:%s errmsg:%s)", cache_filename.c_str(), e.what());
|
|
||||||
|
|
||||||
torrent::runtime::network_manager()->dht_controller()->initialize(torrent::Object::create_map());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_start == dht_on)
|
if (m_start == dht_on)
|
||||||
start_dht();
|
start_dht();
|
||||||
}
|
}
|
||||||
@@ -140,12 +132,11 @@ DhtManager::save_dht_cache() {
|
|||||||
torrent::Object cache = torrent::Object::create_map();
|
torrent::Object cache = torrent::Object::create_map();
|
||||||
cache_file << *torrent::runtime::network_manager()->dht_controller()->store_cache(&cache);
|
cache_file << *torrent::runtime::network_manager()->dht_controller()->store_cache(&cache);
|
||||||
|
|
||||||
// The data only reaches the kernel here, so this is where a full disk is seen.
|
|
||||||
cache_file.close();
|
|
||||||
|
|
||||||
if (!cache_file.good())
|
if (!cache_file.good())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
cache_file.close();
|
||||||
|
|
||||||
::rename(filename_tmp.c_str(), filename.c_str());
|
::rename(filename_tmp.c_str(), filename.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#define RTORRENT_CORE_DHT_MANAGER_H
|
#define RTORRENT_CORE_DHT_MANAGER_H
|
||||||
|
|
||||||
#include <torrent/object.h>
|
#include <torrent/object.h>
|
||||||
#include <torrent/system/scheduler.h>
|
#include <torrent/utils/scheduler.h>
|
||||||
|
|
||||||
namespace core {
|
namespace core {
|
||||||
|
|
||||||
@@ -42,8 +42,8 @@ private:
|
|||||||
uint64_t m_dhtPrevBytesUp;
|
uint64_t m_dhtPrevBytesUp;
|
||||||
uint64_t m_dhtPrevBytesDown;
|
uint64_t m_dhtPrevBytesDown;
|
||||||
|
|
||||||
torrent::system::SchedulerEntry m_update_timeout;
|
torrent::utils::SchedulerEntry m_update_timeout;
|
||||||
torrent::system::SchedulerEntry m_stop_timeout;
|
torrent::utils::SchedulerEntry m_stop_timeout;
|
||||||
|
|
||||||
bool m_warned{};
|
bool m_warned{};
|
||||||
bool m_set_by_user{};
|
bool m_set_by_user{};
|
||||||
|
|||||||
+3
-13
@@ -101,18 +101,7 @@ Download::set_throttle_name(const std::string& name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Download::set_directory(const std::string& path) {
|
Download::set_root_directory(const std::string& path) {
|
||||||
if (!m_download.file_list()->is_multi_file())
|
|
||||||
return set_base_directory(path);
|
|
||||||
|
|
||||||
if (path.empty() || *path.rbegin() == '/')
|
|
||||||
return set_base_directory(path + m_download.info()->name_sanitized());
|
|
||||||
|
|
||||||
set_base_directory(path + "/" + m_download.info()->name_sanitized());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Download::set_base_directory(const std::string& path) {
|
|
||||||
// If the download is open, hashed and has completed chunks make
|
// If the download is open, hashed and has completed chunks make
|
||||||
// sure to verify that the download files are still present.
|
// sure to verify that the download files are still present.
|
||||||
//
|
//
|
||||||
@@ -131,12 +120,13 @@ Download::set_base_directory(const std::string& path) {
|
|||||||
!file_stat.update(file_list->front()->frozen_path().str()))) {
|
!file_stat.update(file_list->front()->frozen_path().str()))) {
|
||||||
|
|
||||||
set_message("Cannot change the directory of an open download after the files have been moved.");
|
set_message("Cannot change the directory of an open download after the files have been moved.");
|
||||||
|
rpc::call_command("d.state.set", (int64_t)0, rpc::make_target(this));
|
||||||
control->core()->download_list()->close_directly(this);
|
control->core()->download_list()->close_directly(this);
|
||||||
|
|
||||||
throw torrent::input_error("Cannot change the directory of an open download after the files have been moved.");
|
throw torrent::input_error("Cannot change the directory of an open download after the files have been moved.");
|
||||||
}
|
}
|
||||||
|
|
||||||
control->core()->download_list()->close_files(this);
|
control->core()->download_list()->close_directly(this);
|
||||||
file_list->set_root_dir(expand_path(path));
|
file_list->set_root_dir(expand_path(path));
|
||||||
|
|
||||||
bencode()->get_key("rtorrent").insert_key("directory", path);
|
bencode()->get_key("rtorrent").insert_key("directory", path);
|
||||||
|
|||||||
+1
-13
@@ -1,7 +1,6 @@
|
|||||||
#ifndef RTORRENT_CORE_DOWNLOAD_H
|
#ifndef RTORRENT_CORE_DOWNLOAD_H
|
||||||
#define RTORRENT_CORE_DOWNLOAD_H
|
#define RTORRENT_CORE_DOWNLOAD_H
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <torrent/common.h>
|
#include <torrent/common.h>
|
||||||
#include <torrent/download.h>
|
#include <torrent/download.h>
|
||||||
#include <torrent/download_info.h>
|
#include <torrent/download_info.h>
|
||||||
@@ -51,14 +50,6 @@ public:
|
|||||||
bool is_hash_checking() const { return m_download.is_hash_checking(); }
|
bool is_hash_checking() const { return m_download.is_hash_checking(); }
|
||||||
|
|
||||||
bool is_hash_failed() const { return m_hashFailed; }
|
bool is_hash_failed() const { return m_hashFailed; }
|
||||||
|
|
||||||
// Expires once the download is erased, even if other owners keep the
|
|
||||||
// object alive. Take it before triggering events that may erase.
|
|
||||||
std::weak_ptr<void> lifetime() const { return m_lifetime; }
|
|
||||||
void release_lifetime() { m_lifetime.reset(); }
|
|
||||||
|
|
||||||
bool is_erasing() const { return m_erasing; }
|
|
||||||
void set_erasing() { m_erasing = true; }
|
|
||||||
void set_hash_failed(bool v) { m_hashFailed = v; }
|
void set_hash_failed(bool v) { m_hashFailed = v; }
|
||||||
|
|
||||||
download_type* download() { return &m_download; }
|
download_type* download() { return &m_download; }
|
||||||
@@ -87,8 +78,7 @@ public:
|
|||||||
uint32_t resume_flags() { return m_resumeFlags; }
|
uint32_t resume_flags() { return m_resumeFlags; }
|
||||||
void set_resume_flags(uint32_t flags) { m_resumeFlags = flags; }
|
void set_resume_flags(uint32_t flags) { m_resumeFlags = flags; }
|
||||||
|
|
||||||
void set_directory(const std::string& path);
|
void set_root_directory(const std::string& path);
|
||||||
void set_base_directory(const std::string& path);
|
|
||||||
|
|
||||||
void set_throttle_name(const std::string& name);
|
void set_throttle_name(const std::string& name);
|
||||||
|
|
||||||
@@ -111,8 +101,6 @@ private:
|
|||||||
// Store the FileList instance so we can use slots etc on it.
|
// Store the FileList instance so we can use slots etc on it.
|
||||||
download_type m_download;
|
download_type m_download;
|
||||||
bool m_hashFailed{};
|
bool m_hashFailed{};
|
||||||
bool m_erasing{};
|
|
||||||
std::shared_ptr<void> m_lifetime{std::make_shared<char>()};
|
|
||||||
std::string m_message;
|
std::string m_message;
|
||||||
uint32_t m_resumeFlags{default_resume_flags};
|
uint32_t m_resumeFlags{default_resume_flags};
|
||||||
unsigned int m_group{};
|
unsigned int m_group{};
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <torrent/utils/log.h>
|
#include <torrent/utils/log.h>
|
||||||
#include <torrent/utils/resume.h>
|
#include <torrent/utils/resume.h>
|
||||||
#include <torrent/object.h>
|
#include <torrent/object.h>
|
||||||
@@ -16,7 +15,6 @@
|
|||||||
#include <torrent/rate.h>
|
#include <torrent/rate.h>
|
||||||
#include <torrent/data/file_utils.h>
|
#include <torrent/data/file_utils.h>
|
||||||
#include <torrent/net/http_stack.h>
|
#include <torrent/net/http_stack.h>
|
||||||
#include <torrent/runtime/client_config.h>
|
|
||||||
#include <torrent/utils/string_manip.h>
|
#include <torrent/utils/string_manip.h>
|
||||||
|
|
||||||
#include "control.h"
|
#include "control.h"
|
||||||
@@ -36,15 +34,8 @@ is_network_uri(const std::string& uri) {
|
|||||||
std::strncmp(uri.c_str(), "ftp://", 6) == 0;
|
std::strncmp(uri.c_str(), "ftp://", 6) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr const char* session_invalid_message = "Session data is invalid, ignoring it";
|
|
||||||
|
|
||||||
static std::unique_ptr<torrent::Object>
|
static std::unique_ptr<torrent::Object>
|
||||||
download_factory_load_stream(const char* filename, bool* is_invalid) {
|
download_factory_load_stream(const char* filename) {
|
||||||
struct stat sb;
|
|
||||||
|
|
||||||
if (stat(filename, &sb) != 0 || !S_ISREG(sb.st_mode))
|
|
||||||
return std::unique_ptr<torrent::Object>();
|
|
||||||
|
|
||||||
std::fstream stream(filename, std::ios::in | std::ios::binary);
|
std::fstream stream(filename, std::ios::in | std::ios::binary);
|
||||||
|
|
||||||
if (!stream.is_open())
|
if (!stream.is_open())
|
||||||
@@ -53,10 +44,8 @@ download_factory_load_stream(const char* filename, bool* is_invalid) {
|
|||||||
auto obj = std::make_unique<torrent::Object>();
|
auto obj = std::make_unique<torrent::Object>();
|
||||||
stream >> *obj;
|
stream >> *obj;
|
||||||
|
|
||||||
if (!stream.good() || !obj->is_map()) {
|
if (!stream.good())
|
||||||
*is_invalid = true;
|
|
||||||
return std::unique_ptr<torrent::Object>();
|
return std::unique_ptr<torrent::Object>();
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
@@ -171,21 +160,8 @@ DownloadFactory::receive_commit() {
|
|||||||
|
|
||||||
void
|
void
|
||||||
DownloadFactory::receive_success() {
|
DownloadFactory::receive_success() {
|
||||||
bool session_invalid = false;
|
auto rtorrent_object = download_factory_load_stream((expand_path(m_uri) + ".rtorrent").c_str());
|
||||||
|
auto libtorrent_resume_object = download_factory_load_stream((expand_path(m_uri) + ".libtorrent_resume").c_str());
|
||||||
std::unique_ptr<torrent::Object> rtorrent_object;
|
|
||||||
std::unique_ptr<torrent::Object> libtorrent_resume_object;
|
|
||||||
|
|
||||||
if (m_session) {
|
|
||||||
if (m_uri.empty())
|
|
||||||
throw torrent::input_error("Session torrent URI is empty.");
|
|
||||||
|
|
||||||
rtorrent_object = download_factory_load_stream((expand_path(m_uri) + ".rtorrent").c_str(), &session_invalid);
|
|
||||||
libtorrent_resume_object = download_factory_load_stream((expand_path(m_uri) + ".libtorrent_resume").c_str(), &session_invalid);
|
|
||||||
|
|
||||||
if (session_invalid)
|
|
||||||
lt_log_print(torrent::LOG_ERROR, "%s: %s", session_invalid_message, m_uri.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t tracker_key;
|
uint32_t tracker_key;
|
||||||
|
|
||||||
@@ -207,14 +183,6 @@ DownloadFactory::receive_success() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session_invalid) {
|
|
||||||
download->set_hash_failed(true);
|
|
||||||
download->set_message(session_invalid_message);
|
|
||||||
|
|
||||||
if (m_printLog)
|
|
||||||
m_manager->push_log_std(std::string(session_invalid_message) + ": \"" + m_uri + "\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object* root = download->bencode();
|
torrent::Object* root = download->bencode();
|
||||||
|
|
||||||
if (download->download()->info()->is_meta_download()) {
|
if (download->download()->info()->is_meta_download()) {
|
||||||
@@ -303,25 +271,12 @@ DownloadFactory::receive_success() {
|
|||||||
if (!m_session && m_variables["tied_to_file"].as_value())
|
if (!m_session && m_variables["tied_to_file"].as_value())
|
||||||
rpc::call_command("d.tied_to_file.set", m_uri.empty() ? m_variables["tied_file"] : m_uri, rpc::make_target(download));
|
rpc::call_command("d.tied_to_file.set", m_uri.empty() ? m_variables["tied_file"] : m_uri, rpc::make_target(download));
|
||||||
|
|
||||||
rpc::call_command("d.peer_exchange.set", torrent::runtime::client_config()->is_pex_enabled(), rpc::make_target(download));
|
rpc::call_command("d.peer_exchange.set", rpc::call_command_value("protocol.pex"), rpc::make_target(download));
|
||||||
|
|
||||||
try {
|
|
||||||
torrent::resume_load_addresses(*download->download(), resumeObject);
|
torrent::resume_load_addresses(*download->download(), resumeObject);
|
||||||
torrent::resume_load_file_priorities(*download->download(), resumeObject);
|
torrent::resume_load_file_priorities(*download->download(), resumeObject);
|
||||||
torrent::resume_load_tracker_settings(*download->download(), resumeObject);
|
torrent::resume_load_tracker_settings(*download->download(), resumeObject);
|
||||||
|
|
||||||
} catch (const torrent::input_error& e) {
|
|
||||||
std::string msg = std::string(session_invalid_message) + ": " + e.what();
|
|
||||||
|
|
||||||
lt_log_print(torrent::LOG_ERROR, "%s: %s", msg.c_str(), m_uri.c_str());
|
|
||||||
|
|
||||||
if (m_printLog)
|
|
||||||
m_manager->push_log_std(msg + ": \"" + m_uri + "\"");
|
|
||||||
|
|
||||||
download->set_hash_failed(true);
|
|
||||||
download->set_message(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The action of inserting might cause the torrent to be
|
// The action of inserting might cause the torrent to be
|
||||||
// opened/started or such. Figure out a nicer way of handling this.
|
// opened/started or such. Figure out a nicer way of handling this.
|
||||||
if (m_manager->download_list()->insert(download) == m_manager->download_list()->end()) {
|
if (m_manager->download_list()->insert(download) == m_manager->download_list()->end()) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
|
|
||||||
#include <torrent/object.h>
|
#include <torrent/object.h>
|
||||||
#include <torrent/system/scheduler.h>
|
#include <torrent/utils/scheduler.h>
|
||||||
|
|
||||||
#include "http_queue.h"
|
#include "http_queue.h"
|
||||||
|
|
||||||
@@ -80,8 +80,8 @@ private:
|
|||||||
torrent::Object::map_type m_variables;
|
torrent::Object::map_type m_variables;
|
||||||
|
|
||||||
slot_void m_slot_finished;
|
slot_void m_slot_finished;
|
||||||
torrent::system::SchedulerEntry m_task_load;
|
torrent::utils::SchedulerEntry m_task_load;
|
||||||
torrent::system::SchedulerEntry m_task_commit;
|
torrent::utils::SchedulerEntry m_task_commit;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool is_network_uri(const std::string& uri);
|
bool is_network_uri(const std::string& uri);
|
||||||
|
|||||||
+31
-116
@@ -37,7 +37,7 @@ namespace core {
|
|||||||
inline void
|
inline void
|
||||||
DownloadList::check_contains([[maybe_unused]] Download* d) {
|
DownloadList::check_contains([[maybe_unused]] Download* d) {
|
||||||
#ifdef USE_EXTRA_DEBUG
|
#ifdef USE_EXTRA_DEBUG
|
||||||
if (std::find_if(begin(), end(), [d](const auto& entry) { return entry.get() == d; }) == end())
|
if (std::find(begin(), end(), d) == end())
|
||||||
throw torrent::internal_error("DownloadList::check_contains(...) failed.");
|
throw torrent::internal_error("DownloadList::check_contains(...) failed.");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -54,6 +54,7 @@ DownloadList::clear() {
|
|||||||
base_type::pop_back();
|
base_type::pop_back();
|
||||||
|
|
||||||
torrent::download_remove(*download->download());
|
torrent::download_remove(*download->download());
|
||||||
|
delete download;
|
||||||
|
|
||||||
} catch (torrent::internal_error& e) {
|
} catch (torrent::internal_error& e) {
|
||||||
lt_log_print(torrent::LOG_ERROR, "DownloadList::clear() failed to close or remove download: %s", e.what());
|
lt_log_print(torrent::LOG_ERROR, "DownloadList::clear() failed to close or remove download: %s", e.what());
|
||||||
@@ -69,7 +70,7 @@ DownloadList::clear() {
|
|||||||
void
|
void
|
||||||
DownloadList::session_save() {
|
DownloadList::session_save() {
|
||||||
for (auto& download : *this)
|
for (auto& download : *this)
|
||||||
session_thread::manager()->save_resume_download(download.get());
|
session_thread::manager()->save_resume_download(download);
|
||||||
|
|
||||||
control->dht_manager()->save_dht_cache();
|
control->dht_manager()->save_dht_cache();
|
||||||
control->ui()->save_input_history();
|
control->ui()->save_input_history();
|
||||||
@@ -77,12 +78,12 @@ DownloadList::session_save() {
|
|||||||
|
|
||||||
DownloadList::iterator
|
DownloadList::iterator
|
||||||
DownloadList::find(const torrent::HashString& hash) {
|
DownloadList::find(const torrent::HashString& hash) {
|
||||||
return std::find_if(begin(), end(), [hash](const auto& d) { return hash == d->info()->hash(); });
|
return std::find_if(begin(), end(), [hash](Download* d) { return hash == d->info()->hash(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadList::iterator
|
DownloadList::iterator
|
||||||
DownloadList::find_hex(const char* hash) {
|
DownloadList::find_hex(const char* hash) {
|
||||||
if (strlen(hash) != 40)
|
if (strlen(hash) < 40)
|
||||||
return end();
|
return end();
|
||||||
|
|
||||||
torrent::HashString key;
|
torrent::HashString key;
|
||||||
@@ -90,14 +91,14 @@ DownloadList::find_hex(const char* hash) {
|
|||||||
if (torrent::utils::transform_from_hex(hash, hash + 40, key) != key.end())
|
if (torrent::utils::transform_from_hex(hash, hash + 40, key) != key.end())
|
||||||
return end();
|
return end();
|
||||||
|
|
||||||
return std::find_if(begin(), end(), [key](const auto& d) { return key == d->info()->hash(); });
|
return std::find_if(begin(), end(), [key](Download* d) { return key == d->info()->hash(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
Download*
|
Download*
|
||||||
DownloadList::find_hex_ptr(const char* hash) {
|
DownloadList::find_hex_ptr(const char* hash) {
|
||||||
iterator itr = find_hex(hash);
|
iterator itr = find_hex(hash);
|
||||||
|
|
||||||
return itr != end() ? itr->get() : NULL;
|
return itr != end() ? *itr : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Download*
|
Download*
|
||||||
@@ -158,7 +159,7 @@ DownloadList::create(std::istream* str, uint32_t tracker_key, bool printLog) {
|
|||||||
|
|
||||||
DownloadList::iterator
|
DownloadList::iterator
|
||||||
DownloadList::insert(Download* download) {
|
DownloadList::insert(Download* download) {
|
||||||
iterator itr = base_type::insert(end(), std::shared_ptr<Download>(download));
|
iterator itr = base_type::insert(end(), download);
|
||||||
|
|
||||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Inserting download.");
|
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Inserting download.");
|
||||||
|
|
||||||
@@ -169,7 +170,7 @@ DownloadList::insert(Download* download) {
|
|||||||
// This needs to be separated into two different calls to ensure
|
// This needs to be separated into two different calls to ensure
|
||||||
// the download remains in the view.
|
// the download remains in the view.
|
||||||
for (auto v : *control->view_manager())
|
for (auto v : *control->view_manager())
|
||||||
v->insert(*itr);
|
v->insert(download);
|
||||||
for (auto v : *control->view_manager())
|
for (auto v : *control->view_manager())
|
||||||
v->filter_download(download);
|
v->filter_download(download);
|
||||||
|
|
||||||
@@ -186,7 +187,7 @@ DownloadList::insert(Download* download) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
DownloadList::erase_ptr(Download* download) {
|
DownloadList::erase_ptr(Download* download) {
|
||||||
erase(std::find_if(begin(), end(), [download](const auto& entry) { return entry.get() == download; }));
|
erase(std::find(begin(), end(), download));
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadList::iterator
|
DownloadList::iterator
|
||||||
@@ -194,27 +195,21 @@ DownloadList::erase(iterator itr) {
|
|||||||
if (itr == end())
|
if (itr == end())
|
||||||
throw torrent::internal_error("DownloadList::erase(...) could not find download.");
|
throw torrent::internal_error("DownloadList::erase(...) could not find download.");
|
||||||
|
|
||||||
// An event handler below may erase the same download again.
|
|
||||||
if ((*itr)->is_erasing())
|
|
||||||
return std::next(itr);
|
|
||||||
|
|
||||||
(*itr)->set_erasing();
|
|
||||||
|
|
||||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, (*itr)->info(), "download_list", "Erasing download.");
|
lt_log_print_info(torrent::LOG_TORRENT_INFO, (*itr)->info(), "download_list", "Erasing download.");
|
||||||
|
|
||||||
// Makes sure close doesn't restart hashing of this download.
|
// Makes sure close doesn't restart hashing of this download.
|
||||||
(*itr)->set_hash_failed(true);
|
(*itr)->set_hash_failed(true);
|
||||||
|
|
||||||
close(*itr);
|
close(*itr);
|
||||||
session_thread::manager()->remove_download(itr->get());
|
session_thread::manager()->remove_download(*itr);
|
||||||
|
|
||||||
DL_TRIGGER_EVENT(*itr, "event.download.erased");
|
DL_TRIGGER_EVENT(*itr, "event.download.erased");
|
||||||
|
|
||||||
for (auto v : *control->view_manager())
|
for (auto v : *control->view_manager())
|
||||||
v->erase(itr->get());
|
v->erase(*itr);
|
||||||
|
|
||||||
(*itr)->release_lifetime();
|
|
||||||
torrent::download_remove(*(*itr)->download());
|
torrent::download_remove(*(*itr)->download());
|
||||||
|
delete *itr;
|
||||||
|
|
||||||
return base_type::erase(itr);
|
return base_type::erase(itr);
|
||||||
}
|
}
|
||||||
@@ -261,11 +256,9 @@ DownloadList::close(Download* download) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Releases the files without changing the download's state, for callers that
|
|
||||||
// need the files closed and will keep using the download.
|
|
||||||
void
|
void
|
||||||
DownloadList::close_files(Download* download) {
|
DownloadList::close_directly(Download* download) {
|
||||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download files.");
|
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download directly.");
|
||||||
|
|
||||||
if (download->download()->info()->is_active()) {
|
if (download->download()->info()->is_active()) {
|
||||||
download->download()->stop(torrent::Download::stop_skip_tracker);
|
download->download()->stop(torrent::Download::stop_skip_tracker);
|
||||||
@@ -278,53 +271,6 @@ DownloadList::close_files(Download* download) {
|
|||||||
download->download()->close();
|
download->download()->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
DownloadList::close_directly(Download* download) {
|
|
||||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download directly.");
|
|
||||||
|
|
||||||
auto lifetime = download->lifetime();
|
|
||||||
bool was_active = download->download()->info()->is_active();
|
|
||||||
bool was_open = download->download()->info()->is_open();
|
|
||||||
|
|
||||||
close_files(download);
|
|
||||||
set_state_stopped(download);
|
|
||||||
|
|
||||||
if (was_active) {
|
|
||||||
DL_TRIGGER_EVENT(download, "event.download.paused");
|
|
||||||
|
|
||||||
if (lifetime.expired())
|
|
||||||
return;
|
|
||||||
|
|
||||||
update_paused_state(download);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (was_open) {
|
|
||||||
DL_TRIGGER_EVENT(download, "event.download.hash_removed");
|
|
||||||
|
|
||||||
if (lifetime.expired())
|
|
||||||
return;
|
|
||||||
|
|
||||||
DL_TRIGGER_EVENT(download, "event.download.closed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
DownloadList::set_state_stopped(Download* download) {
|
|
||||||
control->view_manager()->find_ptr_throw("stopped")->set_visible(download);
|
|
||||||
rpc::call_command("d.state.set", (int64_t)0, rpc::make_target(download));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
DownloadList::update_paused_state(Download* download) {
|
|
||||||
rpc::call_command("d.state_changed.set", torrent::this_thread::cached_seconds().count(), rpc::make_target(download));
|
|
||||||
rpc::call_command("d.state_counter.set", rpc::call_command_value("d.state_counter", rpc::make_target(download)) + 1, rpc::make_target(download));
|
|
||||||
|
|
||||||
// If initial seeding is complete, don't try it again when restarting.
|
|
||||||
if (download->is_done() &&
|
|
||||||
rpc::call_command("d.connection_current", torrent::Object(), rpc::make_target(download)).as_string() == "initial_seed")
|
|
||||||
rpc::call_command("d.connection_seed.set", rpc::call_command("d.connection_current", torrent::Object(), rpc::make_target(download)), rpc::make_target(download));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
DownloadList::close_quick(Download* download) {
|
DownloadList::close_quick(Download* download) {
|
||||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download quickly.");
|
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Closing download quickly.");
|
||||||
@@ -346,13 +292,8 @@ DownloadList::close_throw(Download* download) {
|
|||||||
// When pause gets called it will clear the initial hash check state
|
// When pause gets called it will clear the initial hash check state
|
||||||
// and set hash failed. This should ensure hashing doesn't restart
|
// and set hash failed. This should ensure hashing doesn't restart
|
||||||
// until resume gets called.
|
// until resume gets called.
|
||||||
auto lifetime = download->lifetime();
|
|
||||||
|
|
||||||
pause(download);
|
pause(download);
|
||||||
|
|
||||||
if (lifetime.expired())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Check for is_open after pause due to hashing.
|
// Check for is_open after pause due to hashing.
|
||||||
if (!download->is_open())
|
if (!download->is_open())
|
||||||
return;
|
return;
|
||||||
@@ -372,10 +313,6 @@ DownloadList::close_throw(Download* download) {
|
|||||||
throw torrent::internal_error("DownloadList::close_throw(...) called but we're going into a hashing loop.");
|
throw torrent::internal_error("DownloadList::close_throw(...) called but we're going into a hashing loop.");
|
||||||
|
|
||||||
DL_TRIGGER_EVENT(download, "event.download.hash_removed");
|
DL_TRIGGER_EVENT(download, "event.download.hash_removed");
|
||||||
|
|
||||||
if (lifetime.expired())
|
|
||||||
return;
|
|
||||||
|
|
||||||
DL_TRIGGER_EVENT(download, "event.download.closed");
|
DL_TRIGGER_EVENT(download, "event.download.closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,8 +419,6 @@ DownloadList::pause(Download* download, int flags) {
|
|||||||
|
|
||||||
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Pausing download: flags:%0x.", flags);
|
lt_log_print_info(torrent::LOG_TORRENT_INFO, download->info(), "download_list", "Pausing download: flags:%0x.", flags);
|
||||||
|
|
||||||
auto lifetime = download->lifetime();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
download->set_resume_flags(Download::default_resume_flags);
|
download->set_resume_flags(Download::default_resume_flags);
|
||||||
@@ -497,9 +432,6 @@ DownloadList::pause(Download* download, int flags) {
|
|||||||
rpc::call_command_set_value("d.hashing.set", Download::variable_hashing_stopped, rpc::make_target(download));
|
rpc::call_command_set_value("d.hashing.set", Download::variable_hashing_stopped, rpc::make_target(download));
|
||||||
|
|
||||||
DL_TRIGGER_EVENT(download, "event.download.hash_removed");
|
DL_TRIGGER_EVENT(download, "event.download.hash_removed");
|
||||||
|
|
||||||
if (lifetime.expired())
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!download->download()->info()->is_active())
|
if (!download->download()->info()->is_active())
|
||||||
@@ -513,10 +445,15 @@ DownloadList::pause(Download* download, int flags) {
|
|||||||
// view.
|
// view.
|
||||||
DL_TRIGGER_EVENT(download, "event.download.paused");
|
DL_TRIGGER_EVENT(download, "event.download.paused");
|
||||||
|
|
||||||
if (lifetime.expired())
|
auto cached_seconds = torrent::this_thread::cached_seconds().count();
|
||||||
return;
|
|
||||||
|
|
||||||
update_paused_state(download);
|
rpc::call_command("d.state_changed.set", cached_seconds, rpc::make_target(download));
|
||||||
|
rpc::call_command("d.state_counter.set", rpc::call_command_value("d.state_counter", rpc::make_target(download)), rpc::make_target(download));
|
||||||
|
|
||||||
|
// If initial seeding is complete, don't try it again when restarting.
|
||||||
|
if (download->is_done() &&
|
||||||
|
rpc::call_command("d.connection_current", torrent::Object(), rpc::make_target(download)).as_string() == "initial_seed")
|
||||||
|
rpc::call_command("d.connection_seed.set", rpc::call_command("d.connection_current", torrent::Object(), rpc::make_target(download)), rpc::make_target(download));
|
||||||
|
|
||||||
// Save the state after all the slots, etc have been called so we
|
// Save the state after all the slots, etc have been called so we
|
||||||
// include the modifications they may make.
|
// include the modifications they may make.
|
||||||
@@ -587,7 +524,7 @@ DownloadList::hash_done(Download* download) {
|
|||||||
// If the download was previously completed but the files were
|
// If the download was previously completed but the files were
|
||||||
// f.ex deleted, then we clear the state and complete.
|
// f.ex deleted, then we clear the state and complete.
|
||||||
if (rpc::call_command_value("d.complete", rpc::make_target(download)) && !download->is_done()) {
|
if (rpc::call_command_value("d.complete", rpc::make_target(download)) && !download->is_done()) {
|
||||||
set_state_stopped(download);
|
rpc::call_command("d.state.set", (int64_t)0, rpc::make_target(download));
|
||||||
download->set_message("Download registered as completed, but hash check returned unfinished chunks.");
|
download->set_message("Download registered as completed, but hash check returned unfinished chunks.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -596,15 +533,9 @@ DownloadList::hash_done(Download* download) {
|
|||||||
rpc::call_command("d.complete.set", (int64_t)download->is_done(), rpc::make_target(download));
|
rpc::call_command("d.complete.set", (int64_t)download->is_done(), rpc::make_target(download));
|
||||||
torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"));
|
torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"));
|
||||||
|
|
||||||
if (rpc::call_command_value("d.state", rpc::make_target(download)) == 1) {
|
if (rpc::call_command_value("d.state", rpc::make_target(download)) == 1)
|
||||||
auto lifetime = download->lifetime();
|
|
||||||
|
|
||||||
resume(download, download->resume_flags());
|
resume(download, download->resume_flags());
|
||||||
|
|
||||||
if (lifetime.expired())
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Download::variable_hashing_last:
|
case Download::variable_hashing_last:
|
||||||
@@ -641,24 +572,11 @@ DownloadList::hash_queue(Download* download, int type) {
|
|||||||
|
|
||||||
// HACK
|
// HACK
|
||||||
if (download->is_open()) {
|
if (download->is_open()) {
|
||||||
auto lifetime = download->lifetime();
|
|
||||||
|
|
||||||
pause(download, torrent::Download::stop_skip_tracker);
|
pause(download, torrent::Download::stop_skip_tracker);
|
||||||
|
|
||||||
if (lifetime.expired())
|
|
||||||
return;
|
|
||||||
|
|
||||||
download->download()->close();
|
download->download()->close();
|
||||||
|
|
||||||
DL_TRIGGER_EVENT(download, "event.download.hash_removed");
|
DL_TRIGGER_EVENT(download, "event.download.hash_removed");
|
||||||
|
|
||||||
if (lifetime.expired())
|
|
||||||
return;
|
|
||||||
|
|
||||||
DL_TRIGGER_EVENT(download, "event.download.closed");
|
DL_TRIGGER_EVENT(download, "event.download.closed");
|
||||||
|
|
||||||
if (lifetime.expired())
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
torrent::resume_clear_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"));
|
torrent::resume_clear_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"));
|
||||||
@@ -709,10 +627,6 @@ DownloadList::confirm_finished(Download* download) {
|
|||||||
if (choke_up.is_string_empty()) choke_up = rpc::call_command("protocol.choke_heuristics.up.seed", torrent::Object(), rpc::make_target(download));
|
if (choke_up.is_string_empty()) choke_up = rpc::call_command("protocol.choke_heuristics.up.seed", torrent::Object(), rpc::make_target(download));
|
||||||
if (choke_down.is_string_empty()) choke_down = rpc::call_command("protocol.choke_heuristics.down.seed", torrent::Object(), rpc::make_target(download));
|
if (choke_down.is_string_empty()) choke_down = rpc::call_command("protocol.choke_heuristics.down.seed", torrent::Object(), rpc::make_target(download));
|
||||||
|
|
||||||
// libtorrent refuses initial seeding on an active download.
|
|
||||||
if (download->is_active() && conn_current.as_string() == "initial_seed")
|
|
||||||
conn_current = "seed";
|
|
||||||
|
|
||||||
rpc::call_command("d.connection_current.set", conn_current, rpc::make_target(download));
|
rpc::call_command("d.connection_current.set", conn_current, rpc::make_target(download));
|
||||||
rpc::call_command("d.up.choke_heuristics.set", choke_up, rpc::make_target(download));
|
rpc::call_command("d.up.choke_heuristics.set", choke_up, rpc::make_target(download));
|
||||||
rpc::call_command("d.down.choke_heuristics.set", choke_down, rpc::make_target(download));
|
rpc::call_command("d.down.choke_heuristics.set", choke_down, rpc::make_target(download));
|
||||||
@@ -738,12 +652,12 @@ DownloadList::confirm_finished(Download* download) {
|
|||||||
// up/downloaded baseline.
|
// up/downloaded baseline.
|
||||||
download->download()->send_completed();
|
download->download()->send_completed();
|
||||||
|
|
||||||
// The finished event may erase the download.
|
// Save the hash in case the finished event erases it.
|
||||||
auto lifetime = download->lifetime();
|
torrent::HashString infohash = download->info()->hash();
|
||||||
|
|
||||||
DL_TRIGGER_EVENT(download, "event.download.finished");
|
DL_TRIGGER_EVENT(download, "event.download.finished");
|
||||||
|
|
||||||
if (lifetime.expired())
|
if (find(infohash) == end())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// if (download->resume_flags() != Download::default_resume_flags)
|
// if (download->resume_flags() != Download::default_resume_flags)
|
||||||
@@ -782,10 +696,11 @@ DownloadList::process_meta_download(Download* download) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto bencode = std::make_unique<torrent::Object>(torrent::Object::create_map());
|
torrent::Object* bencode = new torrent::Object(torrent::Object::create_map());
|
||||||
file >> bencode->insert_key("info", torrent::Object());
|
file >> bencode->insert_key("info", torrent::Object());
|
||||||
|
|
||||||
if (file.fail()) {
|
if (file.fail()) {
|
||||||
|
delete bencode;
|
||||||
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download, the input is not a valid torrent.");
|
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not create download, the input is not a valid torrent.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -801,7 +716,7 @@ DownloadList::process_meta_download(Download* download) {
|
|||||||
|
|
||||||
erase_ptr(download);
|
erase_ptr(download);
|
||||||
|
|
||||||
control->core()->try_create_download_from_meta_download(std::move(bencode), metafile);
|
control->core()->try_create_download_from_meta_download(bencode, metafile);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace torrent {
|
namespace torrent {
|
||||||
@@ -21,9 +20,9 @@ class Download;
|
|||||||
//
|
//
|
||||||
// Fix apply_on_ratio if the base_type is changed.
|
// Fix apply_on_ratio if the base_type is changed.
|
||||||
|
|
||||||
class DownloadList : private std::list<std::shared_ptr<Download>> {
|
class DownloadList : private std::list<Download*> {
|
||||||
public:
|
public:
|
||||||
typedef std::list<std::shared_ptr<Download>> base_type;
|
typedef std::list<Download*> base_type;
|
||||||
|
|
||||||
using base_type::iterator;
|
using base_type::iterator;
|
||||||
using base_type::const_iterator;
|
using base_type::const_iterator;
|
||||||
@@ -65,17 +64,8 @@ public:
|
|||||||
bool open(Download* d);
|
bool open(Download* d);
|
||||||
void open_throw(Download* d);
|
void open_throw(Download* d);
|
||||||
|
|
||||||
// Overloads for the list's own entries, so callers iterating it do not need
|
|
||||||
// to unwrap.
|
|
||||||
bool open(const value_type& d) { return open(d.get()); }
|
|
||||||
void close(const value_type& d) { close(d.get()); }
|
|
||||||
void close_quick(const value_type& d) { close_quick(d.get()); }
|
|
||||||
void pause(const value_type& d) { pause(d.get()); }
|
|
||||||
void resume(const value_type& d) { resume(d.get()); }
|
|
||||||
|
|
||||||
void close(Download* d);
|
void close(Download* d);
|
||||||
void close_directly(Download* d);
|
void close_directly(Download* d);
|
||||||
void close_files(Download* d);
|
|
||||||
void close_quick(Download* d);
|
void close_quick(Download* d);
|
||||||
void close_throw(Download* d);
|
void close_throw(Download* d);
|
||||||
|
|
||||||
@@ -133,9 +123,6 @@ private:
|
|||||||
void hash_done(Download* d);
|
void hash_done(Download* d);
|
||||||
void hash_queue(Download* d, int type);
|
void hash_queue(Download* d, int type);
|
||||||
|
|
||||||
void set_state_stopped(Download* d);
|
|
||||||
void update_paused_state(Download* d);
|
|
||||||
|
|
||||||
inline void check_contains(Download* d);
|
inline void check_contains(Download* d);
|
||||||
|
|
||||||
void received_finished(Download* d);
|
void received_finished(Download* d);
|
||||||
|
|||||||
+7
-10
@@ -154,10 +154,10 @@ Manager::cleanup() {
|
|||||||
void
|
void
|
||||||
Manager::shutdown(bool force) {
|
Manager::shutdown(bool force) {
|
||||||
if (!force) {
|
if (!force) {
|
||||||
for (const auto& d : *m_download_list)
|
for (auto d : *m_download_list)
|
||||||
m_download_list->pause_default(d.get());
|
m_download_list->pause_default(d);
|
||||||
} else {
|
} else {
|
||||||
for (const auto& d : *m_download_list)
|
for (auto d : *m_download_list)
|
||||||
m_download_list->close_quick(d);
|
m_download_list->close_quick(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,14 +195,14 @@ Manager::try_create_download(const std::string& uri, int flags, const command_li
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Adding download.
|
// Adding download.
|
||||||
auto f = std::make_unique<DownloadFactory>(this);
|
DownloadFactory* f = new DownloadFactory(this);
|
||||||
|
|
||||||
f->variables()["tied_to_file"] = (int64_t)(bool)(flags & create_tied);
|
f->variables()["tied_to_file"] = (int64_t)(bool)(flags & create_tied);
|
||||||
f->commands().insert(f->commands().end(), commands.begin(), commands.end());
|
f->commands().insert(f->commands().end(), commands.begin(), commands.end());
|
||||||
|
|
||||||
f->set_start(flags & create_start);
|
f->set_start(flags & create_start);
|
||||||
f->set_print_log(!(flags & create_quiet));
|
f->set_print_log(!(flags & create_quiet));
|
||||||
f->slot_finished([factory = f.get()]() { delete factory; });
|
f->slot_finished([f]() { delete f; });
|
||||||
|
|
||||||
if (is_data_uri(uri)) {
|
if (is_data_uri(uri)) {
|
||||||
// Allow the use of data URIs, primarily for JSON-RPC which
|
// Allow the use of data URIs, primarily for JSON-RPC which
|
||||||
@@ -216,13 +216,10 @@ Manager::try_create_download(const std::string& uri, int flags, const command_li
|
|||||||
}
|
}
|
||||||
|
|
||||||
f->commit();
|
f->commit();
|
||||||
|
|
||||||
// From here the finished slot deletes it.
|
|
||||||
f.release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Manager::try_create_download_from_meta_download(std::unique_ptr<torrent::Object> bencode, const std::string& metafile) {
|
Manager::try_create_download_from_meta_download(torrent::Object* bencode, const std::string& metafile) {
|
||||||
DownloadFactory* f = new DownloadFactory(this);
|
DownloadFactory* f = new DownloadFactory(this);
|
||||||
|
|
||||||
f->variables()["tied_to_file"] = (int64_t)true;
|
f->variables()["tied_to_file"] = (int64_t)true;
|
||||||
@@ -408,7 +405,7 @@ Manager::receive_hashing_changed() {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
m_download_list->open_throw(itr->get());
|
m_download_list->open_throw(*itr);
|
||||||
|
|
||||||
// Since the bitfield is allocated on loading of resume load or
|
// Since the bitfield is allocated on loading of resume load or
|
||||||
// hash start, and unallocated on close, we know that if it it
|
// hash start, and unallocated on close, we know that if it it
|
||||||
|
|||||||
+2
-1
@@ -8,6 +8,7 @@
|
|||||||
#include <torrent/object.h>
|
#include <torrent/object.h>
|
||||||
|
|
||||||
#include "download_list.h"
|
#include "download_list.h"
|
||||||
|
#include "range_map.h"
|
||||||
|
|
||||||
namespace torrent {
|
namespace torrent {
|
||||||
class Bencode;
|
class Bencode;
|
||||||
@@ -75,7 +76,7 @@ public:
|
|||||||
// Temporary, find a better place for this.
|
// Temporary, find a better place for this.
|
||||||
void try_create_download(const std::string& uri, int flags, const command_list_type& commands);
|
void try_create_download(const std::string& uri, int flags, const command_list_type& commands);
|
||||||
void try_create_download_expand(const std::string& uri, int flags, command_list_type commands = command_list_type());
|
void try_create_download_expand(const std::string& uri, int flags, command_list_type commands = command_list_type());
|
||||||
void try_create_download_from_meta_download(std::unique_ptr<torrent::Object> bencode, const std::string& metafile);
|
void try_create_download_from_meta_download(torrent::Object* bencode, const std::string& metafile);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void create_http(const std::string& uri);
|
void create_http(const std::string& uri);
|
||||||
|
|||||||
@@ -0,0 +1,199 @@
|
|||||||
|
// 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 <sundell.software@gmail.com>
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef RTORRENT_CORE_RANGE_MAP_H
|
||||||
|
#define RTORRENT_CORE_RANGE_MAP_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
// Associate values with a range of keys, and retrieve for any key in the range.
|
||||||
|
|
||||||
|
// The template arguments have the same semantics as std::map.
|
||||||
|
// Exception: if set_merge is used, the value type must have a defined operator ==.
|
||||||
|
template<typename Key, typename T, typename Compare = std::less<Key>,
|
||||||
|
typename Alloc = std::allocator<std::pair<const Key, T> > >
|
||||||
|
class RangeMap : private std::map<Key, std::pair<Key, T>, Compare,
|
||||||
|
typename std::allocator_traits<Alloc>::template rebind_alloc<std::pair<const Key, std::pair<Key, T>>>> {
|
||||||
|
|
||||||
|
typedef std::map<Key, std::pair<Key, T>, Compare,
|
||||||
|
typename std::allocator_traits<Alloc>::template rebind_alloc<std::pair<const Key, std::pair<Key, T>>>> base_type;
|
||||||
|
|
||||||
|
//std::allocator_traits<Alloc>::template rebind_alloc<std::pair<const Key, std::pair<Key, T>>>
|
||||||
|
|
||||||
|
public:
|
||||||
|
RangeMap() = default;
|
||||||
|
RangeMap(const Compare& c) : base_type(c) {}
|
||||||
|
|
||||||
|
typedef typename base_type::iterator iterator;
|
||||||
|
typedef typename base_type::reverse_iterator reverse_iterator;
|
||||||
|
typedef typename base_type::const_iterator const_iterator;
|
||||||
|
typedef typename base_type::const_reverse_iterator const_reverse_iterator;
|
||||||
|
|
||||||
|
// using typename base_type::const_iterator;
|
||||||
|
// using typename base_type::const_reverse_iterator;
|
||||||
|
|
||||||
|
using base_type::clear;
|
||||||
|
using base_type::swap;
|
||||||
|
|
||||||
|
using base_type::size;
|
||||||
|
using base_type::empty;
|
||||||
|
|
||||||
|
using base_type::begin;
|
||||||
|
using base_type::end;
|
||||||
|
using base_type::rbegin;
|
||||||
|
using base_type::rend;
|
||||||
|
|
||||||
|
using base_type::key_comp;
|
||||||
|
using base_type::value_comp;
|
||||||
|
|
||||||
|
// Store a value for the range [begin, end). Returns iterator for the range.
|
||||||
|
const_iterator set_range(const Key& begin, const Key& end, const T& value);
|
||||||
|
|
||||||
|
// Same, but merge adjacent ranges having the same value. Returns iterator for the merged range.
|
||||||
|
const_iterator set_merge(Key begin, const Key& end, const T& value);
|
||||||
|
|
||||||
|
// Find range containing the given key, or end().
|
||||||
|
const_iterator find(const Key& key) const;
|
||||||
|
|
||||||
|
// Retrieve value for key in a range, throw std::out_of_range if range does not exist.
|
||||||
|
const T& get(const Key& key) const;
|
||||||
|
|
||||||
|
// Retrieve value for key in a range, return def if range does not exist.
|
||||||
|
T get(const Key& key, T def) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
iterator crop_overlap(const Key& begin, const Key& end);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Semantics of an entry:
|
||||||
|
// .first End of range (exclusive), map key.
|
||||||
|
// .second.first Beginning of range.
|
||||||
|
// .second.second Value.
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename C, typename A>
|
||||||
|
inline typename RangeMap<Key,T,C,A>::iterator
|
||||||
|
RangeMap<Key,T,C,A>::crop_overlap(const Key& _begin, const Key& _end) {
|
||||||
|
typename RangeMap::iterator itr = base_type::upper_bound(_begin);
|
||||||
|
|
||||||
|
while (itr != end() && key_comp()(itr->second.first, _end)) {
|
||||||
|
// There's a subrange before the new begin: need new entry (new range end means new key).
|
||||||
|
if (key_comp()(itr->second.first, _begin))
|
||||||
|
base_type::insert(itr, typename RangeMap::value_type(_begin, itr->second));
|
||||||
|
|
||||||
|
// Old end is within our range: erase entry.
|
||||||
|
if (!key_comp()(_end, itr->first)) {
|
||||||
|
base_type::erase(itr++);
|
||||||
|
|
||||||
|
// Otherwise simply set the new begin of the old range.
|
||||||
|
} else {
|
||||||
|
itr->second.first = _end;
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return itr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename C, typename A>
|
||||||
|
inline typename RangeMap<Key,T,C,A>::const_iterator
|
||||||
|
RangeMap<Key,T,C,A>::set_merge(Key _begin, const Key& _end, const T& value) {
|
||||||
|
if (!key_comp()(_begin, _end))
|
||||||
|
return end();
|
||||||
|
|
||||||
|
// Crop overlapping ranges and return iterator to first range after the one we're inserting.
|
||||||
|
typename RangeMap::iterator itr = crop_overlap(_begin, _end);
|
||||||
|
|
||||||
|
// Check if range before new one is adjacent and has same value: if so erase it and use its beginning.
|
||||||
|
if (itr != begin()) {
|
||||||
|
typename RangeMap::iterator prev = itr;
|
||||||
|
if (!key_comp()((--prev)->first, _begin) && prev->second.second == value) {
|
||||||
|
_begin = prev->second.first;
|
||||||
|
base_type::erase(prev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Range after new one is adjacent and has same value: set new beginning.
|
||||||
|
if (itr != end() && !key_comp()(_end, itr->second.first) && itr->second.second == value) {
|
||||||
|
itr->second.first = _begin;
|
||||||
|
return itr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, this range isn't mergeable, make new entry.
|
||||||
|
return base_type::insert(itr, typename RangeMap::value_type(_end, typename RangeMap::mapped_type(_begin, value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename C, typename A>
|
||||||
|
inline typename RangeMap<Key,T,C,A>::const_iterator
|
||||||
|
RangeMap<Key,T,C,A>::set_range(const Key& _begin, const Key& _end, const T& value) {
|
||||||
|
if (!key_comp()(_begin, _end))
|
||||||
|
return end();
|
||||||
|
|
||||||
|
return base_type::insert(crop_overlap(_begin, _end), typename RangeMap::value_type(_end, typename RangeMap::mapped_type(_begin, value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename C, typename A>
|
||||||
|
inline typename RangeMap<Key,T,C,A>::const_iterator
|
||||||
|
RangeMap<Key,T,C,A>::find(const Key& key) const {
|
||||||
|
typename RangeMap::const_iterator itr = base_type::upper_bound(key);
|
||||||
|
|
||||||
|
if (itr != end() && key_comp()(key, itr->second.first))
|
||||||
|
itr = end();
|
||||||
|
|
||||||
|
return itr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename C, typename A>
|
||||||
|
inline const T&
|
||||||
|
RangeMap<Key,T,C,A>::get(const Key& key) const {
|
||||||
|
typename RangeMap::const_iterator itr = find(key);
|
||||||
|
|
||||||
|
if (itr == end())
|
||||||
|
throw std::out_of_range("RangeMap::get");
|
||||||
|
|
||||||
|
return itr->second.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Key, typename T, typename C, typename A>
|
||||||
|
inline T
|
||||||
|
RangeMap<Key,T,C,A>::get(const Key& key, T def) const {
|
||||||
|
typename RangeMap::const_iterator itr = find(key);
|
||||||
|
return (itr == end() ? def : itr->second.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
+18
-42
@@ -16,20 +16,10 @@
|
|||||||
namespace core {
|
namespace core {
|
||||||
|
|
||||||
// Also add focus thingie here?
|
// Also add focus thingie here?
|
||||||
// Matches the list entry that owns a given download.
|
|
||||||
inline auto
|
|
||||||
entry_is(Download* download) {
|
|
||||||
return [download](const std::shared_ptr<Download>& entry) { return entry.get() == download; };
|
|
||||||
}
|
|
||||||
|
|
||||||
struct view_downloads_compare {
|
struct view_downloads_compare {
|
||||||
view_downloads_compare(const torrent::Object& cmd) :
|
view_downloads_compare(const torrent::Object& cmd) :
|
||||||
m_command(cmd) {}
|
m_command(cmd) {}
|
||||||
|
|
||||||
bool operator()(const std::shared_ptr<Download>& d1, const std::shared_ptr<Download>& d2) const {
|
|
||||||
return (*this)(d1.get(), d2.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator()(Download* d1, Download* d2) const {
|
bool operator()(Download* d1, Download* d2) const {
|
||||||
try {
|
try {
|
||||||
if (m_command.is_empty())
|
if (m_command.is_empty())
|
||||||
@@ -64,10 +54,6 @@ struct view_downloads_filter {
|
|||||||
view_downloads_filter(const torrent::Object& cmd, const torrent::Object& cmd2) :
|
view_downloads_filter(const torrent::Object& cmd, const torrent::Object& cmd2) :
|
||||||
m_command(cmd), m_command2(cmd2) {}
|
m_command(cmd), m_command2(cmd2) {}
|
||||||
|
|
||||||
bool operator()(const std::shared_ptr<Download>& d1) const {
|
|
||||||
return (*this)(d1.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator()(Download* d1) const {
|
bool operator()(Download* d1) const {
|
||||||
return this->evalCmd(m_command, d1) && this->evalCmd(m_command2, d1);
|
return this->evalCmd(m_command, d1) && this->evalCmd(m_command2, d1);
|
||||||
}
|
}
|
||||||
@@ -166,7 +152,7 @@ View::initialize(const std::string& name) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
View::erase(Download* download) {
|
View::erase(Download* download) {
|
||||||
iterator itr = std::find_if(base_type::begin(), base_type::end(), entry_is(download));
|
iterator itr = std::find(base_type::begin(), base_type::end(), download);
|
||||||
|
|
||||||
if (itr >= end_visible()) {
|
if (itr >= end_visible()) {
|
||||||
erase_internal(itr);
|
erase_internal(itr);
|
||||||
@@ -179,24 +165,22 @@ View::erase(Download* download) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
View::set_visible(Download* download) {
|
View::set_visible(Download* download) {
|
||||||
iterator itr = std::find_if(begin_filtered(), end_filtered(), entry_is(download));
|
iterator itr = std::find(begin_filtered(), end_filtered(), download);
|
||||||
|
|
||||||
if (itr == end_filtered())
|
if (itr == end_filtered())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Don't optimize erase since we want to keep the order of the
|
// Don't optimize erase since we want to keep the order of the
|
||||||
// non-visible elements.
|
// non-visible elements.
|
||||||
auto entry = *itr;
|
|
||||||
|
|
||||||
base_type::erase(itr);
|
base_type::erase(itr);
|
||||||
insert_visible(entry);
|
insert_visible(download);
|
||||||
|
|
||||||
rpc::call_object_nothrow(m_event_added, rpc::make_target(download));
|
rpc::call_object_nothrow(m_event_added, rpc::make_target(download));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
View::set_not_visible(Download* download) {
|
View::set_not_visible(Download* download) {
|
||||||
iterator itr = std::find_if(begin_visible(), end_visible(), entry_is(download));
|
iterator itr = std::find(begin_visible(), end_visible(), download);
|
||||||
|
|
||||||
if (itr == end_visible())
|
if (itr == end_visible())
|
||||||
return;
|
return;
|
||||||
@@ -206,17 +190,15 @@ View::set_not_visible(Download* download) {
|
|||||||
|
|
||||||
// Don't optimize erase since we want to keep the order of the
|
// Don't optimize erase since we want to keep the order of the
|
||||||
// non-visible elements.
|
// non-visible elements.
|
||||||
auto entry = *itr;
|
|
||||||
|
|
||||||
base_type::erase(itr);
|
base_type::erase(itr);
|
||||||
base_type::push_back(entry);
|
base_type::push_back(download);
|
||||||
|
|
||||||
rpc::call_object_nothrow(m_event_removed, rpc::make_target(download));
|
rpc::call_object_nothrow(m_event_removed, rpc::make_target(download));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
View::next_focus(unsigned int i) {
|
View::next_focus(unsigned int i) {
|
||||||
if (empty_visible())
|
if (empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If at the boundary, roll over
|
// If at the boundary, roll over
|
||||||
@@ -239,7 +221,7 @@ View::next_focus(unsigned int i) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
View::prev_focus(unsigned int i) {
|
View::prev_focus(unsigned int i) {
|
||||||
if (empty_visible())
|
if (empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If at the boundary, roll over
|
// If at the boundary, roll over
|
||||||
@@ -259,12 +241,12 @@ View::prev_focus(unsigned int i) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
View::sort() {
|
View::sort() {
|
||||||
Download* curFocus = focus() != end_visible() ? focus()->get() : NULL;
|
Download* curFocus = focus() != end_visible() ? *focus() : NULL;
|
||||||
|
|
||||||
// Don't go randomly switching around equivalent elements.
|
// Don't go randomly switching around equivalent elements.
|
||||||
std::stable_sort(begin(), end_visible(), view_downloads_compare(m_sortCurrent));
|
std::stable_sort(begin(), end_visible(), view_downloads_compare(m_sortCurrent));
|
||||||
|
|
||||||
m_focus = position(std::find_if(begin(), end_visible(), entry_is(curFocus)));
|
m_focus = position(std::find(begin(), end_visible(), curFocus));
|
||||||
emit_changed();
|
emit_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,10 +280,10 @@ View::filter() {
|
|||||||
// set the elements to NULL as we trigger commands on them. Or
|
// set the elements to NULL as we trigger commands on them. Or
|
||||||
// perhaps always clear them, thus not throwing anything.
|
// perhaps always clear them, thus not throwing anything.
|
||||||
if (!m_event_removed.is_empty())
|
if (!m_event_removed.is_empty())
|
||||||
std::for_each(changed.begin(), splitChanged, [this](const auto& d) { rpc::call_object_d_nothrow(m_event_removed, d.get()); });
|
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())
|
if (!m_event_added.is_empty())
|
||||||
std::for_each(splitChanged, changed.end(), [this](const auto& d) { rpc::call_object_d_nothrow(m_event_added, d.get()); });
|
std::for_each(changed.begin(), splitChanged, std::bind(&rpc::call_object_d_nothrow, m_event_added, std::placeholders::_1));
|
||||||
|
|
||||||
emit_changed();
|
emit_changed();
|
||||||
}
|
}
|
||||||
@@ -312,23 +294,21 @@ View::filter_by(const torrent::Object& condition, View::base_type& result) {
|
|||||||
view_downloads_filter matches = view_downloads_filter(condition, m_temp_filter);
|
view_downloads_filter matches = view_downloads_filter(condition, m_temp_filter);
|
||||||
|
|
||||||
for (iterator itr = begin_visible(); itr != end_visible(); ++itr)
|
for (iterator itr = begin_visible(); itr != end_visible(); ++itr)
|
||||||
if (matches(itr->get()))
|
if (matches(*itr))
|
||||||
result.push_back(*itr);
|
result.push_back(*itr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
View::filter_download(core::Download* download) {
|
View::filter_download(core::Download* download) {
|
||||||
iterator itr = std::find_if(base_type::begin(), base_type::end(), entry_is(download));
|
iterator itr = std::find(base_type::begin(), base_type::end(), download);
|
||||||
|
|
||||||
if (itr == base_type::end())
|
if (itr == base_type::end())
|
||||||
throw torrent::internal_error("View::filter_download(...) could not find download.");
|
throw torrent::internal_error("View::filter_download(...) could not find download.");
|
||||||
|
|
||||||
if (view_downloads_filter(m_filter, m_temp_filter)(download)) {
|
if (view_downloads_filter(m_filter, m_temp_filter)(download)) {
|
||||||
if (itr >= end_visible()) {
|
if (itr >= end_visible()) {
|
||||||
auto entry = *itr;
|
|
||||||
|
|
||||||
erase_internal(itr);
|
erase_internal(itr);
|
||||||
insert_visible(entry);
|
insert_visible(download);
|
||||||
|
|
||||||
rpc::call_object_nothrow(m_event_added, rpc::make_target(download));
|
rpc::call_object_nothrow(m_event_added, rpc::make_target(download));
|
||||||
|
|
||||||
@@ -337,20 +317,16 @@ View::filter_download(core::Download* download) {
|
|||||||
// already visible.
|
// already visible.
|
||||||
//
|
//
|
||||||
// Consider removing this.
|
// Consider removing this.
|
||||||
auto entry = *itr;
|
|
||||||
|
|
||||||
erase_internal(itr);
|
erase_internal(itr);
|
||||||
insert_visible(entry);
|
insert_visible(download);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (itr >= end_visible())
|
if (itr >= end_visible())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto entry = *itr;
|
|
||||||
|
|
||||||
erase_internal(itr);
|
erase_internal(itr);
|
||||||
base_type::push_back(entry);
|
base_type::push_back(download);
|
||||||
|
|
||||||
rpc::call_object_nothrow(m_event_removed, rpc::make_target(download));
|
rpc::call_object_nothrow(m_event_removed, rpc::make_target(download));
|
||||||
}
|
}
|
||||||
@@ -369,8 +345,8 @@ View::clear_filter_on() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
View::insert_visible(const std::shared_ptr<Download>& d) {
|
View::insert_visible(Download* d) {
|
||||||
auto itr = std::find_if(begin_visible(), end_visible(), [this, &d](const auto& d2) { return view_downloads_compare(m_sortNew)(d.get(), d2.get()); });
|
auto itr = std::find_if(begin_visible(), end_visible(), [this, d](auto d2) { return view_downloads_compare(m_sortNew)(d, d2); });
|
||||||
|
|
||||||
m_size++;
|
m_size++;
|
||||||
m_focus += (m_focus >= position(itr));
|
m_focus += (m_focus >= position(itr));
|
||||||
|
|||||||
+7
-8
@@ -15,11 +15,10 @@
|
|||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <torrent/object.h>
|
#include <torrent/object.h>
|
||||||
#include <torrent/system/scheduler.h>
|
#include <torrent/utils/scheduler.h>
|
||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
|
||||||
@@ -27,9 +26,9 @@ namespace core {
|
|||||||
|
|
||||||
class Download;
|
class Download;
|
||||||
|
|
||||||
class View : private std::vector<std::shared_ptr<Download>> {
|
class View : private std::vector<Download*> {
|
||||||
public:
|
public:
|
||||||
typedef std::vector<std::shared_ptr<Download>> base_type;
|
typedef std::vector<Download*> base_type;
|
||||||
typedef std::function<void()> slot_void;
|
typedef std::function<void()> slot_void;
|
||||||
typedef std::list<slot_void> signal_void;
|
typedef std::list<slot_void> signal_void;
|
||||||
|
|
||||||
@@ -73,7 +72,7 @@ public:
|
|||||||
emit_changed();
|
emit_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void insert(const std::shared_ptr<Download>& download) { base_type::push_back(download); }
|
void insert(Download* download) { base_type::push_back(download); }
|
||||||
void erase(Download* download);
|
void erase(Download* download);
|
||||||
|
|
||||||
void set_visible(Download* download);
|
void set_visible(Download* download);
|
||||||
@@ -125,9 +124,9 @@ private:
|
|||||||
View(const View&);
|
View(const View&);
|
||||||
void operator=(const View&);
|
void operator=(const View&);
|
||||||
|
|
||||||
void push_back(const std::shared_ptr<Download>& d) { base_type::push_back(d); }
|
void push_back(Download* d) { base_type::push_back(d); }
|
||||||
|
|
||||||
inline void insert_visible(const std::shared_ptr<Download>& d);
|
inline void insert_visible(Download* d);
|
||||||
inline void erase_internal(iterator itr);
|
inline void erase_internal(iterator itr);
|
||||||
|
|
||||||
void emit_changed();
|
void emit_changed();
|
||||||
@@ -155,7 +154,7 @@ private:
|
|||||||
std::chrono::microseconds m_last_changed{};
|
std::chrono::microseconds m_last_changed{};
|
||||||
|
|
||||||
signal_void m_signal_changed;
|
signal_void m_signal_changed;
|
||||||
torrent::system::SchedulerEntry m_delay_changed;
|
torrent::utils::SchedulerEntry m_delay_changed;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace core
|
} // namespace core
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef RTORRENT_DISPLAY_MANAGER_H
|
#ifndef RTORRENT_DISPLAY_MANAGER_H
|
||||||
#define RTORRENT_DISPLAY_MANAGER_H
|
#define RTORRENT_DISPLAY_MANAGER_H
|
||||||
|
|
||||||
#include <torrent/system/scheduler.h>
|
#include <torrent/utils/scheduler.h>
|
||||||
|
|
||||||
#include "display/frame.h"
|
#include "display/frame.h"
|
||||||
|
|
||||||
@@ -32,8 +32,8 @@ private:
|
|||||||
Frame m_root_frame;
|
Frame m_root_frame;
|
||||||
|
|
||||||
std::chrono::microseconds m_time_last_update{};
|
std::chrono::microseconds m_time_last_update{};
|
||||||
torrent::system::ExternalScheduler m_scheduler;
|
torrent::utils::ExternalScheduler m_scheduler;
|
||||||
torrent::system::SchedulerEntry m_task_update;
|
torrent::utils::SchedulerEntry m_task_update;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ print_status_info(char* first, char* last) {
|
|||||||
if (!torrent::up_throttle_global()->is_throttled()) {
|
if (!torrent::up_throttle_global()->is_throttled()) {
|
||||||
first = print_buffer(first, last, "[Throttle off");
|
first = print_buffer(first, last, "[Throttle off");
|
||||||
} else {
|
} else {
|
||||||
first = print_buffer(first, last, "[Throttle %3i", (int)(torrent::up_throttle_global()->max_rate() / 1024));
|
first = print_buffer(first, last, "[Throttle %3i", torrent::up_throttle_global()->max_rate() / 1024);
|
||||||
|
|
||||||
if (!throttle_up_names.empty())
|
if (!throttle_up_names.empty())
|
||||||
first = print_status_throttle_limit(first, last, true, throttle_up_names);
|
first = print_status_throttle_limit(first, last, true, throttle_up_names);
|
||||||
@@ -354,7 +354,7 @@ print_status_info(char* first, char* last) {
|
|||||||
if (!torrent::down_throttle_global()->is_throttled()) {
|
if (!torrent::down_throttle_global()->is_throttled()) {
|
||||||
first = print_buffer(first, last, " / off KB]");
|
first = print_buffer(first, last, " / off KB]");
|
||||||
} else {
|
} else {
|
||||||
first = print_buffer(first, last, " / %3i", (int)(torrent::down_throttle_global()->max_rate() / 1024));
|
first = print_buffer(first, last, " / %3i", torrent::down_throttle_global()->max_rate() / 1024);
|
||||||
|
|
||||||
if (!throttle_down_names.empty())
|
if (!throttle_down_names.empty())
|
||||||
first = print_status_throttle_limit(first, last, false, throttle_down_names);
|
first = print_status_throttle_limit(first, last, false, throttle_down_names);
|
||||||
|
|||||||
@@ -16,12 +16,15 @@ namespace utils {
|
|||||||
|
|
||||||
namespace torrent {
|
namespace torrent {
|
||||||
class ClientInfo;
|
class ClientInfo;
|
||||||
|
class Entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Control;
|
class Control;
|
||||||
|
|
||||||
namespace display {
|
namespace display {
|
||||||
|
|
||||||
|
char* print_string(char* first, char* last, char* str);
|
||||||
|
|
||||||
char* print_hhmmss(char* first, char* last, time_t t);
|
char* print_hhmmss(char* first, char* last, time_t t);
|
||||||
char* print_hhmmss_local(char* first, char* last, time_t t);
|
char* print_hhmmss_local(char* first, char* last, time_t t);
|
||||||
char* print_ddhhmm(char* first, char* last, time_t t);
|
char* print_ddhhmm(char* first, char* last, time_t t);
|
||||||
@@ -39,6 +42,9 @@ char* print_download_percentage_done(char* first, char* last, core::Downlo
|
|||||||
|
|
||||||
char* print_client_version(char* first, char* last, const torrent::ClientInfo& clientInfo);
|
char* print_client_version(char* first, char* last, const torrent::ClientInfo& clientInfo);
|
||||||
|
|
||||||
|
char* print_entry_tags(char* first, char* last);
|
||||||
|
char* print_entry_file(char* first, char* last, const torrent::Entry& entry);
|
||||||
|
|
||||||
char* print_status_throttle_limit(char* first, char* last, bool up, const std::vector<std::string>& throttle_names);
|
char* print_status_throttle_limit(char* first, char* last, bool up, const std::vector<std::string>& throttle_names);
|
||||||
char* print_status_throttle_rate(char* first, char* last, bool up, const std::vector<std::string>& throttle_names, const double& global_rate);
|
char* print_status_throttle_rate(char* first, char* last, bool up, const std::vector<std::string>& throttle_names, const double& global_rate);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#define RTORRENT_WINDOW_BASE_H
|
#define RTORRENT_WINDOW_BASE_H
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <torrent/system/scheduler.h>
|
#include <torrent/utils/scheduler.h>
|
||||||
#include <torrent/system/thread.h>
|
#include <torrent/system/thread.h>
|
||||||
|
|
||||||
#include "canvas.h"
|
#include "canvas.h"
|
||||||
@@ -99,7 +99,7 @@ protected:
|
|||||||
extent_type m_max_width;
|
extent_type m_max_width;
|
||||||
extent_type m_max_height;
|
extent_type m_max_height;
|
||||||
|
|
||||||
torrent::system::SchedulerEntry m_task_update;
|
torrent::utils::SchedulerEntry m_task_update;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return a range with a distance of no more than __distance and
|
// Return a range with a distance of no more than __distance and
|
||||||
|
|||||||
@@ -57,8 +57,7 @@ WindowDownloadChunksSeen::redraw() {
|
|||||||
const torrent::TransferList* transfers = m_download->download()->transfer_list();
|
const torrent::TransferList* transfers = m_download->download()->transfer_list();
|
||||||
std::vector<torrent::BlockList*> transferChunks(transfers->begin(), transfers->end());
|
std::vector<torrent::BlockList*> transferChunks(transfers->begin(), transfers->end());
|
||||||
|
|
||||||
std::sort(transferChunks.begin(), transferChunks.end(),
|
std::sort(transferChunks.begin(), transferChunks.end());
|
||||||
[](const auto& left, const auto& right) { return left->index() < right->index(); });
|
|
||||||
|
|
||||||
std::vector<torrent::BlockList*>::const_iterator itrTransfer = transferChunks.begin();
|
std::vector<torrent::BlockList*>::const_iterator itrTransfer = transferChunks.begin();
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ WindowDownloadList::set_view(core::View* l) {
|
|||||||
// Return a pair of ints, representing a) the ncurses attributes and b) the ncurses color pair ID to use
|
// Return a pair of ints, representing a) the ncurses attributes and b) the ncurses color pair ID to use
|
||||||
std::pair<int, int>
|
std::pair<int, int>
|
||||||
WindowDownloadList::get_attr_color(core::View::iterator selected) {
|
WindowDownloadList::get_attr_color(core::View::iterator selected) {
|
||||||
core::Download* item = selected->get();
|
core::Download* item = *selected;
|
||||||
unsigned long focus_attr = selected == m_view->focus() ? m_canvas->attr_map().at(RCOLOR_FOCUS) : 0;
|
unsigned long focus_attr = selected == m_view->focus() ? m_canvas->attr_map().at(RCOLOR_FOCUS) : 0;
|
||||||
int offset = (((selected - m_view->begin_visible()) & 1) + 1) * RCOLOR_MAX; // Determine the even/odd offset for the color pair
|
int offset = (((selected - m_view->begin_visible()) & 1) + 1) * RCOLOR_MAX; // Determine the even/odd offset for the color pair
|
||||||
bool active = item->is_open() && item->is_active();
|
bool active = item->is_open() && item->is_active();
|
||||||
@@ -132,15 +132,15 @@ WindowDownloadList::redraw() {
|
|||||||
ColorKind focus_color = is_focused ? RCOLOR_FOCUS : RCOLOR_LABEL;
|
ColorKind focus_color = is_focused ? RCOLOR_FOCUS : RCOLOR_LABEL;
|
||||||
auto attr_color = get_attr_color(range.first);
|
auto attr_color = get_attr_color(range.first);
|
||||||
|
|
||||||
print_download_title(buffer.data(), last, range.first->get());
|
print_download_title(buffer.data(), last, *range.first);
|
||||||
m_canvas->print(0, pos, "%c %s", focus_char, buffer.data());
|
m_canvas->print(0, pos, "%c %s", focus_char, buffer.data());
|
||||||
m_canvas->set_attr(2, pos++, -1, attr_color.first, attr_color.second);
|
m_canvas->set_attr(2, pos++, -1, attr_color.first, attr_color.second);
|
||||||
|
|
||||||
print_download_info_full(buffer.data(), last, range.first->get());
|
print_download_info_full(buffer.data(), last, *range.first);
|
||||||
m_canvas->print(0, pos, "%c %s", focus_char, buffer.data());
|
m_canvas->print(0, pos, "%c %s", focus_char, buffer.data());
|
||||||
m_canvas->set_attr(2, pos++, -1, focus_color);
|
m_canvas->set_attr(2, pos++, -1, focus_color);
|
||||||
|
|
||||||
print_download_status(buffer.data(), last, range.first->get());
|
print_download_status(buffer.data(), last, *range.first);
|
||||||
m_canvas->print(0, pos, "%c %s", focus_char, buffer.data());
|
m_canvas->print(0, pos, "%c %s", focus_char, buffer.data());
|
||||||
m_canvas->set_attr(2, pos++, -1, focus_color);
|
m_canvas->set_attr(2, pos++, -1, focus_color);
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ WindowDownloadList::redraw() {
|
|||||||
char focus_char = range.first == m_view->focus() ? '*' : ' ';
|
char focus_char = range.first == m_view->focus() ? '*' : ' ';
|
||||||
auto attr_color = get_attr_color(range.first);
|
auto attr_color = get_attr_color(range.first);
|
||||||
|
|
||||||
print_download_info_compact(buffer.data(), last, range.first->get());
|
print_download_info_compact(buffer.data(), last, *range.first);
|
||||||
m_canvas->print(0, pos, "%c %s", focus_char, buffer.data());
|
m_canvas->print(0, pos, "%c %s", focus_char, buffer.data());
|
||||||
m_canvas->set_attr(2, pos++, -1, attr_color.first, attr_color.second);
|
m_canvas->set_attr(2, pos++, -1, attr_color.first, attr_color.second);
|
||||||
|
|
||||||
|
|||||||
@@ -25,10 +25,7 @@ WindowFileList::WindowFileList(const ui::ElementFileList* element) :
|
|||||||
// Convert std::string to std::wstring of given width (in screen positions),
|
// Convert std::string to std::wstring of given width (in screen positions),
|
||||||
// taking into account that some characters may be occupying two screen positions.
|
// taking into account that some characters may be occupying two screen positions.
|
||||||
std::wstring
|
std::wstring
|
||||||
wstring_width(const std::string& i_str, int width) {
|
wstring_width(const std::string& i_str, [[maybe_unused]] int width) {
|
||||||
if (width < 0)
|
|
||||||
width = 0;
|
|
||||||
|
|
||||||
std::wstring result(width + 1, L' ');
|
std::wstring result(width + 1, L' ');
|
||||||
size_t length = std::mbstowcs(result.data(), i_str.c_str(), width);
|
size_t length = std::mbstowcs(result.data(), i_str.c_str(), width);
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ private:
|
|||||||
signal_curl_get::iterator m_conn_insert;
|
signal_curl_get::iterator m_conn_insert;
|
||||||
signal_curl_get::iterator m_conn_erase;
|
signal_curl_get::iterator m_conn_erase;
|
||||||
|
|
||||||
torrent::system::SchedulerEntry m_task_deactivate;
|
torrent::utils::SchedulerEntry m_task_deactivate;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <mutex>
|
|
||||||
#include <vector>
|
|
||||||
#include <torrent/torrent.h>
|
#include <torrent/torrent.h>
|
||||||
#include <torrent/system/callbacks.h>
|
#include <torrent/system/callbacks.h>
|
||||||
#include <torrent/system/thread.h>
|
#include <torrent/system/thread.h>
|
||||||
@@ -43,20 +41,13 @@ WindowLog::redraw() {
|
|||||||
|
|
||||||
int pos = m_canvas->height();
|
int pos = m_canvas->height();
|
||||||
|
|
||||||
std::vector<torrent::log_entry> entries;
|
for (iterator itr = m_log->end(), last = find_older(); itr != last && pos > 0; --pos) {
|
||||||
|
itr--;
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard<torrent::log_buffer> guard(*m_log);
|
|
||||||
|
|
||||||
for (iterator itr = m_log->end(), last = find_older(); itr != last && entries.size() < static_cast<size_t>(pos); )
|
|
||||||
entries.push_back(*--itr);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& entry : entries) {
|
|
||||||
char buffer[16];
|
char buffer[16];
|
||||||
print_hhmmss_local(buffer, buffer + 16, static_cast<time_t>(entry.timestamp));
|
print_hhmmss_local(buffer, buffer + 16, static_cast<time_t>(itr->timestamp));
|
||||||
|
|
||||||
m_canvas->print(0, --pos, "(%s) %s", buffer, entry.message.c_str());
|
m_canvas->print(0, pos - 1, "(%s) %s", buffer, itr->message.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,13 +60,8 @@ WindowLog::receive_update() {
|
|||||||
if (!is_active())
|
if (!is_active())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::iterator_traits<iterator>::difference_type height;
|
auto itr = find_older();
|
||||||
|
auto height = std::min(std::distance(itr, (iterator)m_log->end()), (std::iterator_traits<iterator>::difference_type)10);
|
||||||
{
|
|
||||||
std::lock_guard<torrent::log_buffer> guard(*m_log);
|
|
||||||
|
|
||||||
height = std::min(std::distance(find_older(), (iterator)m_log->end()), (std::iterator_traits<iterator>::difference_type)10);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (height != m_max_height) {
|
if (height != m_max_height) {
|
||||||
m_min_height = height != 0 ? 1 : 0;
|
m_min_height = height != 0 ? 1 : 0;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#ifndef RTORRENT_DISPLAY_WINDOW_LOG_H
|
#ifndef RTORRENT_DISPLAY_WINDOW_LOG_H
|
||||||
#define RTORRENT_DISPLAY_WINDOW_LOG_H
|
#define RTORRENT_DISPLAY_WINDOW_LOG_H
|
||||||
|
|
||||||
#include <torrent/system/scheduler.h>
|
|
||||||
#include <torrent/utils/log_buffer.h>
|
#include <torrent/utils/log_buffer.h>
|
||||||
|
#include <torrent/utils/scheduler.h>
|
||||||
|
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
|
|
||||||
@@ -23,11 +23,9 @@ private:
|
|||||||
inline iterator find_older();
|
inline iterator find_older();
|
||||||
|
|
||||||
torrent::log_buffer* m_log;
|
torrent::log_buffer* m_log;
|
||||||
torrent::system::SchedulerEntry m_task_update;
|
torrent::utils::SchedulerEntry m_task_update;
|
||||||
|
|
||||||
align_cacheline
|
align_cacheline std::atomic<bool> m_log_updating{};
|
||||||
|
|
||||||
std::atomic<bool> m_log_updating{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <mutex>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "canvas.h"
|
#include "canvas.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
@@ -31,34 +29,24 @@ WindowLogComplete::redraw() {
|
|||||||
|
|
||||||
int pos = m_canvas->height();
|
int pos = m_canvas->height();
|
||||||
|
|
||||||
std::vector<torrent::log_entry> entries;
|
for (iterator itr = m_log->end(), last = m_log->begin(); itr != last && pos > 0; ) {
|
||||||
|
itr--;
|
||||||
{
|
|
||||||
std::lock_guard<torrent::log_buffer> guard(*m_log);
|
|
||||||
|
|
||||||
for (iterator itr = m_log->end(), last = m_log->begin(); itr != last && entries.size() < static_cast<size_t>(pos); )
|
|
||||||
entries.push_back(*--itr);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& entry : entries) {
|
|
||||||
if (pos <= 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
char buffer[16];
|
char buffer[16];
|
||||||
|
|
||||||
// Use an arbitrary min width of 60 for allowing multiple
|
// Use an arbitrary min width of 60 for allowing multiple
|
||||||
// lines. This should ensure we don't mess up the display when the
|
// lines. This should ensure we don't mess up the display when the
|
||||||
// screen is shrunk too much.
|
// screen is shrunk too much.
|
||||||
unsigned int timeWidth = 3 + print_hhmmss_local(buffer, buffer + 16, static_cast<time_t>(entry.timestamp)) - buffer;
|
unsigned int timeWidth = 3 + print_hhmmss_local(buffer, buffer + 16, static_cast<time_t>(itr->timestamp)) - buffer;
|
||||||
|
|
||||||
unsigned int logWidth = m_canvas->width() > 60 ? (m_canvas->width() - timeWidth) : (60 - timeWidth);
|
unsigned int logWidth = m_canvas->width() > 60 ? (m_canvas->width() - timeWidth) : (60 - timeWidth);
|
||||||
unsigned int logHeight = (entry.message.size() + logWidth - 1) / logWidth;
|
unsigned int logHeight = (itr->message.size() + logWidth - 1) / logWidth;
|
||||||
|
|
||||||
for (unsigned int j = logHeight; j > 0 && pos > 0; --j, --pos)
|
for (unsigned int j = logHeight; j > 0 && pos > 0; --j, --pos)
|
||||||
if (j == 1)
|
if (j == 1)
|
||||||
m_canvas->print(0, pos - 1, "(%s) %s", buffer, entry.message.substr(0, m_canvas->width() - timeWidth).c_str());
|
m_canvas->print(0, pos - 1, "(%s) %s", buffer, itr->message.substr(0, m_canvas->width() - timeWidth).c_str());
|
||||||
else
|
else
|
||||||
m_canvas->print(timeWidth, pos - 1, "%s", entry.message.substr(logWidth * (j - 1), m_canvas->width() - timeWidth).c_str());
|
m_canvas->print(timeWidth, pos - 1, "%s", itr->message.substr(logWidth * (j - 1), m_canvas->width() - timeWidth).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,21 @@
|
|||||||
|
|
||||||
namespace display {
|
namespace display {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
char
|
||||||
|
connection_type_char(const torrent::Peer* p) {
|
||||||
|
if (p->is_encrypted())
|
||||||
|
return p->is_incoming() ? 'R' : 'L';
|
||||||
|
|
||||||
|
if (p->is_obfuscated())
|
||||||
|
return p->is_incoming() ? 'H' : 'h';
|
||||||
|
|
||||||
|
return p->is_incoming() ? 'r' : 'l';
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
WindowPeerList::WindowPeerList(core::Download* d, PList* l, PList::iterator* f) :
|
WindowPeerList::WindowPeerList(core::Download* d, PList* l, PList::iterator* f) :
|
||||||
Window(new Canvas, 0, 0, 0, extent_full, extent_full),
|
Window(new Canvas, 0, 0, 0, extent_full, extent_full),
|
||||||
m_download(d),
|
m_download(d),
|
||||||
@@ -35,6 +50,7 @@ WindowPeerList::redraw() {
|
|||||||
m_canvas->print(x, y, "UP"); x += 7;
|
m_canvas->print(x, y, "UP"); x += 7;
|
||||||
m_canvas->print(x, y, "DOWN"); x += 7;
|
m_canvas->print(x, y, "DOWN"); x += 7;
|
||||||
m_canvas->print(x, y, "PEER"); x += 7;
|
m_canvas->print(x, y, "PEER"); x += 7;
|
||||||
|
// CT: R/H/r or L/h/l (RC4 / handshake-only / plain) + peer type (u/p/ )
|
||||||
m_canvas->print(x, y, "CT/RE/LO"); x += 10;
|
m_canvas->print(x, y, "CT/RE/LO"); x += 10;
|
||||||
m_canvas->print(x, y, "QS"); x += 6;
|
m_canvas->print(x, y, "QS"); x += 6;
|
||||||
m_canvas->print(x, y, "DONE"); x += 6;
|
m_canvas->print(x, y, "DONE"); x += 6;
|
||||||
@@ -95,7 +111,7 @@ WindowPeerList::redraw() {
|
|||||||
peerType = ' ';
|
peerType = ' ';
|
||||||
|
|
||||||
m_canvas->print(x, y, "%c%c/%c%c/%c%c",
|
m_canvas->print(x, y, "%c%c/%c%c/%c%c",
|
||||||
p->is_encrypted() ? (p->is_incoming() ? 'R' : 'L') : (p->is_incoming() ? 'r' : 'l'),
|
connection_type_char(p),
|
||||||
peerType,
|
peerType,
|
||||||
p->is_down_choked() ? std::tolower(remoteChoked) : remoteChoked,
|
p->is_down_choked() ? std::tolower(remoteChoked) : remoteChoked,
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "encryption_config.h"
|
||||||
|
|
||||||
|
#include <torrent/runtime/network_config.h>
|
||||||
|
#include <torrent/runtime/runtime.h>
|
||||||
|
#include <torrent/utils/option_strings.h>
|
||||||
|
|
||||||
|
#include "command_helpers.h"
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
|
namespace encryption_config {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
Policy
|
||||||
|
current_policy() {
|
||||||
|
return torrent::runtime::network_config()->encryption_policy();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Policy
|
||||||
|
default_policy() {
|
||||||
|
Policy policy;
|
||||||
|
policy.handshake = Policy::Mode::allow;
|
||||||
|
policy.stream = Policy::Mode::allow;
|
||||||
|
return policy;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
mode_to_string(Policy::Mode value) {
|
||||||
|
return torrent::option_to_str_or_throw(torrent::OPTION_ENCRYPTION_MODE,
|
||||||
|
static_cast<unsigned int>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
apply_mode_value(Policy& policy, Policy::Mode Policy::*field, const std::string& value) {
|
||||||
|
policy.*field = static_cast<Policy::Mode>(
|
||||||
|
torrent::option_find_string_str(torrent::OPTION_ENCRYPTION_MODE, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
summary_string(const Policy& policy) {
|
||||||
|
return "handshake=" + mode_to_string(policy.handshake)
|
||||||
|
+ " stream=" + mode_to_string(policy.stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
apply_policy(const Policy& policy) {
|
||||||
|
torrent::runtime::network_config()->set_encryption_policy(policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
torrent::Object
|
||||||
|
apply_handshake_set(const std::string& value) {
|
||||||
|
Policy policy = current_policy();
|
||||||
|
apply_mode_value(policy, &Policy::handshake, value);
|
||||||
|
apply_policy(policy);
|
||||||
|
return torrent::Object();
|
||||||
|
}
|
||||||
|
|
||||||
|
torrent::Object
|
||||||
|
apply_stream_set(const std::string& value) {
|
||||||
|
Policy policy = current_policy();
|
||||||
|
apply_mode_value(policy, &Policy::stream, value);
|
||||||
|
apply_policy(policy);
|
||||||
|
return torrent::Object();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
initialize_commands() {
|
||||||
|
apply_policy(default_policy());
|
||||||
|
|
||||||
|
CMD2_ANY("protocol.encryption", [](auto, auto) { return summary_string(current_policy()); });
|
||||||
|
|
||||||
|
CMD2_ANY("protocol.encryption.handshake", [](auto, auto) { return mode_to_string(current_policy().handshake); });
|
||||||
|
CMD2_ANY_STRING_V("protocol.encryption.handshake.set", [](auto, auto& str) { return apply_handshake_set(str); });
|
||||||
|
|
||||||
|
CMD2_ANY("protocol.encryption.stream", [](auto, auto) { return mode_to_string(current_policy().stream); });
|
||||||
|
CMD2_ANY_STRING_V("protocol.encryption.stream.set", [](auto, auto& str) { return apply_stream_set(str); });
|
||||||
|
|
||||||
|
rpc::rpc.mark_safe("protocol.encryption");
|
||||||
|
rpc::rpc.mark_safe("protocol.encryption.handshake");
|
||||||
|
rpc::rpc.mark_safe("protocol.encryption.handshake.set");
|
||||||
|
rpc::rpc.mark_safe("protocol.encryption.stream");
|
||||||
|
rpc::rpc.mark_safe("protocol.encryption.stream.set");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace encryption_config
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#ifndef RTORRENT_ENCRYPTION_CONFIG_H
|
||||||
|
#define RTORRENT_ENCRYPTION_CONFIG_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <torrent/runtime/encryption_policy.h>
|
||||||
|
#include <torrent/object.h>
|
||||||
|
|
||||||
|
namespace encryption_config {
|
||||||
|
|
||||||
|
using Policy = torrent::EncryptionPolicy;
|
||||||
|
|
||||||
|
Policy default_policy();
|
||||||
|
|
||||||
|
std::string mode_to_string(Policy::Mode value);
|
||||||
|
void apply_mode_value(Policy& policy, Policy::Mode Policy::*field, const std::string& value);
|
||||||
|
|
||||||
|
std::string summary_string(const Policy& policy);
|
||||||
|
|
||||||
|
void apply_policy(const Policy& policy);
|
||||||
|
torrent::Object apply_handshake_set(const std::string& value);
|
||||||
|
torrent::Object apply_stream_set(const std::string& value);
|
||||||
|
|
||||||
|
void initialize_commands();
|
||||||
|
|
||||||
|
} // namespace encryption_config
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <torrent/exceptions.h>
|
#include <torrent/exceptions.h>
|
||||||
|
|
||||||
rpc::ip_table_list ip_tables;
|
rpc::ip_table_list ip_tables;
|
||||||
@@ -29,32 +28,3 @@ expand_path(const std::string& path) {
|
|||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolves a path to a canonical one with no symlinks or relative components,
|
|
||||||
// so it can safely be handed to an external script. Returns an empty string if
|
|
||||||
// the path does not name an existing file or directory.
|
|
||||||
std::string
|
|
||||||
resolve_path(const std::string& path) {
|
|
||||||
if (path.empty())
|
|
||||||
return std::string();
|
|
||||||
|
|
||||||
char* resolved = ::realpath(expand_path(path).c_str(), nullptr);
|
|
||||||
|
|
||||||
if (resolved == nullptr)
|
|
||||||
return std::string();
|
|
||||||
|
|
||||||
std::string result(resolved);
|
|
||||||
std::free(resolved);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
resolve_path_or_throw(const std::string& path) {
|
|
||||||
auto result = resolve_path(path);
|
|
||||||
|
|
||||||
if (result.empty())
|
|
||||||
throw torrent::input_error("Could not resolve path: '" + path + "'.");
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ extern rpc::ip_table_list ip_tables;
|
|||||||
extern Control* control;
|
extern Control* control;
|
||||||
|
|
||||||
std::string expand_path(const std::string& path);
|
std::string expand_path(const std::string& path);
|
||||||
std::string resolve_path(const std::string& path);
|
|
||||||
std::string resolve_path_or_throw(const std::string& path);
|
|
||||||
|
|
||||||
namespace rpc {
|
namespace rpc {
|
||||||
class SCgi;
|
class SCgi;
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ public:
|
|||||||
|
|
||||||
bool pressed(int key);
|
bool pressed(int key);
|
||||||
|
|
||||||
|
void ignore(int key) { (*this)[key] = slot_void(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_enabled{true};
|
bool m_enabled{true};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,11 +2,12 @@
|
|||||||
#define RTORRENT_INPUT_INPUT_EVENT_H
|
#define RTORRENT_INPUT_INPUT_EVENT_H
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <torrent/system/event.h>
|
|
||||||
|
#include <torrent/event.h>
|
||||||
|
|
||||||
namespace input {
|
namespace input {
|
||||||
|
|
||||||
class InputEvent : public torrent::system::Event {
|
class InputEvent : public torrent::Event {
|
||||||
public:
|
public:
|
||||||
typedef std::function<void (int)> slot_int;
|
typedef std::function<void (int)> slot_int;
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ PathInput::receive_do_complete() {
|
|||||||
entry.s_name += '/';
|
entry.s_name += '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
range_type r = find_incomplete(dir, str().substr(dirEnd, get_pos() - dirEnd));
|
range_type r = find_incomplete(dir, str().substr(dirEnd, get_pos()));
|
||||||
|
|
||||||
if (r.first == r.second)
|
if (r.first == r.second)
|
||||||
return; // Show some nice colors here.
|
return; // Show some nice colors here.
|
||||||
|
|||||||
+12
-15
@@ -301,6 +301,9 @@ main(int argc, char** argv) {
|
|||||||
"schedule = low_diskspace,5,60,((close_low_diskspace,500M))\n"
|
"schedule = low_diskspace,5,60,((close_low_diskspace,500M))\n"
|
||||||
"schedule = prune_file_status,3600,86400,((system.file_status_cache.prune))\n"
|
"schedule = prune_file_status,3600,86400,((system.file_status_cache.prune))\n"
|
||||||
|
|
||||||
|
"protocol.encryption.handshake.set=allow\n"
|
||||||
|
"protocol.encryption.stream.set=allow\n"
|
||||||
|
|
||||||
"ui.color.focus.set=reverse\n"
|
"ui.color.focus.set=reverse\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -319,8 +322,6 @@ main(int argc, char** argv) {
|
|||||||
CMD_REDIRECT("ratio.max.set", "group.seeding.ratio.max.set");
|
CMD_REDIRECT("ratio.max.set", "group.seeding.ratio.max.set");
|
||||||
CMD_REDIRECT("ratio.upload.set", "group.seeding.ratio.upload.set");
|
CMD_REDIRECT("ratio.upload.set", "group.seeding.ratio.upload.set");
|
||||||
|
|
||||||
CMD_REDIRECT("encryption", "protocol.encryption.set");
|
|
||||||
|
|
||||||
CMD_REDIRECT("check_hash", "pieces.hash.on_completion.set");
|
CMD_REDIRECT("check_hash", "pieces.hash.on_completion.set");
|
||||||
|
|
||||||
CMD_REDIRECT("connection_leech", "protocol.connection.leech.set");
|
CMD_REDIRECT("connection_leech", "protocol.connection.leech.set");
|
||||||
@@ -344,9 +345,8 @@ main(int argc, char** argv) {
|
|||||||
CMD_REDIRECT("directory", "directory.default.set");
|
CMD_REDIRECT("directory", "directory.default.set");
|
||||||
CMD_REDIRECT("session", "session.path.set");
|
CMD_REDIRECT("session", "session.path.set");
|
||||||
|
|
||||||
CMD_REDIRECT_NO_EXPORT("port_range", "network.listen.port.range.set");
|
CMD_REDIRECT("scgi_port", "network.scgi.open_port");
|
||||||
CMD_REDIRECT_NO_EXPORT("scgi_port", "network.scgi.open_port");
|
CMD_REDIRECT("scgi_local", "network.scgi.open_local");
|
||||||
CMD_REDIRECT_NO_EXPORT("scgi_local", "network.scgi.open_local");
|
|
||||||
|
|
||||||
CMD_REDIRECT("to_gm_time", "convert.gm_time");
|
CMD_REDIRECT("to_gm_time", "convert.gm_time");
|
||||||
CMD_REDIRECT("to_gm_date", "convert.gm_date");
|
CMD_REDIRECT("to_gm_date", "convert.gm_date");
|
||||||
@@ -379,12 +379,6 @@ main(int argc, char** argv) {
|
|||||||
lt_log_print(torrent::LOG_WARN, "network.max_open_files.set is deprecated, use system.sockets.files.min_alloc.set instead.");
|
lt_log_print(torrent::LOG_WARN, "network.max_open_files.set is deprecated, use system.sockets.files.min_alloc.set instead.");
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Keep d.directory_base for a while as it is widely used.
|
|
||||||
CMD_REDIRECT("d.directory_base", "d.directory");
|
|
||||||
CMD_REDIRECT("d.directory_base.set", "d.directory.base.set");
|
|
||||||
|
|
||||||
rpc::rpc.mark_safe("d.directory_base");
|
|
||||||
|
|
||||||
// if (rpc::call_command_value("method.use_intermediate") == 1) {
|
// if (rpc::call_command_value("method.use_intermediate") == 1) {
|
||||||
|
|
||||||
// } else if (rpc::call_command_value("method.use_intermediate") == 2) {
|
// } else if (rpc::call_command_value("method.use_intermediate") == 2) {
|
||||||
@@ -396,14 +390,17 @@ main(int argc, char** argv) {
|
|||||||
CMD_REDIRECT("schedule2", "schedule");
|
CMD_REDIRECT("schedule2", "schedule");
|
||||||
CMD_REDIRECT("schedule_remove2", "schedule.remove");
|
CMD_REDIRECT("schedule_remove2", "schedule.remove");
|
||||||
|
|
||||||
|
// TODO: Remove file.append when cleaning these up.
|
||||||
|
|
||||||
CMD_REDIRECT("bind", "network.bind_address.set");
|
CMD_REDIRECT("bind", "network.bind_address.set");
|
||||||
CMD_REDIRECT("ip", "network.local_address.set");
|
CMD_REDIRECT("ip", "network.local_address.set");
|
||||||
|
CMD_REDIRECT("port_range", "network.port_range.set");
|
||||||
|
|
||||||
// TODO: Check if dht is on by default.
|
// TODO: Check if dht is on by default.
|
||||||
CMD_REDIRECT("dht", "dht.mode.set");
|
CMD_REDIRECT("dht", "dht.mode.set");
|
||||||
|
|
||||||
CMD_REDIRECT("port_random", "network.listen.port.random.set");
|
CMD_REDIRECT("port_random", "network.port_random.set");
|
||||||
CMD_REDIRECT("proxy_address", "network.proxy.http.set");
|
CMD_REDIRECT("proxy_address", "network.proxy_address.set");
|
||||||
|
|
||||||
CMD_REDIRECT("key_layout", "keys.layout.set");
|
CMD_REDIRECT("key_layout", "keys.layout.set");
|
||||||
|
|
||||||
@@ -492,7 +489,6 @@ main(int argc, char** argv) {
|
|||||||
control->cleanup();
|
control->cleanup();
|
||||||
|
|
||||||
} catch (torrent::internal_error& e) {
|
} catch (torrent::internal_error& e) {
|
||||||
if (control != nullptr)
|
|
||||||
control->cleanup_exception();
|
control->cleanup_exception();
|
||||||
|
|
||||||
std::cout << "rtorrent: caught torrent::internal_error: "
|
std::cout << "rtorrent: caught torrent::internal_error: "
|
||||||
@@ -506,7 +502,6 @@ main(int argc, char** argv) {
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
if (control != nullptr)
|
|
||||||
control->cleanup_exception();
|
control->cleanup_exception();
|
||||||
|
|
||||||
std::cout << "rtorrent: caught" << typeid(e).name() << " : " << e.what() << std::endl;
|
std::cout << "rtorrent: caught" << typeid(e).name() << " : " << e.what() << std::endl;
|
||||||
@@ -552,6 +547,8 @@ handle_sigbus(int signum, siginfo_t* sa, [[maybe_unused]] void* ptr) {
|
|||||||
#else
|
#else
|
||||||
output << "Stack dump not enabled." << std::endl;
|
output << "Stack dump not enabled." << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
output << std::endl << "Error: " << std::strerror(sa->si_errno) << std::endl;
|
||||||
|
|
||||||
const char* signal_reason;
|
const char* signal_reason;
|
||||||
|
|
||||||
switch (sa->si_code) {
|
switch (sa->si_code) {
|
||||||
|
|||||||
+1
-9
@@ -1,7 +1,5 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
#include "core/download.h"
|
#include "core/download.h"
|
||||||
#include "parse.h"
|
#include "parse.h"
|
||||||
|
|
||||||
@@ -43,13 +41,7 @@ command_base_call_value_base(command_base* command_raw, target_type target, cons
|
|||||||
return command_base::_call<typename command_value_function<T>::type, T>(command_raw, target, val);
|
return command_base::_call<typename command_value_function<T>::type, T>(command_raw, target, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto value = arg.as_value();
|
return command_base::_call<typename command_value_function<T>::type, T>(command_raw, target, unit * arg.as_value());
|
||||||
|
|
||||||
if (value > std::numeric_limits<int64_t>::max() / unit ||
|
|
||||||
value < std::numeric_limits<int64_t>::min() / unit)
|
|
||||||
throw torrent::input_error("Value out of range.");
|
|
||||||
|
|
||||||
return command_base::_call<typename command_value_function<T>::type, T>(command_raw, target, unit * value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> const torrent::Object
|
template <typename T> const torrent::Object
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#ifndef RTORRENT_RPC_COMMAND_MAP_H
|
#ifndef RTORRENT_RPC_COMMAND_MAP_H
|
||||||
#define RTORRENT_RPC_COMMAND_MAP_H
|
#define RTORRENT_RPC_COMMAND_MAP_H
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -105,10 +104,6 @@ inline target_type make_target(T target) {
|
|||||||
return target_type((int)target_type_id<T>::value, target);
|
return target_type((int)target_type_id<T>::value, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline target_type make_target(const std::shared_ptr<core::Download>& target) {
|
|
||||||
return make_target(target.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline target_type make_target_pair(T target1, T target2) {
|
inline target_type make_target_pair(T target1, T target2) {
|
||||||
return target_type((int)target_type_id<T, T>::value, target1, target2);
|
return target_type((int)target_type_id<T, T>::value, target1, target2);
|
||||||
|
|||||||
@@ -61,20 +61,14 @@ CommandScheduler::call_item(value_type item) {
|
|||||||
// Remove the item before calling the command if it should be
|
// Remove the item before calling the command if it should be
|
||||||
// removed.
|
// removed.
|
||||||
|
|
||||||
std::string key = item->key();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
rpc::call_object(item->command());
|
rpc::call_object(item->command());
|
||||||
|
|
||||||
} catch (torrent::input_error& e) {
|
} catch (torrent::input_error& e) {
|
||||||
if (m_slotErrorMessage)
|
if (m_slotErrorMessage)
|
||||||
m_slotErrorMessage("Scheduled command failed: " + key + ": " + e.what());
|
m_slotErrorMessage("Scheduled command failed: " + item->key() + ": " + e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
// The command is allowed to erase or replace this item, which deletes it.
|
|
||||||
if (std::find(begin(), end(), item) == end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Still schedule if we caught a torrrent::input_error?
|
// Still schedule if we caught a torrrent::input_error?
|
||||||
auto next = item->next_time_scheduled();
|
auto next = item->next_time_scheduled();
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <torrent/object.h>
|
#include <torrent/object.h>
|
||||||
#include <torrent/system/scheduler.h>
|
#include <torrent/utils/scheduler.h>
|
||||||
|
|
||||||
namespace rpc {
|
namespace rpc {
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ private:
|
|||||||
uint32_t m_interval{};
|
uint32_t m_interval{};
|
||||||
std::chrono::microseconds m_time_scheduled;
|
std::chrono::microseconds m_time_scheduled;
|
||||||
|
|
||||||
torrent::system::SchedulerEntry m_task;
|
torrent::utils::SchedulerEntry m_task;
|
||||||
|
|
||||||
// Flags for various things.
|
// Flags for various things.
|
||||||
};
|
};
|
||||||
|
|||||||
+150
-49
@@ -1,24 +1,23 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <cassert>
|
||||||
|
#include <cerrno>
|
||||||
#include "rpc/exec_file.h"
|
#include <cstring>
|
||||||
|
#include <fcntl.h>
|
||||||
// #include <cassert>
|
#include <spawn.h>
|
||||||
// #include <cerrno>
|
#include <string>
|
||||||
// #include <cstring>
|
|
||||||
// #include <fcntl.h>
|
|
||||||
// #include <spawn.h>
|
|
||||||
// #include <string>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
// #include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/uio.h>
|
#include <sys/wait.h>
|
||||||
// #include <torrent/net/fd.h>
|
#include <torrent/net/fd.h>
|
||||||
#include <torrent/system/thread.h>
|
#include <torrent/system/thread.h>
|
||||||
#include <torrent/system/spawn_process.h>
|
|
||||||
#include <torrent/system/types.h>
|
#include <torrent/system/types.h>
|
||||||
|
|
||||||
#include "rpc/parse.h"
|
#include "exec_file.h"
|
||||||
|
#include "parse.h"
|
||||||
|
|
||||||
|
// Standard POSIX environment pointer
|
||||||
|
extern char** environ;
|
||||||
|
|
||||||
namespace rpc {
|
namespace rpc {
|
||||||
|
|
||||||
@@ -26,56 +25,159 @@ namespace rpc {
|
|||||||
|
|
||||||
int
|
int
|
||||||
ExecFile::execute(const char* file, char* const* argv, int flags) {
|
ExecFile::execute(const char* file, char* const* argv, int flags) {
|
||||||
torrent::system::SpawnProcess spawn_process;
|
assert(!((flags & flag_capture) && (flags & flag_background)));
|
||||||
|
|
||||||
spawn_process.set_log_fd(m_log_fd);
|
// Write the executed command and its parameters to the log fd.
|
||||||
spawn_process.set_background(flags & flag_background);
|
[[maybe_unused]] int result;
|
||||||
spawn_process.set_capture_output(flags & flag_capture);
|
|
||||||
|
|
||||||
if (m_log_fd != -1) {
|
if (m_log_fd != -1) {
|
||||||
std::vector<struct iovec> iovecs;
|
for (char* const* itr = argv; *itr != NULL; itr++) {
|
||||||
iovecs.reserve(32);
|
if (itr == argv)
|
||||||
iovecs.push_back({const_cast<char*>("\n---\n"), 5});
|
result = write(m_log_fd, "\n---\n", sizeof("\n---\n"));
|
||||||
|
else
|
||||||
|
result = write(m_log_fd, " ", 1);
|
||||||
|
|
||||||
for (auto* itr = argv; *itr != nullptr; itr++) {
|
result = write(m_log_fd, *itr, std::strlen(*itr));
|
||||||
if (itr != argv)
|
|
||||||
iovecs.push_back({const_cast<char*>(" "), 1});
|
|
||||||
|
|
||||||
iovecs.push_back({*itr, std::strlen(*itr)});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
iovecs.push_back({const_cast<char*>("\n---\n"), 5});
|
result = write(m_log_fd, "\n---\n", sizeof("\n---\n"));
|
||||||
|
|
||||||
[[maybe_unused]] int result = ::writev(m_log_fd, iovecs.data(), iovecs.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int spawn_status = spawn_process.execute(file, argv);
|
posix_spawn_file_actions_t actions{};
|
||||||
|
|
||||||
|
if (posix_spawn_file_actions_init(&actions) != 0)
|
||||||
|
throw torrent::internal_error("ExecFile::execute(...) posix_spawn_file_actions_init failed.");
|
||||||
|
|
||||||
|
posix_spawnattr_t attr;
|
||||||
|
posix_spawnattr_init(&attr);
|
||||||
|
|
||||||
|
// Try to avoid leaking open fds to the spawned process. Prefer POSIX_SPAWN_CLOEXEC_DEFAULT
|
||||||
|
// (macOS-only) or posix_spawn_file_actions_addclosefrom_np (glibc >= 2.34, FreeBSD >= 13.1).
|
||||||
|
//
|
||||||
|
// Other platforms like musl libc, OpenBSD and NetBSD must rely on explicit O_CLOEXEC.
|
||||||
|
|
||||||
|
// Handle standard input redirection (/dev/null), posix_spawn_file_actions_addopen handles opening
|
||||||
|
// and dup2 natively
|
||||||
|
if (posix_spawn_file_actions_addopen(&actions, 0, "/dev/null", O_RDWR, 0) != 0) {
|
||||||
|
// Fallback if open fails inside action setup
|
||||||
|
posix_spawn_file_actions_addclose(&actions, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pipe_0 = -1;
|
||||||
|
int pipe_1 = -1;
|
||||||
|
|
||||||
|
// Handle standard output redirection
|
||||||
|
if (flags & flag_capture) {
|
||||||
|
torrent::fd_open_pipe(pipe_0, pipe_1);
|
||||||
|
|
||||||
|
posix_spawn_file_actions_adddup2(&actions, pipe_1, 1);
|
||||||
|
|
||||||
|
// Ensure the write end of the pipe is closed in the child after duplicating.
|
||||||
|
posix_spawn_file_actions_addclose(&actions, pipe_0);
|
||||||
|
posix_spawn_file_actions_addclose(&actions, pipe_1);
|
||||||
|
|
||||||
|
} else if (m_log_fd != -1) {
|
||||||
|
posix_spawn_file_actions_adddup2(&actions, m_log_fd, 1);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
posix_spawn_file_actions_addopen(&actions, 1, "/dev/null", O_RDWR, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_log_fd != -1) {
|
||||||
|
posix_spawn_file_actions_adddup2(&actions, m_log_fd, 2);
|
||||||
|
} else {
|
||||||
|
posix_spawn_file_actions_addopen(&actions, 2, "/dev/null", O_RDWR, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
short spawn_flags = 0;
|
||||||
|
|
||||||
|
#if defined(POSIX_SPAWN_CLOEXEC_DEFAULT)
|
||||||
|
spawn_flags |= POSIX_SPAWN_CLOEXEC_DEFAULT;
|
||||||
|
#elif defined(HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP)
|
||||||
|
posix_spawn_file_actions_addclosefrom_np(&actions, 3);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (flags & flag_background) {
|
||||||
|
#ifdef POSIX_SPAWN_SETSID
|
||||||
|
spawn_flags |= POSIX_SPAWN_SETSID;
|
||||||
|
#else
|
||||||
|
spawn_flags |= POSIX_SPAWN_SETPGROUP;
|
||||||
|
posix_spawnattr_setpgroup(&attr, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
posix_spawnattr_setflags(&attr, spawn_flags);
|
||||||
|
|
||||||
|
pid_t child_pid{};
|
||||||
|
int spawn_status = posix_spawnp(&child_pid, file, &actions, &attr, argv, environ);
|
||||||
|
|
||||||
|
posix_spawn_file_actions_destroy(&actions);
|
||||||
|
posix_spawnattr_destroy(&attr);
|
||||||
|
|
||||||
if (spawn_status != 0) {
|
if (spawn_status != 0) {
|
||||||
if (m_log_fd != -1) {
|
if (pipe_0 != -1)
|
||||||
auto prefix = "\n--- posix_spawn failed: ";
|
torrent::fd_close(pipe_0);
|
||||||
auto errno_str = torrent::system::errno_enum_str(spawn_status) + " ---\n";
|
|
||||||
|
|
||||||
struct iovec iovecs[2] = {
|
if (pipe_1 != -1)
|
||||||
{const_cast<char*>(prefix), std::strlen(prefix)},
|
torrent::fd_close(pipe_1);
|
||||||
{const_cast<char*>(errno_str.c_str()), errno_str.size()},
|
|
||||||
};
|
|
||||||
|
|
||||||
[[maybe_unused]] int result = ::writev(m_log_fd, iovecs, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw torrent::input_error("ExecFile::execute() posix_spawn failed: " + torrent::system::errno_enum_str(spawn_status));
|
throw torrent::input_error("ExecFile::execute() posix_spawn failed: " + torrent::system::errno_enum_str(spawn_status));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (flags & flag_capture) {
|
||||||
|
m_capture = std::string();
|
||||||
|
torrent::fd_close(pipe_1);
|
||||||
|
|
||||||
|
char buffer[4096];
|
||||||
|
ssize_t length;
|
||||||
|
|
||||||
|
do {
|
||||||
|
length = read(pipe_0, buffer, sizeof(buffer));
|
||||||
|
|
||||||
|
if (length > 0)
|
||||||
|
m_capture += std::string(buffer, length);
|
||||||
|
|
||||||
|
} while (length > 0);
|
||||||
|
|
||||||
|
torrent::fd_close(pipe_0);
|
||||||
|
|
||||||
|
if (m_log_fd != -1) {
|
||||||
|
result = write(m_log_fd, "Captured output:\n", sizeof("Captured output:\n"));
|
||||||
|
result = write(m_log_fd, m_capture.data(), m_capture.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (flags & flag_background) {
|
if (flags & flag_background) {
|
||||||
m_waitpid_queue.close_pid(spawn_process.child_pid());
|
if (m_log_fd != -1)
|
||||||
|
result = write(m_log_fd, "\n--- Running in Background ---\n", sizeof("\n--- Running in Background ---\n"));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & flag_capture)
|
int status;
|
||||||
m_capture = spawn_process.capture_child_output();
|
|
||||||
|
|
||||||
return spawn_process.wait_for_child();
|
while (::waitpid(child_pid, &status, 0) == -1) {
|
||||||
|
switch (errno) {
|
||||||
|
case EINTR:
|
||||||
|
continue;
|
||||||
|
case ECHILD:
|
||||||
|
throw torrent::internal_error("ExecFile::execute(...) waitpid failed with ECHILD, child process not found.");
|
||||||
|
case EINVAL:
|
||||||
|
throw torrent::internal_error("ExecFile::execute(...) waitpid failed with EINVAL.");
|
||||||
|
default:
|
||||||
|
throw torrent::internal_error("ExecFile::execute(...) waitpid failed with unexpected error: " + std::string(std::strerror(errno)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check return value?
|
||||||
|
if (m_log_fd != -1) {
|
||||||
|
if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
|
||||||
|
result = write(m_log_fd, "\n--- Success ---\n", sizeof("\n--- Success ---\n"));
|
||||||
|
else
|
||||||
|
result = write(m_log_fd, "\n--- Error ---\n", sizeof("\n--- Error ---\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
torrent::Object
|
torrent::Object
|
||||||
@@ -83,9 +185,8 @@ ExecFile::execute_object(const torrent::Object& rawArgs, int flags) {
|
|||||||
char* argsBuffer[max_args];
|
char* argsBuffer[max_args];
|
||||||
char** argsCurrent = argsBuffer;
|
char** argsCurrent = argsBuffer;
|
||||||
|
|
||||||
// On the heap: a buffer of buffer_size does not belong on the stack of whichever thread runs the command.
|
// Size of value strings are less than 24.
|
||||||
auto valueStorage = std::make_unique<char[]>(buffer_size + 1);
|
char valueBuffer[buffer_size+1];
|
||||||
char* valueBuffer = valueStorage.get();
|
|
||||||
char* valueCurrent = valueBuffer;
|
char* valueCurrent = valueBuffer;
|
||||||
|
|
||||||
if (rawArgs.is_list()) {
|
if (rawArgs.is_list()) {
|
||||||
@@ -136,4 +237,4 @@ ExecFile::execute_object(const torrent::Object& rawArgs, int flags) {
|
|||||||
return torrent::Object((int64_t)status);
|
return torrent::Object((int64_t)status);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace rpc
|
}
|
||||||
|
|||||||
+1
-5
@@ -3,14 +3,12 @@
|
|||||||
|
|
||||||
#include <torrent/object.h>
|
#include <torrent/object.h>
|
||||||
|
|
||||||
#include "utils/waitpid_queue.h"
|
|
||||||
|
|
||||||
namespace rpc {
|
namespace rpc {
|
||||||
|
|
||||||
class ExecFile {
|
class ExecFile {
|
||||||
public:
|
public:
|
||||||
static constexpr unsigned int max_args = 128;
|
static constexpr unsigned int max_args = 128;
|
||||||
static constexpr unsigned int buffer_size = 128 * 1024;
|
static constexpr unsigned int buffer_size = 4096;
|
||||||
|
|
||||||
static constexpr int flag_expand_tilde = 0x1;
|
static constexpr int flag_expand_tilde = 0x1;
|
||||||
static constexpr int flag_throw = 0x2;
|
static constexpr int flag_throw = 0x2;
|
||||||
@@ -26,8 +24,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
int m_log_fd{-1};
|
int m_log_fd{-1};
|
||||||
std::string m_capture;
|
std::string m_capture;
|
||||||
|
|
||||||
utils::WaitpidQueue m_waitpid_queue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-61
@@ -2,10 +2,8 @@
|
|||||||
|
|
||||||
#include "rpc/jsonrpc.h"
|
#include "rpc/jsonrpc.h"
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
|
||||||
#include <torrent/common.h>
|
#include <torrent/common.h>
|
||||||
#include <torrent/torrent.h>
|
#include <torrent/torrent.h>
|
||||||
#include <torrent/utils/string_manip.h>
|
#include <torrent/utils/string_manip.h>
|
||||||
@@ -15,7 +13,6 @@
|
|||||||
#include "rpc/command_map.h"
|
#include "rpc/command_map.h"
|
||||||
#include "rpc/nlohmann/json.h"
|
#include "rpc/nlohmann/json.h"
|
||||||
#include "rpc/parse_commands.h"
|
#include "rpc/parse_commands.h"
|
||||||
#include "rpc/scgi_task.h"
|
|
||||||
#include "torrent/exceptions.h"
|
#include "torrent/exceptions.h"
|
||||||
#include "torrent/object.h"
|
#include "torrent/object.h"
|
||||||
#include "utils/functional.h"
|
#include "utils/functional.h"
|
||||||
@@ -29,12 +26,10 @@ constexpr int JSONRPC_METHOD_NOT_FOUND_ERROR = -32601;
|
|||||||
constexpr int JSONRPC_INVALID_PARAMS_ERROR = -32602;
|
constexpr int JSONRPC_INVALID_PARAMS_ERROR = -32602;
|
||||||
constexpr int JSONRPC_INTERNAL_ERROR = -32000;
|
constexpr int JSONRPC_INTERNAL_ERROR = -32000;
|
||||||
|
|
||||||
constexpr uint32_t max_json_depth = 1024;
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
torrent::Object
|
torrent::Object
|
||||||
json_to_object(const json& value, uint32_t depth = 0) {
|
json_to_object(const json& value) {
|
||||||
switch (value.type()) {
|
switch (value.type()) {
|
||||||
case json::value_t::number_unsigned:
|
case json::value_t::number_unsigned:
|
||||||
case json::value_t::number_integer:
|
case json::value_t::number_integer:
|
||||||
@@ -47,26 +42,20 @@ json_to_object(const json& value, uint32_t depth = 0) {
|
|||||||
return torrent::Object(value.get<std::string>());
|
return torrent::Object(value.get<std::string>());
|
||||||
|
|
||||||
case json::value_t::array: {
|
case json::value_t::array: {
|
||||||
if (++depth >= max_json_depth)
|
|
||||||
throw torrent::input_error("invalid parameters: max depth reached");
|
|
||||||
|
|
||||||
auto array_raw = torrent::Object::create_list();
|
auto array_raw = torrent::Object::create_list();
|
||||||
auto& array = array_raw.as_list();
|
auto& array = array_raw.as_list();
|
||||||
|
|
||||||
for (const auto& entry : value)
|
for (const auto& entry : value)
|
||||||
array.push_back(json_to_object(entry, depth));
|
array.push_back(json_to_object(entry));
|
||||||
|
|
||||||
return array_raw;
|
return array_raw;
|
||||||
}
|
}
|
||||||
case json::value_t::object: {
|
case json::value_t::object: {
|
||||||
if (++depth >= max_json_depth)
|
|
||||||
throw torrent::input_error("invalid parameters: max depth reached");
|
|
||||||
|
|
||||||
auto map_raw = torrent::Object::create_map();
|
auto map_raw = torrent::Object::create_map();
|
||||||
auto& map = map_raw.as_map();
|
auto& map = map_raw.as_map();
|
||||||
|
|
||||||
for (const auto& entry : value.items())
|
for (const auto& entry : value.items())
|
||||||
map[entry.key()] = json_to_object(entry.value(), depth);
|
map[entry.key()] = json_to_object(entry.value());
|
||||||
|
|
||||||
return map_raw;
|
return map_raw;
|
||||||
}
|
}
|
||||||
@@ -149,7 +138,7 @@ jsonrpc_call_command(const std::string& method, const json& params) {
|
|||||||
|
|
||||||
CommandMap::iterator itr = commands.find(method.c_str());
|
CommandMap::iterator itr = commands.find(method.c_str());
|
||||||
|
|
||||||
if (itr == commands.end() || !(itr->second.m_flags & CommandMap::flag_public_rpc)) {
|
if (itr == commands.end()) {
|
||||||
throw rpc_error(JSONRPC_METHOD_NOT_FOUND_ERROR, "method not found: " + method);
|
throw rpc_error(JSONRPC_METHOD_NOT_FOUND_ERROR, "method not found: " + method);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,49 +214,13 @@ handle_notification(const json& request) noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
using json_input_adapter = decltype(nlohmann::detail::input_adapter(std::declval<const char*>(), std::declval<const char*>()));
|
|
||||||
using json_dom_parser = nlohmann::detail::json_sax_dom_parser<json, json_input_adapter>;
|
|
||||||
|
|
||||||
class json_depth_limited_parser : public json_dom_parser {
|
|
||||||
public:
|
|
||||||
explicit json_depth_limited_parser(json& root) : json_dom_parser(root) {}
|
|
||||||
|
|
||||||
bool start_object(std::size_t length) { return enter() && json_dom_parser::start_object(length); }
|
|
||||||
bool start_array(std::size_t length) { return enter() && json_dom_parser::start_array(length); }
|
|
||||||
|
|
||||||
bool end_object() { m_depth--; return json_dom_parser::end_object(); }
|
|
||||||
bool end_array() { m_depth--; return json_dom_parser::end_array(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool enter() { return ++m_depth <= max_json_depth; }
|
|
||||||
|
|
||||||
uint32_t m_depth{0};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
JsonRpc::process(const char* in_buffer, uint32_t length, slot_write callback) {
|
JsonRpc::process(const char* in_buffer, uint32_t length, slot_write callback) {
|
||||||
json response;
|
json response;
|
||||||
json body;
|
json body;
|
||||||
|
|
||||||
if (length > m_size_limit) {
|
|
||||||
auto err_str = json_error(JSONRPC_INVALID_REQUEST_ERROR, "content size exceeds maximum RPC limit", nullptr).dump();
|
|
||||||
|
|
||||||
return callback(err_str.c_str(), err_str.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
json_depth_limited_parser handler(body);
|
body = json::parse(in_buffer, in_buffer + length);
|
||||||
|
|
||||||
if (!json::sax_parse(in_buffer, in_buffer + length, &handler)) {
|
|
||||||
auto err_str = json_error(JSONRPC_INVALID_REQUEST_ERROR, "maximum nesting depth exceeded", nullptr).dump();
|
|
||||||
|
|
||||||
return callback(err_str.c_str(), err_str.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (body.type()) {
|
switch (body.type()) {
|
||||||
case json::value_t::object: {
|
case json::value_t::object: {
|
||||||
if (!body.contains("id")) {
|
if (!body.contains("id")) {
|
||||||
@@ -303,17 +256,13 @@ JsonRpc::process(const char* in_buffer, uint32_t length, slot_write callback) {
|
|||||||
|
|
||||||
std::string response_str = response.dump();
|
std::string response_str = response.dump();
|
||||||
|
|
||||||
if (response_str.size() > SCgiTask::max_response_size) {
|
|
||||||
const auto& id = response.is_object() && response.contains("id") ? response["id"] : json(nullptr);
|
|
||||||
auto err_str = json_error(JSONRPC_INTERNAL_ERROR, "response size exceeds maximum RPC limit", id).dump();
|
|
||||||
|
|
||||||
return callback(err_str.c_str(), err_str.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback(response_str.c_str(), response_str.size());
|
return callback(response_str.c_str(), response_str.size());
|
||||||
|
|
||||||
} catch (json::exception& e) {
|
} catch (json::parse_error& e) {
|
||||||
// Exception strings may contain invalid UTF-8, hence the ::replace
|
auto err_str = json_error(JSONRPC_PARSE_ERROR, e.what(), nullptr).dump(-1, ' ', false, json::error_handler_t::replace);
|
||||||
|
return callback(err_str.c_str(), err_str.size());
|
||||||
|
} catch (json::type_error& e) {
|
||||||
|
// Type errors may be caused by invalid UTF-8 strings in exception strings, hence the ::replace
|
||||||
auto err_str = json_error(JSONRPC_PARSE_ERROR, e.what(), nullptr).dump(-1, ' ', false, json::error_handler_t::replace);
|
auto err_str = json_error(JSONRPC_PARSE_ERROR, e.what(), nullptr).dump(-1, ' ', false, json::error_handler_t::replace);
|
||||||
return callback(err_str.c_str(), err_str.size());
|
return callback(err_str.c_str(), err_str.size());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "rpc/scgi_task.h"
|
|
||||||
|
|
||||||
namespace rpc {
|
namespace rpc {
|
||||||
|
|
||||||
class JsonRpc {
|
class JsonRpc {
|
||||||
@@ -19,11 +17,6 @@ public:
|
|||||||
bool process(const char* in_buffer, uint32_t length, slot_write callback);
|
bool process(const char* in_buffer, uint32_t length, slot_write callback);
|
||||||
|
|
||||||
void insert_command(const char* name, const char* parm, const char* doc) {};
|
void insert_command(const char* name, const char* parm, const char* doc) {};
|
||||||
|
|
||||||
void set_size_limit(uint64_t size) { m_size_limit = size; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint64_t m_size_limit{SCgiTask::max_content_size};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace rpc
|
} // namespace rpc
|
||||||
|
|||||||
@@ -394,10 +394,6 @@ execute_lua(LuaEngine* engine, rpc::target_type target_type, torrent::Object con
|
|||||||
switch (raw_args.type()) {
|
switch (raw_args.type()) {
|
||||||
case torrent::Object::TYPE_LIST: {
|
case torrent::Object::TYPE_LIST: {
|
||||||
const torrent::Object::list_type& args = raw_args.as_list();
|
const torrent::Object::list_type& args = raw_args.as_list();
|
||||||
|
|
||||||
if (args.empty())
|
|
||||||
throw torrent::input_error("Too few arguments.");
|
|
||||||
|
|
||||||
if (flags & LuaEngine::flag_string) {
|
if (flags & LuaEngine::flag_string) {
|
||||||
check_lua_status(l_state, luaL_loadstring(l_state, args.begin()->as_string().c_str()));
|
check_lua_status(l_state, luaL_loadstring(l_state, args.begin()->as_string().c_str()));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -26,7 +26,10 @@ const size_t object_storage::key_size;
|
|||||||
|
|
||||||
object_storage::iterator
|
object_storage::iterator
|
||||||
object_storage::find_raw_string(const torrent::raw_string& key) {
|
object_storage::find_raw_string(const torrent::raw_string& key) {
|
||||||
return find(key_type::from_raw_string(key));
|
fixed_key_type<64> k;
|
||||||
|
k.set_c_str(key.data());
|
||||||
|
|
||||||
|
return find(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
object_storage::iterator
|
object_storage::iterator
|
||||||
@@ -61,12 +64,8 @@ object_storage::insert(const char* key_data, uint32_t key_size, const torrent::O
|
|||||||
if (std::find(key_data, key_data + key_size, '\0') != key_data + key_size)
|
if (std::find(key_data, key_data + key_size, '\0') != key_data + key_size)
|
||||||
throw torrent::input_error("Found nul-char in string.");
|
throw torrent::input_error("Found nul-char in string.");
|
||||||
|
|
||||||
// key_type turns a key this long into the empty key.
|
// Check for size > key_size.
|
||||||
if (key_size >= object_storage::key_size)
|
// Check for empty string.
|
||||||
throw torrent::input_error("Key is too long.");
|
|
||||||
|
|
||||||
if (key_size == 0)
|
|
||||||
throw torrent::input_error("Key is empty.");
|
|
||||||
|
|
||||||
bool use_raw = false;
|
bool use_raw = false;
|
||||||
torrent::Object object;
|
torrent::Object object;
|
||||||
|
|||||||
@@ -158,7 +158,14 @@ fixed_key_type<MaxSize>::set_data(const value_type* src_data, size_type src_size
|
|||||||
|
|
||||||
template <size_t MaxSize> inline void
|
template <size_t MaxSize> inline void
|
||||||
fixed_key_type<MaxSize>::set_c_str(const value_type* src_data) {
|
fixed_key_type<MaxSize>::set_c_str(const value_type* src_data) {
|
||||||
set_c_str(src_data, std::strlen(src_data));
|
value_type* itr = m_data;
|
||||||
|
const value_type* last = m_data + max_size;
|
||||||
|
|
||||||
|
while (itr != last && *src_data != '\0')
|
||||||
|
*itr++ = *src_data++;
|
||||||
|
|
||||||
|
*itr = '\0';
|
||||||
|
m_size = std::distance(m_data, itr);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t MaxSize> inline void
|
template <size_t MaxSize> inline void
|
||||||
|
|||||||
+24
-73
@@ -1,10 +1,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <cctype>
|
|
||||||
#include <charconv>
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <limits>
|
|
||||||
#include <locale>
|
#include <locale>
|
||||||
#include <torrent/exceptions.h>
|
#include <torrent/exceptions.h>
|
||||||
|
|
||||||
@@ -13,8 +10,6 @@
|
|||||||
|
|
||||||
namespace rpc {
|
namespace rpc {
|
||||||
|
|
||||||
constexpr uint32_t max_parse_depth = 1024;
|
|
||||||
|
|
||||||
const char*
|
const char*
|
||||||
parse_skip_wspace(const char* first, const char* last) {
|
parse_skip_wspace(const char* first, const char* last) {
|
||||||
while (first != last && parse_is_space(*first))
|
while (first != last && parse_is_space(*first))
|
||||||
@@ -104,56 +99,21 @@ parse_whole_value_nothrow(const char* src, int64_t* value, int base, int unit) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
value_fits_shifted(int64_t value, int shift) {
|
|
||||||
return value <= (std::numeric_limits<int64_t>::max() >> shift) &&
|
|
||||||
value >= (std::numeric_limits<int64_t>::min() >> shift);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char*
|
const char*
|
||||||
parse_value_nothrow(const char* src, int64_t* value, int base, int unit) {
|
parse_value_nothrow(const char* src, int64_t* value, int base, int unit) {
|
||||||
if (unit <= 0)
|
if (unit <= 0)
|
||||||
throw torrent::input_error("Command::string_to_value_unit(...) received unit <= 0.");
|
throw torrent::input_error("Command::string_to_value_unit(...) received unit <= 0.");
|
||||||
|
|
||||||
if (base != 0 && base != 8 && base != 10 && base != 16)
|
char* last;
|
||||||
throw torrent::input_error("Command::string_to_value_unit(...) received invalid base.");
|
|
||||||
|
|
||||||
const char* first = src;
|
|
||||||
|
|
||||||
while (parse_is_space(*src))
|
|
||||||
src++;
|
|
||||||
|
|
||||||
if (src[0] == '+')
|
|
||||||
return first;
|
|
||||||
|
|
||||||
if (src[0] == '-') {
|
|
||||||
if (base == 8 || base == 16)
|
|
||||||
return first;
|
|
||||||
|
|
||||||
if (src[1] == '0')
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (base == 10 && src[0] == '0' && (src[1] >= '0' && src[1] <= '9'))
|
|
||||||
return first;
|
|
||||||
|
|
||||||
char* last{};
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
*value = strtoll(src, &last, base);
|
*value = strtoll(src, &last, base);
|
||||||
|
|
||||||
if (errno == ERANGE)
|
|
||||||
return first;
|
|
||||||
|
|
||||||
if (last == src) {
|
if (last == src) {
|
||||||
*value = 0;
|
|
||||||
|
|
||||||
if (strcasecmp(src, "no") == 0) { *value = 0; return src + strlen("no"); }
|
if (strcasecmp(src, "no") == 0) { *value = 0; return src + strlen("no"); }
|
||||||
if (strcasecmp(src, "yes") == 0) { *value = 1; return src + strlen("yes"); }
|
if (strcasecmp(src, "yes") == 0) { *value = 1; return src + strlen("yes"); }
|
||||||
if (strcasecmp(src, "true") == 0) { *value = 1; return src + strlen("true"); }
|
if (strcasecmp(src, "true") == 0) { *value = 1; return src + strlen("true"); }
|
||||||
if (strcasecmp(src, "false") == 0) { *value = 0; return src + strlen("false"); }
|
if (strcasecmp(src, "false") == 0) { *value = 0; return src + strlen("false"); }
|
||||||
|
|
||||||
return first;
|
return src;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (*last) {
|
switch (*last) {
|
||||||
@@ -161,26 +121,20 @@ parse_value_nothrow(const char* src, int64_t* value, int base, int unit) {
|
|||||||
case 'B': ++last; break;
|
case 'B': ++last; break;
|
||||||
case 'k':
|
case 'k':
|
||||||
case 'K':
|
case 'K':
|
||||||
if (!value_fits_shifted(*value, 10)) return first; // overflow guard
|
if (*value > (int64_t)0x1FFFFFFFFFFFFF) return src; // overflow guard
|
||||||
*value = *value << 10; ++last; break;
|
*value = *value << 10; ++last; break;
|
||||||
case 'm':
|
case 'm':
|
||||||
case 'M':
|
case 'M':
|
||||||
if (!value_fits_shifted(*value, 20)) return first; // overflow guard
|
if (*value > (int64_t)0x7FFFFFFFFFF) return src; // overflow guard
|
||||||
*value = *value << 20; ++last; break;
|
*value = *value << 20; ++last; break;
|
||||||
case 'g':
|
case 'g':
|
||||||
case 'G':
|
case 'G':
|
||||||
if (!value_fits_shifted(*value, 30)) return first; // overflow guard
|
if (*value > (int64_t)0x1FFFFFFFF) return src; // overflow guard
|
||||||
*value = *value << 30; ++last; break;
|
*value = *value << 30; ++last; break;
|
||||||
// case ' ':
|
// case ' ':
|
||||||
// case '\0': *value = *value * unit; break;
|
// case '\0': *value = *value * unit; break;
|
||||||
// default: throw torrent::input_error("Could not parse value.");
|
// default: throw torrent::input_error("Could not parse value.");
|
||||||
default:
|
default: *value = *value * unit; break;
|
||||||
if (*value > std::numeric_limits<int64_t>::max() / unit ||
|
|
||||||
*value < std::numeric_limits<int64_t>::min() / unit)
|
|
||||||
return first; // overflow guard
|
|
||||||
|
|
||||||
*value = *value * unit;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return last;
|
return last;
|
||||||
@@ -188,13 +142,10 @@ parse_value_nothrow(const char* src, int64_t* value, int base, int unit) {
|
|||||||
|
|
||||||
// Somewhat ugly...
|
// Somewhat ugly...
|
||||||
const char*
|
const char*
|
||||||
parse_object(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char), uint32_t depth) {
|
parse_object(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char)) {
|
||||||
if (++depth >= max_parse_depth)
|
if (*first == '{') {
|
||||||
throw torrent::input_error("Max parse depth reached.");
|
|
||||||
|
|
||||||
if (first != last && *first == '{') {
|
|
||||||
*dest = torrent::Object::create_list();
|
*dest = torrent::Object::create_list();
|
||||||
first = parse_list(first + 1, last, dest, &parse_is_delim_block, depth);
|
first = parse_list(first + 1, last, dest, &parse_is_delim_block);
|
||||||
first = parse_skip_wspace(first, last);
|
first = parse_skip_wspace(first, last);
|
||||||
|
|
||||||
if (first == last || *first != '}')
|
if (first == last || *first != '}')
|
||||||
@@ -202,19 +153,19 @@ parse_object(const char* first, const char* last, torrent::Object* dest, bool (*
|
|||||||
|
|
||||||
return ++first;
|
return ++first;
|
||||||
|
|
||||||
} else if (first != last && *first == '(') {
|
} else if (*first == '(') {
|
||||||
int32_t parentheses = 1;
|
int32_t depth = 1;
|
||||||
|
|
||||||
while (first + 1 != last && *(first + 1) == '(') {
|
while (first + 1 != last && *(first + 1) == '(') {
|
||||||
first++;
|
first++;
|
||||||
parentheses++;
|
depth++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parentheses > 3)
|
if (depth > 3)
|
||||||
throw torrent::input_error("Max 3 parentheses per object allowed.");
|
throw torrent::input_error("Max 3 parentheses per object allowed.");
|
||||||
|
|
||||||
*dest = torrent::Object::create_dict_key();
|
*dest = torrent::Object::create_dict_key();
|
||||||
dest->set_flags(torrent::Object::flag_function << (parentheses - 1));
|
dest->set_flags(torrent::Object::flag_function << (depth - 1));
|
||||||
|
|
||||||
first = parse_string(first + 1, last, &dest->as_dict_key(), &parse_is_delim_func);
|
first = parse_string(first + 1, last, &dest->as_dict_key(), &parse_is_delim_func);
|
||||||
first = parse_skip_wspace(first, last);
|
first = parse_skip_wspace(first, last);
|
||||||
@@ -225,16 +176,16 @@ parse_object(const char* first, const char* last, torrent::Object* dest, bool (*
|
|||||||
if (*first == ',') {
|
if (*first == ',') {
|
||||||
// This will always create a list even for single argument functions...
|
// This will always create a list even for single argument functions...
|
||||||
dest->as_dict_obj() = torrent::Object::create_list();
|
dest->as_dict_obj() = torrent::Object::create_list();
|
||||||
first = parse_list(first + 1, last, &dest->as_dict_obj(), &parse_is_delim_func, depth);
|
first = parse_list(first + 1, last, &dest->as_dict_obj(), &parse_is_delim_func);
|
||||||
first = parse_skip_wspace(first, last);
|
first = parse_skip_wspace(first, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (parentheses != 0 && first != last && *first == ')') {
|
while (depth != 0 && first != last && *first == ')') {
|
||||||
first++;
|
first++;
|
||||||
parentheses--;
|
depth--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parentheses != 0)
|
if (depth != 0)
|
||||||
throw torrent::input_error("Parentheses mismatch.");
|
throw torrent::input_error("Parentheses mismatch.");
|
||||||
|
|
||||||
return first;
|
return first;
|
||||||
@@ -247,7 +198,7 @@ parse_object(const char* first, const char* last, torrent::Object* dest, bool (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char*
|
const char*
|
||||||
parse_list(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char), uint32_t depth) {
|
parse_list(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char)) {
|
||||||
if (!dest->is_list())
|
if (!dest->is_list())
|
||||||
throw torrent::internal_error("parse_list(...) !dest->is_list().");
|
throw torrent::internal_error("parse_list(...) !dest->is_list().");
|
||||||
|
|
||||||
@@ -255,7 +206,7 @@ parse_list(const char* first, const char* last, torrent::Object* dest, bool (*de
|
|||||||
torrent::Object tmp;
|
torrent::Object tmp;
|
||||||
|
|
||||||
first = parse_skip_wspace(first, last);
|
first = parse_skip_wspace(first, last);
|
||||||
first = parse_object(first, last, &tmp, delim, depth);
|
first = parse_object(first, last, &tmp, delim);
|
||||||
first = parse_skip_wspace(first, last);
|
first = parse_skip_wspace(first, last);
|
||||||
|
|
||||||
dest->as_list().push_back(tmp);
|
dest->as_list().push_back(tmp);
|
||||||
@@ -270,9 +221,9 @@ parse_list(const char* first, const char* last, torrent::Object* dest, bool (*de
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char*
|
const char*
|
||||||
parse_whole_list(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char), uint32_t depth) {
|
parse_whole_list(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char)) {
|
||||||
first = parse_skip_wspace(first, last);
|
first = parse_skip_wspace(first, last);
|
||||||
first = parse_object(first, last, dest, delim, depth);
|
first = parse_object(first, last, dest, delim);
|
||||||
first = parse_skip_wspace(first, last);
|
first = parse_skip_wspace(first, last);
|
||||||
|
|
||||||
if (first != last && parse_is_seperator(*first)) {
|
if (first != last && parse_is_seperator(*first)) {
|
||||||
@@ -280,7 +231,7 @@ parse_whole_list(const char* first, const char* last, torrent::Object* dest, boo
|
|||||||
tmp.swap(*dest);
|
tmp.swap(*dest);
|
||||||
|
|
||||||
dest->as_list().push_back(tmp);
|
dest->as_list().push_back(tmp);
|
||||||
first = parse_list(++first, last, dest, delim, depth);
|
first = parse_list(++first, last, dest, delim);
|
||||||
}
|
}
|
||||||
|
|
||||||
return first;
|
return first;
|
||||||
@@ -424,7 +375,7 @@ convert_to_value_nothrow(const torrent::Object& src, int64_t* value, int base, i
|
|||||||
== unpacked.as_string().c_str() + unpacked.as_string().size();
|
== unpacked.as_string().c_str() + unpacked.as_string().size();
|
||||||
|
|
||||||
case torrent::Object::TYPE_RAW_STRING: {
|
case torrent::Object::TYPE_RAW_STRING: {
|
||||||
const torrent::raw_string& str = unpacked.as_raw_string();
|
const torrent::raw_string& str = src.as_raw_string();
|
||||||
|
|
||||||
auto buffer = std::make_unique<char[]>(str.size() + 1);
|
auto buffer = std::make_unique<char[]>(str.size() + 1);
|
||||||
std::memcpy(buffer.get(), str.data(), str.size());
|
std::memcpy(buffer.get(), str.data(), str.size());
|
||||||
|
|||||||
+37
-3
@@ -1,3 +1,37 @@
|
|||||||
|
// rTorrent - BitTorrent client
|
||||||
|
// Copyright (C) 2005-2011, Jari Sundell
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation; either version 2 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
//
|
||||||
|
// In addition, as a special exception, the copyright holders give
|
||||||
|
// permission to link the code of portions of this program with the
|
||||||
|
// OpenSSL library under certain conditions as described in each
|
||||||
|
// individual source file, and distribute linked combinations
|
||||||
|
// including the two.
|
||||||
|
//
|
||||||
|
// You must obey the GNU General Public License in all respects for
|
||||||
|
// all of the code used other than OpenSSL. If you modify file(s)
|
||||||
|
// with this exception, you may extend this exception to your version
|
||||||
|
// of the file(s), but you are not obligated to do so. If you do not
|
||||||
|
// wish to do so, delete this exception statement from your version.
|
||||||
|
// If you delete this exception statement from all source files in the
|
||||||
|
// program, then also delete it here.
|
||||||
|
//
|
||||||
|
// Contact: Jari Sundell <sundell.software@gmail.com>
|
||||||
|
|
||||||
|
|
||||||
#ifndef RTORRENT_RPC_PARSE_H
|
#ifndef RTORRENT_RPC_PARSE_H
|
||||||
#define RTORRENT_RPC_PARSE_H
|
#define RTORRENT_RPC_PARSE_H
|
||||||
|
|
||||||
@@ -43,9 +77,9 @@ const char* parse_value_nothrow(const char* first, const char* last, int64_t* va
|
|||||||
void parse_whole_value(const char* src, int64_t* value, int base = 0, int unit = 1);
|
void parse_whole_value(const char* src, int64_t* value, int base = 0, int unit = 1);
|
||||||
bool parse_whole_value_nothrow(const char* src, int64_t* value, int base = 0, int unit = 1);
|
bool parse_whole_value_nothrow(const char* src, int64_t* value, int base = 0, int unit = 1);
|
||||||
|
|
||||||
const char* parse_object (const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char) = &parse_is_delim_default, uint32_t depth = 0);
|
const char* parse_object (const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char) = &parse_is_delim_default);
|
||||||
const char* parse_list (const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char) = &parse_is_delim_default, uint32_t depth = 0);
|
const char* parse_list (const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char) = &parse_is_delim_default);
|
||||||
const char* parse_whole_list(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char) = &parse_is_delim_default, uint32_t depth = 0);
|
const char* parse_whole_list(const char* first, const char* last, torrent::Object* dest, bool (*delim)(const char) = &parse_is_delim_default);
|
||||||
|
|
||||||
std::string convert_to_string(const torrent::Object& src);
|
std::string convert_to_string(const torrent::Object& src);
|
||||||
|
|
||||||
|
|||||||
@@ -181,11 +181,6 @@ parse_command_file(const std::string& path) {
|
|||||||
getCount = 0;
|
getCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.fail() && !file.eof()) {
|
|
||||||
lineNumber++;
|
|
||||||
throw torrent::input_error("Exceeded max line length.");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (torrent::input_error& e) {
|
} catch (torrent::input_error& e) {
|
||||||
snprintf(buffer, 2048, "Error in option file: %s:%u: %s", path.c_str(), lineNumber, e.what());
|
snprintf(buffer, 2048, "Error in option file: %s:%u: %s", path.c_str(), lineNumber, e.what());
|
||||||
|
|
||||||
|
|||||||
+34
-15
@@ -113,20 +113,25 @@ bool
|
|||||||
RpcManager::process(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback) {
|
RpcManager::process(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case RPCType::XML:
|
case RPCType::XML:
|
||||||
if (!m_xmlrpc.is_valid() || !m_use_xmlrpc) {
|
// TODO: 'network.rpc.use_xmlrpc' should be a bool in RpcManager, not a command variable.
|
||||||
|
if (m_xmlrpc.is_valid() && rpc::call_command_value("network.rpc.use_xmlrpc")) {
|
||||||
|
return m_xmlrpc.process(in_buffer, length, callback);
|
||||||
|
|
||||||
|
} else {
|
||||||
const std::string response = "<?xml version=\"1.0\"?><methodResponse><fault><value><struct><member><name>faultCode</name><value><i8>-501</i8></value></member><member><name>faultString</name><value><string>XML-RPC not supported</string></value></member></struct></value></fault></methodResponse>";
|
const std::string response = "<?xml version=\"1.0\"?><methodResponse><fault><value><struct><member><name>faultCode</name><value><i8>-501</i8></value></member><member><name>faultString</name><value><string>XML-RPC not supported</string></value></member></struct></value></fault></methodResponse>";
|
||||||
return callback(response.c_str(), response.size());
|
return callback(response.c_str(), response.size());
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
return m_xmlrpc.process(in_buffer, length, callback);
|
|
||||||
|
|
||||||
case RPCType::JSON:
|
case RPCType::JSON:
|
||||||
if (!m_use_jsonrpc) {
|
if (rpc::call_command_value("network.rpc.use_jsonrpc")) {
|
||||||
|
return m_jsonrpc.process(in_buffer, length, callback);
|
||||||
|
|
||||||
|
} else {
|
||||||
const std::string response = "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32601,\"message\":\"JSON-RPC not supported\"},\"id\":null}";
|
const std::string response = "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32601,\"message\":\"JSON-RPC not supported\"},\"id\":null}";
|
||||||
return callback(response.c_str(), response.size());
|
return callback(response.c_str(), response.size());
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
return m_jsonrpc.process(in_buffer, length, callback);
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw torrent::input_error("invalid parameters: unknown RPC type");
|
throw torrent::input_error("invalid parameters: unknown RPC type");
|
||||||
@@ -167,16 +172,30 @@ RpcManager::cleanup() {
|
|||||||
m_jsonrpc.cleanup();
|
m_jsonrpc.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
RpcManager::is_type_enabled(RPCType type) const {
|
||||||
|
switch (type) {
|
||||||
|
case RPCType::XML:
|
||||||
|
return m_is_xmlrpc_enabled;
|
||||||
|
case RPCType::JSON:
|
||||||
|
return m_is_jsonrpc_enabled;
|
||||||
|
default:
|
||||||
|
throw torrent::input_error("invalid parameters: unknown RPC type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RpcManager::set_size_limit(uint64_t size) {
|
RpcManager::set_type_enabled(RPCType type, bool enabled) {
|
||||||
if (size > SCgiTask::max_content_size)
|
switch (type) {
|
||||||
throw torrent::input_error("XMLRPC size limit cannot exceed the SCGI content size limit.");
|
case RPCType::XML:
|
||||||
|
m_is_xmlrpc_enabled = enabled;
|
||||||
if (size < min_size_limit)
|
break;
|
||||||
throw torrent::input_error("XMLRPC size limit is too small to hold a request.");
|
case RPCType::JSON:
|
||||||
|
m_is_jsonrpc_enabled = enabled;
|
||||||
m_xmlrpc.set_size_limit(size);
|
break;
|
||||||
m_jsonrpc.set_size_limit(size);
|
default:
|
||||||
|
throw torrent::input_error("invalid parameters: unknown RPC type");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
+5
-18
@@ -53,10 +53,6 @@ public:
|
|||||||
enum RPCType { XML,
|
enum RPCType { XML,
|
||||||
JSON };
|
JSON };
|
||||||
|
|
||||||
// A limit below the shortest useful request rejects every request, the one
|
|
||||||
// that would raise it again included.
|
|
||||||
static constexpr uint64_t min_size_limit = 1024;
|
|
||||||
|
|
||||||
RpcManager() = default;
|
RpcManager() = default;
|
||||||
~RpcManager() = default;
|
~RpcManager() = default;
|
||||||
|
|
||||||
@@ -66,16 +62,13 @@ public:
|
|||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
int64_t size_limit() { return m_xmlrpc.size_limit(); };
|
int64_t size_limit() { return m_xmlrpc.size_limit(); };
|
||||||
void set_size_limit(uint64_t size);
|
void set_size_limit(uint64_t size) { m_xmlrpc.set_size_limit(size); };
|
||||||
|
|
||||||
int dialect() { return m_xmlrpc.dialect(); }
|
int dialect() { return m_xmlrpc.dialect(); }
|
||||||
void set_dialect(int dialect) { m_xmlrpc.set_dialect(dialect); }
|
void set_dialect(int dialect) { m_xmlrpc.set_dialect(dialect); }
|
||||||
|
|
||||||
bool use_xmlrpc() const;
|
bool is_type_enabled(RPCType type) const;
|
||||||
void set_use_xmlrpc(bool v);
|
void set_type_enabled(RPCType type, bool enabled);
|
||||||
|
|
||||||
bool use_jsonrpc() const;
|
|
||||||
void set_use_jsonrpc(bool v);
|
|
||||||
|
|
||||||
bool process(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback);
|
bool process(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback);
|
||||||
bool process_untrusted(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback);
|
bool process_untrusted(RPCType type, const char* in_buffer, uint32_t length, slot_response_callback callback);
|
||||||
@@ -109,9 +102,8 @@ private:
|
|||||||
JsonRpc m_jsonrpc;
|
JsonRpc m_jsonrpc;
|
||||||
|
|
||||||
bool m_handlers_initialized{};
|
bool m_handlers_initialized{};
|
||||||
|
bool m_is_jsonrpc_enabled{true};
|
||||||
bool m_use_jsonrpc{true};
|
bool m_is_xmlrpc_enabled{true};
|
||||||
bool m_use_xmlrpc{true};
|
|
||||||
|
|
||||||
std::atomic<bool> m_scgi_allow_compression{true};
|
std::atomic<bool> m_scgi_allow_compression{true};
|
||||||
std::atomic<unsigned int> m_scgi_min_compress_size{1000};
|
std::atomic<unsigned int> m_scgi_min_compress_size{1000};
|
||||||
@@ -124,11 +116,6 @@ private:
|
|||||||
|
|
||||||
extern RpcManager rpc;
|
extern RpcManager rpc;
|
||||||
|
|
||||||
inline bool RpcManager::use_xmlrpc() const { return m_use_xmlrpc; }
|
|
||||||
inline void RpcManager::set_use_xmlrpc(bool v) { m_use_xmlrpc = v; }
|
|
||||||
inline bool RpcManager::use_jsonrpc() const { return m_use_jsonrpc; }
|
|
||||||
inline void RpcManager::set_use_jsonrpc(bool v) { m_use_jsonrpc = v; }
|
|
||||||
|
|
||||||
} // namespace rpc
|
} // namespace rpc
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+2
-2
@@ -3,13 +3,13 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <torrent/system/event.h>
|
#include <torrent/event.h>
|
||||||
|
|
||||||
#include "rpc/scgi_task.h"
|
#include "rpc/scgi_task.h"
|
||||||
|
|
||||||
namespace rpc {
|
namespace rpc {
|
||||||
|
|
||||||
class SCgi : public torrent::system::Event {
|
class SCgi : public torrent::Event {
|
||||||
public:
|
public:
|
||||||
static const int max_tasks = 100;
|
static const int max_tasks = 100;
|
||||||
|
|
||||||
|
|||||||
+3
-19
@@ -14,7 +14,6 @@
|
|||||||
#include <torrent/runtime/socket_manager.h>
|
#include <torrent/runtime/socket_manager.h>
|
||||||
#include <torrent/system/callbacks.h>
|
#include <torrent/system/callbacks.h>
|
||||||
#include <torrent/system/poll.h>
|
#include <torrent/system/poll.h>
|
||||||
#include <torrent/system/scheduler.h>
|
|
||||||
#include <torrent/utils/log.h>
|
#include <torrent/utils/log.h>
|
||||||
|
|
||||||
#include "control.h"
|
#include "control.h"
|
||||||
@@ -28,8 +27,6 @@ namespace rpc {
|
|||||||
SCgiTask::SCgiTask()
|
SCgiTask::SCgiTask()
|
||||||
: m_callback_id(torrent::system::make_callback_id()) {
|
: m_callback_id(torrent::system::make_callback_id()) {
|
||||||
|
|
||||||
m_task_timeout.slot() = [this]() { close(); };
|
|
||||||
|
|
||||||
reset_file_descriptor();
|
reset_file_descriptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,8 +50,6 @@ SCgiTask::open(SCgi* parent, int fd) {
|
|||||||
torrent::this_thread::poll()->open(this);
|
torrent::this_thread::poll()->open(this);
|
||||||
torrent::this_thread::poll()->insert_read(this);
|
torrent::this_thread::poll()->insert_read(this);
|
||||||
|
|
||||||
torrent::this_thread::scheduler()->update_wait_for_ceil_seconds(&m_task_timeout, timeout_request);
|
|
||||||
|
|
||||||
auto lock = std::lock_guard<std::mutex>(m_result_mutex);
|
auto lock = std::lock_guard<std::mutex>(m_result_mutex);
|
||||||
|
|
||||||
// Leave room for terminating nul byte for parsing the header.
|
// Leave room for terminating nul byte for parsing the header.
|
||||||
@@ -66,8 +61,6 @@ SCgiTask::cancel_open() {
|
|||||||
if (!is_open())
|
if (!is_open())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
torrent::this_thread::scheduler()->erase(&m_task_timeout);
|
|
||||||
|
|
||||||
torrent::this_thread::poll()->remove_and_close(this);
|
torrent::this_thread::poll()->remove_and_close(this);
|
||||||
|
|
||||||
torrent::fd_close(file_descriptor());
|
torrent::fd_close(file_descriptor());
|
||||||
@@ -79,8 +72,6 @@ SCgiTask::close() {
|
|||||||
if (!is_open())
|
if (!is_open())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
torrent::this_thread::scheduler()->erase(&m_task_timeout);
|
|
||||||
|
|
||||||
torrent::system::cancel_callback_and_wait(m_callback_id, scgi_thread::thread(), torrent::main_thread::thread());
|
torrent::system::cancel_callback_and_wait(m_callback_id, scgi_thread::thread(), torrent::main_thread::thread());
|
||||||
|
|
||||||
torrent::runtime::socket_manager()->close_event_or_throw(this, [this]() {
|
torrent::runtime::socket_manager()->close_event_or_throw(this, [this]() {
|
||||||
@@ -93,8 +84,7 @@ SCgiTask::close() {
|
|||||||
// The callbacks are guaranteed to be finished/canceled at this point.
|
// The callbacks are guaranteed to be finished/canceled at this point.
|
||||||
auto lock = std::lock_guard<std::mutex>(m_result_mutex);
|
auto lock = std::lock_guard<std::mutex>(m_result_mutex);
|
||||||
|
|
||||||
// clear() would keep the capacity, and the task is pooled for the lifetime of the process.
|
m_buffer.clear();
|
||||||
std::vector<char>().swap(m_buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -104,12 +94,8 @@ SCgiTask::event_read() {
|
|||||||
if (m_content_length == 0)
|
if (m_content_length == 0)
|
||||||
read_length--;
|
read_length--;
|
||||||
|
|
||||||
if (read_length <= 0) {
|
if (read_length <= 0)
|
||||||
if (m_content_length == 0)
|
|
||||||
return close();
|
|
||||||
|
|
||||||
throw torrent::internal_error("SCgiTask::event_read() no space in buffer for event_read.");
|
throw torrent::internal_error("SCgiTask::event_read() no space in buffer for event_read.");
|
||||||
}
|
|
||||||
|
|
||||||
int bytes = ::recv(file_descriptor(), m_buffer.data() + m_position, read_length, 0);
|
int bytes = ::recv(file_descriptor(), m_buffer.data() + m_position, read_length, 0);
|
||||||
|
|
||||||
@@ -168,8 +154,6 @@ SCgiTask::event_read() {
|
|||||||
|
|
||||||
torrent::this_thread::poll()->remove_read(this);
|
torrent::this_thread::poll()->remove_read(this);
|
||||||
|
|
||||||
torrent::this_thread::scheduler()->update_wait_for_ceil_seconds(&m_task_timeout, timeout_request);
|
|
||||||
|
|
||||||
if (m_parent->log_fd() >= 0) {
|
if (m_parent->log_fd() >= 0) {
|
||||||
[[maybe_unused]] int result;
|
[[maybe_unused]] int result;
|
||||||
|
|
||||||
@@ -405,7 +389,7 @@ void
|
|||||||
SCgiTask::receive_write(const char* buffer, uint32_t length) {
|
SCgiTask::receive_write(const char* buffer, uint32_t length) {
|
||||||
assert(torrent::this_thread::thread() == torrent::main_thread::thread());
|
assert(torrent::this_thread::thread() == torrent::main_thread::thread());
|
||||||
|
|
||||||
if (buffer == nullptr || length > max_response_size)
|
if (buffer == nullptr || length > (100 << 20))
|
||||||
throw torrent::internal_error("SCgiTask::receive_write(...) received bad input.");
|
throw torrent::internal_error("SCgiTask::receive_write(...) received bad input.");
|
||||||
|
|
||||||
// Main thread callback already locked this mutex.
|
// Main thread callback already locked this mutex.
|
||||||
|
|||||||
+3
-11
@@ -1,27 +1,20 @@
|
|||||||
#ifndef RTORRENT_RPC_SCGI_TASK_H
|
#ifndef RTORRENT_RPC_SCGI_TASK_H
|
||||||
#define RTORRENT_RPC_SCGI_TASK_H
|
#define RTORRENT_RPC_SCGI_TASK_H
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <torrent/system/event.h>
|
#include <torrent/event.h>
|
||||||
#include <torrent/system/scheduler.h>
|
|
||||||
|
|
||||||
namespace rpc {
|
namespace rpc {
|
||||||
|
|
||||||
class SCgi;
|
class SCgi;
|
||||||
|
|
||||||
class SCgiTask : public torrent::system::Event {
|
class SCgiTask : public torrent::Event {
|
||||||
public:
|
public:
|
||||||
static constexpr int default_buffer_size = 8191;
|
static constexpr int default_buffer_size = 8191;
|
||||||
static constexpr int max_header_size = 2000;
|
static constexpr int max_header_size = 2000;
|
||||||
static constexpr int max_content_size = (1 << 26);
|
static constexpr int max_content_size = (2 << 23);
|
||||||
|
|
||||||
static constexpr uint32_t max_response_size = (100 << 20);
|
|
||||||
|
|
||||||
static constexpr auto timeout_request = std::chrono::seconds(60);
|
|
||||||
|
|
||||||
enum ContentType { XML, JSON };
|
enum ContentType { XML, JSON };
|
||||||
|
|
||||||
@@ -63,7 +56,6 @@ private:
|
|||||||
|
|
||||||
SCgi* m_parent{};
|
SCgi* m_parent{};
|
||||||
torrent::system::callback_id m_callback_id;
|
torrent::system::callback_id m_callback_id;
|
||||||
torrent::system::SchedulerEntry m_task_timeout;
|
|
||||||
|
|
||||||
std::mutex m_result_mutex;
|
std::mutex m_result_mutex;
|
||||||
|
|
||||||
|
|||||||
+11
-16
@@ -27,11 +27,11 @@ public:
|
|||||||
virtual ~xmlrpc_error_c() throw() {}
|
virtual ~xmlrpc_error_c() throw() {}
|
||||||
|
|
||||||
virtual int type() const throw() { return m_type; }
|
virtual int type() const throw() { return m_type; }
|
||||||
virtual const char* what() const throw() { return m_msg.c_str(); }
|
virtual const char* what() const throw() { return m_msg; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_type;
|
int m_type;
|
||||||
std::string m_msg;
|
const char* m_msg;
|
||||||
};
|
};
|
||||||
|
|
||||||
torrent::Object xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value, int call_type = 0, rpc::target_type* target = NULL, std::function<void()>* deleter = NULL);
|
torrent::Object xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value, int call_type = 0, rpc::target_type* target = NULL, std::function<void()>* deleter = NULL);
|
||||||
@@ -44,9 +44,10 @@ xmlrpc_list_entry_to_object(xmlrpc_env* env, xmlrpc_value* src, int index) {
|
|||||||
if (env->fault_occurred)
|
if (env->fault_occurred)
|
||||||
throw xmlrpc_error_c(env);
|
throw xmlrpc_error_c(env);
|
||||||
|
|
||||||
utils::scope_guard guard([tmp]() { xmlrpc_DECREF(tmp); });
|
torrent::Object obj = xmlrpc_to_object(env, tmp);
|
||||||
|
xmlrpc_DECREF(tmp);
|
||||||
|
|
||||||
return xmlrpc_to_object(env, tmp);
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t
|
int64_t
|
||||||
@@ -76,18 +77,16 @@ xmlrpc_list_entry_to_value(xmlrpc_env* env, xmlrpc_value* src, int index) {
|
|||||||
{
|
{
|
||||||
const char* str;
|
const char* str;
|
||||||
xmlrpc_read_string(env, tmp, &str);
|
xmlrpc_read_string(env, tmp, &str);
|
||||||
xmlrpc_DECREF(tmp);
|
|
||||||
|
|
||||||
if (env->fault_occurred)
|
if (env->fault_occurred)
|
||||||
throw xmlrpc_error_c(env);
|
throw xmlrpc_error_c(env);
|
||||||
|
|
||||||
const char* end = str;
|
const char* end = str;
|
||||||
int64_t v3 = ::strtoll(str, (char**)&end, 0);
|
int64_t v3 = ::strtoll(str, (char**)&end, 0);
|
||||||
bool invalid = *str == '\0' || *end != '\0';
|
|
||||||
|
|
||||||
::free((void*)str);
|
::free((void*)str);
|
||||||
|
|
||||||
if (invalid)
|
if (*str == '\0' || *end != '\0')
|
||||||
throw xmlrpc_error_c(XMLRPC_TYPE_ERROR, "Invalid index.");
|
throw xmlrpc_error_c(XMLRPC_TYPE_ERROR, "Invalid index.");
|
||||||
|
|
||||||
return v3;
|
return v3;
|
||||||
@@ -212,11 +211,11 @@ xmlrpc_to_object(xmlrpc_env* env, xmlrpc_value* value, int call_type, rpc::targe
|
|||||||
if (env->fault_occurred)
|
if (env->fault_occurred)
|
||||||
throw xmlrpc_error_c(env);
|
throw xmlrpc_error_c(env);
|
||||||
|
|
||||||
utils::scope_guard guard([tmp]() { xmlrpc_DECREF(tmp); });
|
|
||||||
|
|
||||||
if (target != nullptr)
|
if (target != nullptr)
|
||||||
std::tie(*target, *deleter) = xmlrpc_to_target(env, tmp, call_type);
|
std::tie(*target, *deleter) = xmlrpc_to_target(env, tmp, call_type);
|
||||||
|
|
||||||
|
xmlrpc_DECREF(tmp);
|
||||||
|
|
||||||
if (env->fault_occurred)
|
if (env->fault_occurred)
|
||||||
throw xmlrpc_error_c(env);
|
throw xmlrpc_error_c(env);
|
||||||
|
|
||||||
@@ -440,8 +439,6 @@ XmlRpc::cleanup() {
|
|||||||
xmlrpc_registry_free((xmlrpc_registry*)m_registry);
|
xmlrpc_registry_free((xmlrpc_registry*)m_registry);
|
||||||
xmlrpc_env_clean((xmlrpc_env*)m_env);
|
xmlrpc_env_clean((xmlrpc_env*)m_env);
|
||||||
delete (xmlrpc_env*)m_env;
|
delete (xmlrpc_env*)m_env;
|
||||||
m_env = nullptr;
|
|
||||||
m_registry = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@@ -454,11 +451,6 @@ XmlRpc::process(const char* inBuffer, uint32_t length, slot_write slotWrite) {
|
|||||||
if (local_env.fault_occurred && local_env.fault_code == XMLRPC_INTERNAL_ERROR)
|
if (local_env.fault_occurred && local_env.fault_code == XMLRPC_INTERNAL_ERROR)
|
||||||
throw torrent::internal_error("Internal error in XMLRPC.");
|
throw torrent::internal_error("Internal error in XMLRPC.");
|
||||||
|
|
||||||
if (memblock == nullptr) {
|
|
||||||
xmlrpc_env_clean(&local_env);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool result = slotWrite((const char*)xmlrpc_mem_block_contents(memblock),
|
bool result = slotWrite((const char*)xmlrpc_mem_block_contents(memblock),
|
||||||
xmlrpc_mem_block_size(memblock));
|
xmlrpc_mem_block_size(memblock));
|
||||||
|
|
||||||
@@ -532,6 +524,9 @@ XmlRpc::size_limit() {
|
|||||||
|
|
||||||
void
|
void
|
||||||
XmlRpc::set_size_limit(uint64_t size) {
|
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);
|
xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-46
@@ -19,7 +19,6 @@
|
|||||||
#include "rpc/tinyxml2/tinyxml2.h"
|
#include "rpc/tinyxml2/tinyxml2.h"
|
||||||
#include "rpc/rpc_manager.h"
|
#include "rpc/rpc_manager.h"
|
||||||
#include "utils/base64.h"
|
#include "utils/base64.h"
|
||||||
#include "utils/functional.h"
|
|
||||||
#include "xmlrpc.h"
|
#include "xmlrpc.h"
|
||||||
|
|
||||||
namespace rpc {
|
namespace rpc {
|
||||||
@@ -50,23 +49,13 @@ element_access(const tinyxml2::XMLElement* elem, std::initializer_list<std::stri
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char*
|
|
||||||
element_text_value(const tinyxml2::XMLNode* node) {
|
|
||||||
auto text = node->ToText();
|
|
||||||
|
|
||||||
if (text == nullptr)
|
|
||||||
throw rpc_error(XMLRPC_TYPE_ERROR, "expected a text value");
|
|
||||||
|
|
||||||
return text->Value();
|
|
||||||
}
|
|
||||||
|
|
||||||
long long
|
long long
|
||||||
element_to_int(const tinyxml2::XMLNode* elem) {
|
element_to_int(const tinyxml2::XMLNode* elem) {
|
||||||
char* pos;
|
char* pos;
|
||||||
if (elem->FirstChild() == nullptr) {
|
if (elem->FirstChild() == nullptr) {
|
||||||
throw rpc_error(XMLRPC_TYPE_ERROR, "unable to parse empty integer");
|
throw rpc_error(XMLRPC_TYPE_ERROR, "unable to parse empty integer");
|
||||||
}
|
}
|
||||||
auto str = element_text_value(elem->FirstChild());
|
auto str = elem->FirstChild()->ToText()->Value();
|
||||||
auto result = std::strtoll(str, &pos, 10);
|
auto result = std::strtoll(str, &pos, 10);
|
||||||
if (pos == str || *pos != '\0')
|
if (pos == str || *pos != '\0')
|
||||||
throw rpc_error(XMLRPC_TYPE_ERROR, "unable to parse integer value");
|
throw rpc_error(XMLRPC_TYPE_ERROR, "unable to parse integer value");
|
||||||
@@ -96,7 +85,7 @@ xml_value_to_object(const tinyxml2::XMLNode* elem) {
|
|||||||
if (child_element == nullptr)
|
if (child_element == nullptr)
|
||||||
return torrent::Object("");
|
return torrent::Object("");
|
||||||
|
|
||||||
return torrent::Object(element_text_value(child_element));
|
return torrent::Object(child_element->ToText()->Value());
|
||||||
|
|
||||||
} else if (std::strncmp(root_type, "int", sizeof("int")) == 0 ||
|
} else if (std::strncmp(root_type, "int", sizeof("int")) == 0 ||
|
||||||
std::strncmp(root_type, "i4", sizeof("i4")) == 0 ||
|
std::strncmp(root_type, "i4", sizeof("i4")) == 0 ||
|
||||||
@@ -109,7 +98,7 @@ xml_value_to_object(const tinyxml2::XMLNode* elem) {
|
|||||||
if (child_element == nullptr)
|
if (child_element == nullptr)
|
||||||
throw rpc_error(XMLRPC_TYPE_ERROR, "empty boolean element");
|
throw rpc_error(XMLRPC_TYPE_ERROR, "empty boolean element");
|
||||||
|
|
||||||
auto boolean_text = std::string(element_text_value(child_element));
|
auto boolean_text = std::string(child_element->ToText()->Value());
|
||||||
|
|
||||||
if (boolean_text == "1")
|
if (boolean_text == "1")
|
||||||
return torrent::Object((int64_t)1);
|
return torrent::Object((int64_t)1);
|
||||||
@@ -140,12 +129,7 @@ xml_value_to_object(const tinyxml2::XMLNode* elem) {
|
|||||||
if (name_element == nullptr)
|
if (name_element == nullptr)
|
||||||
throw rpc_error(XMLRPC_PARSE_ERROR, "struct member missing name element");
|
throw rpc_error(XMLRPC_PARSE_ERROR, "struct member missing name element");
|
||||||
|
|
||||||
auto name_text = name_element->GetText();
|
map[name_element->GetText()] = std::move(xml_value_to_object(child->FirstChildElement("value")));
|
||||||
|
|
||||||
if (name_text == nullptr)
|
|
||||||
throw rpc_error(XMLRPC_PARSE_ERROR, "struct member has an empty name element");
|
|
||||||
|
|
||||||
map[name_text] = std::move(xml_value_to_object(child->FirstChildElement("value")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return map_raw;
|
return map_raw;
|
||||||
@@ -156,7 +140,7 @@ xml_value_to_object(const tinyxml2::XMLNode* elem) {
|
|||||||
if (child_element == nullptr)
|
if (child_element == nullptr)
|
||||||
return torrent::Object("");
|
return torrent::Object("");
|
||||||
|
|
||||||
return torrent::Object(utils::decode_base64(utils::remove_newlines(element_text_value(child_element))));
|
return torrent::Object(utils::decode_base64(utils::remove_newlines(child_element->ToText()->Value())));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw rpc_error(XMLRPC_INTERNAL_ERROR, "received unsupported value type: " + std::string(root_type));
|
throw rpc_error(XMLRPC_INTERNAL_ERROR, "received unsupported value type: " + std::string(root_type));
|
||||||
@@ -259,8 +243,6 @@ execute_command(std::string method_name, const tinyxml2::XMLElement* params_elem
|
|||||||
torrent::Object params_raw = torrent::Object::create_list();
|
torrent::Object params_raw = torrent::Object::create_list();
|
||||||
torrent::Object::list_type& params = params_raw.as_list();
|
torrent::Object::list_type& params = params_raw.as_list();
|
||||||
rpc::target_type target = rpc::make_target();
|
rpc::target_type target = rpc::make_target();
|
||||||
std::function<void()> deleter = []() {};
|
|
||||||
utils::scope_guard guard([&deleter]() { deleter(); });
|
|
||||||
|
|
||||||
if (params_element != nullptr) {
|
if (params_element != nullptr) {
|
||||||
if (std::strncmp(params_element->Name(), "params", sizeof("params")) == 0) {
|
if (std::strncmp(params_element->Name(), "params", sizeof("params")) == 0) {
|
||||||
@@ -268,6 +250,8 @@ execute_command(std::string method_name, const tinyxml2::XMLElement* params_elem
|
|||||||
const auto* child = params_element->FirstChildElement("param");
|
const auto* child = params_element->FirstChildElement("param");
|
||||||
|
|
||||||
if (child != nullptr) {
|
if (child != nullptr) {
|
||||||
|
std::function<void()> deleter = []() {};
|
||||||
|
|
||||||
RpcManager::object_to_target(xml_value_to_object(child->FirstChildElement("value")), cmd_itr->second.m_flags, &target, &deleter);
|
RpcManager::object_to_target(xml_value_to_object(child->FirstChildElement("value")), cmd_itr->second.m_flags, &target, &deleter);
|
||||||
child = child->NextSiblingElement("param");
|
child = child->NextSiblingElement("param");
|
||||||
|
|
||||||
@@ -283,6 +267,8 @@ execute_command(std::string method_name, const tinyxml2::XMLElement* params_elem
|
|||||||
const auto* child = params_element->FirstChildElement("data")->FirstChildElement("value");
|
const auto* child = params_element->FirstChildElement("data")->FirstChildElement("value");
|
||||||
|
|
||||||
if (child != nullptr) {
|
if (child != nullptr) {
|
||||||
|
std::function<void()> deleter = []() {};
|
||||||
|
|
||||||
RpcManager::object_to_target(xml_value_to_object(child), cmd_itr->second.m_flags, &target, &deleter);
|
RpcManager::object_to_target(xml_value_to_object(child), cmd_itr->second.m_flags, &target, &deleter);
|
||||||
child = child->NextSiblingElement("value");
|
child = child->NextSiblingElement("value");
|
||||||
|
|
||||||
@@ -314,9 +300,6 @@ process_document(const tinyxml2::XMLDocument* doc, tinyxml2::XMLPrinter* printer
|
|||||||
if (doc->FirstChildElement("methodCall")->FirstChildElement("methodName") == nullptr)
|
if (doc->FirstChildElement("methodCall")->FirstChildElement("methodName") == nullptr)
|
||||||
throw rpc_error(XMLRPC_PARSE_ERROR, "methodName element not found");
|
throw rpc_error(XMLRPC_PARSE_ERROR, "methodName element not found");
|
||||||
auto method_name = doc->FirstChildElement("methodCall")->FirstChildElement("methodName")->GetText();
|
auto method_name = doc->FirstChildElement("methodCall")->FirstChildElement("methodName")->GetText();
|
||||||
|
|
||||||
if (method_name == nullptr)
|
|
||||||
throw rpc_error(XMLRPC_PARSE_ERROR, "methodName element is empty");
|
|
||||||
torrent::Object result;
|
torrent::Object result;
|
||||||
|
|
||||||
// Add a shim here for system.multicall to allow better code reuse, and
|
// Add a shim here for system.multicall to allow better code reuse, and
|
||||||
@@ -326,20 +309,12 @@ process_document(const tinyxml2::XMLDocument* doc, tinyxml2::XMLPrinter* printer
|
|||||||
auto& result_list = result.as_list();
|
auto& result_list = result.as_list();
|
||||||
auto parent_elements = element_access(doc->RootElement(), {"params", "param", "value", "array", "data"});
|
auto parent_elements = element_access(doc->RootElement(), {"params", "param", "value", "array", "data"});
|
||||||
for (auto child = parent_elements->FirstChildElement("value"); child; child = child->NextSiblingElement("value")) {
|
for (auto child = parent_elements->FirstChildElement("value"); child; child = child->NextSiblingElement("value")) {
|
||||||
auto method_name_member = element_access(child, {"struct", "member"});
|
auto sub_method_name = element_access(child, {"struct", "member", "value", "string"})->GetText();
|
||||||
auto member_name = method_name_member->FirstChildElement("name");
|
|
||||||
|
|
||||||
if (member_name == nullptr || member_name->GetText() == nullptr ||
|
|
||||||
std::strncmp(member_name->GetText(), "methodName", sizeof("methodName")) != 0)
|
|
||||||
throw rpc_error(XMLRPC_PARSE_ERROR, "multicall struct's first member must be methodName");
|
|
||||||
|
|
||||||
auto sub_method_name = element_access(method_name_member, {"value", "string"})->GetText();
|
|
||||||
|
|
||||||
if (sub_method_name == nullptr)
|
|
||||||
throw rpc_error(XMLRPC_PARSE_ERROR, "multicall methodName element is empty");
|
|
||||||
// If sub_params ends up a nullptr at the end of this if-chian,
|
// If sub_params ends up a nullptr at the end of this if-chian,
|
||||||
// execute_command will turn it into an empty list
|
// execute_command will turn it into an empty list
|
||||||
auto sub_params = method_name_member->NextSiblingElement("member");
|
auto sub_params = element_access(child, {"struct", "member"});
|
||||||
|
if (sub_params != nullptr)
|
||||||
|
sub_params = sub_params->NextSiblingElement("member");
|
||||||
if (sub_params != nullptr)
|
if (sub_params != nullptr)
|
||||||
sub_params = sub_params->FirstChildElement("value");
|
sub_params = sub_params->FirstChildElement("value");
|
||||||
if (sub_params != nullptr)
|
if (sub_params != nullptr)
|
||||||
@@ -430,14 +405,6 @@ XmlRpc::process(const char* inBuffer, uint32_t length, slot_write slotWrite) {
|
|||||||
// remains.
|
// remains.
|
||||||
tinyxml2::XMLPrinter printer(nullptr, true, 0);
|
tinyxml2::XMLPrinter printer(nullptr, true, 0);
|
||||||
process_document(&doc, &printer);
|
process_document(&doc, &printer);
|
||||||
|
|
||||||
if (printer.CStrSize() - 1 > static_cast<int>(SCgiTask::max_response_size)) {
|
|
||||||
tinyxml2::XMLPrinter fault_printer(nullptr, true, 0);
|
|
||||||
print_xmlrpc_fault(XMLRPC_LIMIT_EXCEEDED_ERROR, "Response size exceeds maximum XML-RPC limit", &fault_printer);
|
|
||||||
|
|
||||||
return slotWrite(fault_printer.CStr(), fault_printer.CStrSize() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return slotWrite(printer.CStr(), printer.CStrSize() - 1);
|
return slotWrite(printer.CStr(), printer.CStrSize() - 1);
|
||||||
} catch (rpc_error& e) {
|
} catch (rpc_error& e) {
|
||||||
tinyxml2::XMLPrinter printer(nullptr, true, 0);
|
tinyxml2::XMLPrinter printer(nullptr, true, 0);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include "download_storer.h"
|
#include "download_storer.h"
|
||||||
|
|
||||||
#include <cerrno>
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -17,13 +16,6 @@
|
|||||||
#include "core/download.h"
|
#include "core/download.h"
|
||||||
#include "utils/directory.h"
|
#include "utils/directory.h"
|
||||||
|
|
||||||
// O_DIRECTORY makes the open refuse anything that is not a directory. A
|
|
||||||
// platform that does not define it still gets the read-only directory
|
|
||||||
// handle fsync needs, so fall back to no extra flag.
|
|
||||||
#ifndef O_DIRECTORY
|
|
||||||
#define O_DIRECTORY 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace session {
|
namespace session {
|
||||||
|
|
||||||
DownloadStorer::DownloadStorer(core::Download* download)
|
DownloadStorer::DownloadStorer(core::Download* download)
|
||||||
@@ -124,73 +116,34 @@ is_correct_format(const std::string& f) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
save_stream(const std::string& path, bool use_fsyncdisk, const std::stringstream& stream) {
|
save_stream(const std::string& path, bool use_fsyncdisk, const std::stringstream& stream) {
|
||||||
// Remove any leftover temporary file first so that O_EXCL only ever fails on
|
std::fstream output(path.c_str(), std::ios::out | std::ios::trunc);
|
||||||
// an entry that appeared after the unlink, and O_NOFOLLOW keeps a symlink
|
|
||||||
// planted in the session directory from redirecting the write.
|
|
||||||
if (::unlink(path.c_str()) == -1 && errno != ENOENT)
|
|
||||||
throw torrent::storage_error("failed to remove stale file : " + path);
|
|
||||||
|
|
||||||
// TODO: If we cannot open more files, wait for some to finish and try again.
|
// TODO: If we cannot open more files, wait for some to finish and try again.
|
||||||
int fd = ::open(path.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
|
if (!output.is_open())
|
||||||
|
|
||||||
if (fd < 0)
|
|
||||||
throw torrent::storage_error("failed to open file for writing : " + path);
|
throw torrent::storage_error("failed to open file for writing : " + path);
|
||||||
|
|
||||||
const auto data = stream.view();
|
output << stream.rdbuf();
|
||||||
std::size_t remaining = data.size();
|
|
||||||
const char* cursor = data.data();
|
|
||||||
|
|
||||||
while (remaining != 0) {
|
if (!output.good())
|
||||||
ssize_t result = ::write(fd, cursor, remaining);
|
|
||||||
|
|
||||||
if (result == -1) {
|
|
||||||
if (errno == EINTR)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
::close(fd);
|
|
||||||
throw torrent::storage_error("failed to write stream to file : " + path);
|
throw torrent::storage_error("failed to write stream to file : " + path);
|
||||||
}
|
|
||||||
|
|
||||||
cursor += result;
|
output.close();
|
||||||
remaining -= result;
|
|
||||||
}
|
// Ensure that the new file is actually written to the disk
|
||||||
|
int fd = ::open(path.c_str(), O_WRONLY);
|
||||||
|
|
||||||
|
if (fd < 0)
|
||||||
|
throw torrent::storage_error("failed to open file descriptor for fsync : " + path);
|
||||||
|
|
||||||
if (use_fsyncdisk) {
|
if (use_fsyncdisk) {
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
int sync_result = ::fsync(fd);
|
::fsync(fd);
|
||||||
#else
|
#else
|
||||||
int sync_result = ::fdatasync(fd);
|
::fdatasync(fd);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
if (sync_result == -1) {
|
|
||||||
::close(fd);
|
::close(fd);
|
||||||
throw torrent::storage_error("failed to sync file to disk : " + path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A full disk may only be seen when the descriptor is closed.
|
|
||||||
if (::close(fd) == -1)
|
|
||||||
throw torrent::storage_error("failed to close file descriptor : " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
sync_directory_of(const std::string& path) {
|
|
||||||
auto separator = path.rfind('/');
|
|
||||||
auto directory = separator == std::string::npos ? std::string(".") :
|
|
||||||
separator == 0 ? std::string("/") : path.substr(0, separator);
|
|
||||||
|
|
||||||
int fd = ::open(directory.c_str(), O_RDONLY | O_DIRECTORY);
|
|
||||||
|
|
||||||
if (fd < 0)
|
|
||||||
throw torrent::storage_error("failed to open session directory for sync : " + directory);
|
|
||||||
|
|
||||||
if (::fsync(fd) == -1) {
|
|
||||||
::close(fd);
|
|
||||||
throw torrent::storage_error("failed to sync session directory : " + directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (::close(fd) == -1)
|
|
||||||
throw torrent::storage_error("failed to close session directory : " + directory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace anonymous
|
} // namespace anonymous
|
||||||
@@ -220,11 +173,6 @@ DownloadStorer::save_and_move_streams(const std::string& path, bool use_fsyncdis
|
|||||||
|
|
||||||
if (::rename((rtorrent_path + ".new").c_str(), rtorrent_path.c_str()) == -1)
|
if (::rename((rtorrent_path + ".new").c_str(), rtorrent_path.c_str()) == -1)
|
||||||
throw torrent::storage_error("failed to rename rtorrent resume file : " + rtorrent_path);
|
throw torrent::storage_error("failed to rename rtorrent resume file : " + rtorrent_path);
|
||||||
|
|
||||||
// Syncing the files themselves does not persist the renames; the directory
|
|
||||||
// holding them has to be synced for the new names to survive a crash.
|
|
||||||
if (use_fsyncdisk)
|
|
||||||
sync_directory_of(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::Directory
|
utils::Directory
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ SessionManager::save_resume_download(core::Download* download) {
|
|||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(m_pending_builds_mutex);
|
std::unique_lock<std::mutex> lock(m_pending_builds_mutex);
|
||||||
|
|
||||||
|
// TODO: This is under the wrong lock.
|
||||||
if (!m_active)
|
if (!m_active)
|
||||||
throw torrent::internal_error("SessionManager::save_resume_download() called while not active.");
|
throw torrent::internal_error("SessionManager::save_resume_download() called while not active.");
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#ifndef RTORRENT_SESSION_SESSION_MANAGER_H
|
#ifndef RTORRENT_SESSION_SESSION_MANAGER_H
|
||||||
#define RTORRENT_SESSION_SESSION_MANAGER_H
|
#define RTORRENT_SESSION_SESSION_MANAGER_H
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <future>
|
#include <future>
|
||||||
@@ -100,13 +99,9 @@ private:
|
|||||||
bool m_use_fsyncdisk{true};
|
bool m_use_fsyncdisk{true};
|
||||||
bool m_use_lock{true};
|
bool m_use_lock{true};
|
||||||
|
|
||||||
align_cacheline
|
align_cacheline std::mutex m_mutex;
|
||||||
|
|
||||||
std::atomic<bool> m_active{};
|
bool m_active{};
|
||||||
|
|
||||||
align_cacheline
|
|
||||||
|
|
||||||
std::mutex m_mutex;
|
|
||||||
|
|
||||||
std::deque<SaveRequest> m_save_requests;
|
std::deque<SaveRequest> m_save_requests;
|
||||||
std::atomic<size_t> m_save_request_counter{};
|
std::atomic<size_t> m_save_request_counter{};
|
||||||
@@ -131,7 +126,7 @@ private:
|
|||||||
|
|
||||||
inline bool SessionManager::is_used() const { return !m_path.empty(); }
|
inline bool SessionManager::is_used() const { return !m_path.empty(); }
|
||||||
inline std::string SessionManager::path() const { return m_path; }
|
inline std::string SessionManager::path() const { return m_path; }
|
||||||
inline bool SessionManager::use_fsyncdisk() const { return m_use_fsyncdisk; }
|
inline bool SessionManager::use_fsyncdisk() const { return true; }
|
||||||
inline bool SessionManager::use_lock() const { return m_use_lock; }
|
inline bool SessionManager::use_lock() const { return m_use_lock; }
|
||||||
inline void SessionManager::flush_all_pending_builds() { process_pending_builds(true); }
|
inline void SessionManager::flush_all_pending_builds() { process_pending_builds(true); }
|
||||||
|
|
||||||
|
|||||||
+4
-8
@@ -37,8 +37,8 @@ parse_main_options(int argc, char** argv) {
|
|||||||
|
|
||||||
optionParser.insert_option('b', [](auto& arg) { rpc::call_command_set_string("network.bind_address.set", arg); });
|
optionParser.insert_option('b', [](auto& arg) { rpc::call_command_set_string("network.bind_address.set", arg); });
|
||||||
optionParser.insert_option('d', [](auto& arg) { rpc::call_command_set_string("directory.default.set", arg); });
|
optionParser.insert_option('d', [](auto& arg) { rpc::call_command_set_string("directory.default.set", arg); });
|
||||||
optionParser.insert_option('i', [](auto& arg) { rpc::call_command_set_string("network.local_address.set", arg); });
|
optionParser.insert_option('i', [](auto& arg) { rpc::call_command_set_string("ip", arg); });
|
||||||
optionParser.insert_option('p', [](auto& arg) { rpc::call_command_set_string("network.listen.port.range.set", arg); });
|
optionParser.insert_option('p', [](auto& arg) { rpc::call_command_set_string("network.port_range.set", arg); });
|
||||||
optionParser.insert_option('s', [](auto& arg) { rpc::call_command_set_string("session", arg); });
|
optionParser.insert_option('s', [](auto& arg) { rpc::call_command_set_string("session", arg); });
|
||||||
|
|
||||||
optionParser.insert_option('O', [](auto& arg) { rpc::parse_command_single_std(arg); });
|
optionParser.insert_option('O', [](auto& arg) { rpc::parse_command_single_std(arg); });
|
||||||
@@ -86,13 +86,9 @@ config_comment_log(const std::string& command, const std::string& raw_args) {
|
|||||||
pos = next_pos + 1;
|
pos = next_pos + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command == "log.add_output") {
|
if (command == "log.add_output")
|
||||||
if (args.size() != 2)
|
|
||||||
throw torrent::input_error("Invalid number of arguments.");
|
|
||||||
|
|
||||||
log_add_group_output_str(args[0], args[1]);
|
log_add_group_output_str(args[0], args[1]);
|
||||||
|
else if (command == "log.open_file")
|
||||||
} else if (command == "log.open_file")
|
|
||||||
apply_log_open_str(0, args);
|
apply_log_open_str(0, args);
|
||||||
else if (command == "log.open_file.flush")
|
else if (command == "log.open_file.flush")
|
||||||
apply_log_open_str(log_flag_flush, args);
|
apply_log_open_str(log_flag_flush, args);
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
int parse_main_options(int argc, char** argv);
|
int parse_main_options(int argc, char** argv);
|
||||||
void parse_config_file(int argc, char** argv, std::function<void (const std::string&)> parse_fn);
|
void parse_config_file(int argc, char** argv, std::function<void (const std::string&)> parse_fn);
|
||||||
|
|||||||
@@ -63,10 +63,8 @@ SignalHandler::set_block(unsigned int signum) {
|
|||||||
sigemptyset(&mask);
|
sigemptyset(&mask);
|
||||||
sigaddset(&mask, signum);
|
sigaddset(&mask, signum);
|
||||||
|
|
||||||
int result = pthread_sigmask(SIG_BLOCK, &mask, NULL);
|
if (pthread_sigmask(SIG_BLOCK, &mask, NULL) == -1)
|
||||||
|
throw std::logic_error("Could not block signal: " + std::string(std::strerror(errno)));
|
||||||
if (result != 0)
|
|
||||||
throw std::logic_error("Could not block signal: " + std::string(std::strerror(result)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -79,10 +77,8 @@ SignalHandler::set_unblock(unsigned int signum) {
|
|||||||
sigemptyset(&mask);
|
sigemptyset(&mask);
|
||||||
sigaddset(&mask, signum);
|
sigaddset(&mask, signum);
|
||||||
|
|
||||||
int result = pthread_sigmask(SIG_UNBLOCK, &mask, NULL);
|
if (pthread_sigmask(SIG_UNBLOCK, &mask, NULL) == -1)
|
||||||
|
throw std::logic_error("Could not unblock signal: " + std::string(std::strerror(errno)));
|
||||||
if (result != 0)
|
|
||||||
throw std::logic_error("Could not unblock signal: " + std::string(std::strerror(result)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
+2
-5
@@ -1,4 +1,3 @@
|
|||||||
#include <memory>
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
@@ -31,10 +30,8 @@
|
|||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
Download::Download(const std::shared_ptr<core::Download>& download)
|
Download::Download(core::Download* d)
|
||||||
: m_download(download.get()) {
|
: m_download(d) {
|
||||||
|
|
||||||
auto* d = download.get();
|
|
||||||
|
|
||||||
m_windowDownloadStatus = std::make_unique<WDownloadStatus>(d);
|
m_windowDownloadStatus = std::make_unique<WDownloadStatus>(d);
|
||||||
m_windowDownloadStatus->set_bottom(true);
|
m_windowDownloadStatus->set_bottom(true);
|
||||||
|
|||||||
+2
-2
@@ -2,10 +2,10 @@
|
|||||||
#define RTORRENT_UI_DOWNLOAD_H
|
#define RTORRENT_UI_DOWNLOAD_H
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
|
||||||
#include <torrent/peer/peer.h>
|
#include <torrent/peer/peer.h>
|
||||||
|
|
||||||
#include "display/manager.h"
|
#include "display/manager.h"
|
||||||
|
#include "utils/list_focus.h"
|
||||||
|
|
||||||
#include "element_base.h"
|
#include "element_base.h"
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ public:
|
|||||||
DISPLAY_MAX_SIZE
|
DISPLAY_MAX_SIZE
|
||||||
} Display;
|
} Display;
|
||||||
|
|
||||||
Download(const std::shared_ptr<core::Download>& d);
|
Download(core::Download* d);
|
||||||
~Download();
|
~Download();
|
||||||
|
|
||||||
void activate(display::Frame* frame, bool focus = true);
|
void activate(display::Frame* frame, bool focus = true);
|
||||||
|
|||||||
+9
-37
@@ -30,38 +30,6 @@
|
|||||||
|
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
std::string
|
|
||||||
create_filter_pattern(const std::string& input) {
|
|
||||||
std::string pattern = input;
|
|
||||||
|
|
||||||
if (pattern.empty())
|
|
||||||
return pattern;
|
|
||||||
|
|
||||||
if (pattern.back() != '$')
|
|
||||||
pattern = pattern + ".*";
|
|
||||||
if (pattern.front() != '^')
|
|
||||||
pattern = ".*" + pattern;
|
|
||||||
|
|
||||||
std::transform(pattern.begin(), pattern.end(), pattern.begin(), ::tolower);
|
|
||||||
return pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
torrent::Object
|
|
||||||
create_filter_command(const std::string& pattern) {
|
|
||||||
torrent::Object name = torrent::Object::create_dict_key();
|
|
||||||
name.as_dict_key() = "d.name";
|
|
||||||
|
|
||||||
torrent::Object args = torrent::Object::create_list();
|
|
||||||
args.as_list().push_back(name);
|
|
||||||
args.as_list().push_back(torrent::Object(pattern));
|
|
||||||
|
|
||||||
torrent::Object command = torrent::Object::create_dict_key();
|
|
||||||
command.as_dict_key() = "match";
|
|
||||||
command.as_dict_obj() = args;
|
|
||||||
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
DownloadList::DownloadList() {
|
DownloadList::DownloadList() {
|
||||||
m_uiArray[DISPLAY_DOWNLOAD] = NULL;
|
m_uiArray[DISPLAY_DOWNLOAD] = NULL;
|
||||||
m_uiArray[DISPLAY_DOWNLOAD_LIST] = new ElementDownloadList();
|
m_uiArray[DISPLAY_DOWNLOAD_LIST] = new ElementDownloadList();
|
||||||
@@ -121,7 +89,7 @@ DownloadList::unfocus_download(core::Download* d) {
|
|||||||
if (m_state == DISPLAY_DOWNLOAD && d == static_cast<Download*>(m_uiArray[DISPLAY_DOWNLOAD])->download())
|
if (m_state == DISPLAY_DOWNLOAD && d == static_cast<Download*>(m_uiArray[DISPLAY_DOWNLOAD])->download())
|
||||||
activate_display(DISPLAY_DOWNLOAD_LIST);
|
activate_display(DISPLAY_DOWNLOAD_LIST);
|
||||||
|
|
||||||
if (current_view()->focus() < current_view()->end_visible() && current_view()->focus()->get() == d)
|
if (*current_view()->focus() == d && current_view()->focus() < current_view()->end_visible())
|
||||||
current_view()->next_focus();
|
current_view()->next_focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,12 +319,16 @@ DownloadList::receive_exit_input(Input type) {
|
|||||||
current_view()->filter();
|
current_view()->filter();
|
||||||
current_view()->sort();
|
current_view()->sort();
|
||||||
} else {
|
} else {
|
||||||
std::string pattern = create_filter_pattern(input->str());
|
std::string pattern = input->str();
|
||||||
|
if (pattern.back() != '$')
|
||||||
|
pattern = pattern + ".*";
|
||||||
|
if (pattern.front() != '^')
|
||||||
|
pattern = ".*" + pattern;
|
||||||
|
std::transform(pattern.begin(), pattern.end(), pattern.begin(), ::tolower);
|
||||||
|
std::string temp_filter = "match={d.name=," + pattern + "}";
|
||||||
if (rpc::call_command_value("view.filter.temp.log"))
|
if (rpc::call_command_value("view.filter.temp.log"))
|
||||||
control->core()->push_log_std("Temporary filter on '" + current_view()->name() + "' view: " + pattern);
|
control->core()->push_log_std("Temporary filter on '" + current_view()->name() + "' view: " + pattern);
|
||||||
|
current_view()->set_filter_temp(temp_filter);
|
||||||
current_view()->set_filter_temp(create_filter_command(pattern));
|
|
||||||
current_view()->filter();
|
current_view()->filter();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
#ifndef RTORRENT_UI_DOWNLOAD_LIST_H
|
#ifndef RTORRENT_UI_DOWNLOAD_LIST_H
|
||||||
#define RTORRENT_UI_DOWNLOAD_LIST_H
|
#define RTORRENT_UI_DOWNLOAD_LIST_H
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <torrent/object.h>
|
|
||||||
|
|
||||||
#include "element_base.h"
|
#include "element_base.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "display/manager.h"
|
#include "display/manager.h"
|
||||||
@@ -32,12 +29,6 @@ namespace ui {
|
|||||||
|
|
||||||
class Download;
|
class Download;
|
||||||
|
|
||||||
// Wrap a user-typed download name pattern in the anchors 'match' expects.
|
|
||||||
std::string create_filter_pattern(const std::string& input);
|
|
||||||
|
|
||||||
// Build the 'match' command the temporary view filter runs against d.name.
|
|
||||||
torrent::Object create_filter_command(const std::string& pattern);
|
|
||||||
|
|
||||||
class DownloadList : public ElementBase {
|
class DownloadList : public ElementBase {
|
||||||
public:
|
public:
|
||||||
typedef display::WindowDownloadList WList;
|
typedef display::WindowDownloadList WList;
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ ElementDownloadList::receive_cycle_throttle() {
|
|||||||
if (m_view->focus() == m_view->end_visible())
|
if (m_view->focus() == m_view->end_visible())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
core::Download* download = m_view->focus()->get();
|
core::Download* download = *m_view->focus();
|
||||||
if (download->is_active()) {
|
if (download->is_active()) {
|
||||||
lt_log_print(torrent::LOG_TORRENT_WARN, "Cannot change throttle on active download.");
|
lt_log_print(torrent::LOG_TORRENT_WARN, "Cannot change throttle on active download.");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ ElementMenu::set_entry(size_type idx, bool triggerSlot) {
|
|||||||
m_entry = idx;
|
m_entry = idx;
|
||||||
focus_entry(m_entry);
|
focus_entry(m_entry);
|
||||||
|
|
||||||
if (triggerSlot && m_entry < size())
|
if (triggerSlot)
|
||||||
base_type::operator[](m_entry).m_slotFocus();
|
base_type::operator[](m_entry).m_slotFocus();
|
||||||
|
|
||||||
m_window->mark_dirty();
|
m_window->mark_dirty();
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ ElementPeerList::create_info() {
|
|||||||
element->push_column("Client:", te_command("p.client_version="));
|
element->push_column("Client:", te_command("p.client_version="));
|
||||||
element->push_column("Options:", te_command("p.options_str="));
|
element->push_column("Options:", te_command("p.options_str="));
|
||||||
element->push_column("Connected:", te_command("if=$p.is_incoming=,incoming,outgoing"));
|
element->push_column("Connected:", te_command("if=$p.is_incoming=,incoming,outgoing"));
|
||||||
element->push_column("Encrypted:", te_command("if=$p.is_encrypted=,yes,$p.is_obfuscated=,handshake,no"));
|
element->push_column("Encrypted:", te_command("if=$p.is_encrypted=,yes,$if=$p.is_obfuscated=\\,handshake\\,no"));
|
||||||
|
|
||||||
element->push_back("");
|
element->push_back("");
|
||||||
element->push_column("Snubbed:", te_command("if=$p.is_snubbed=,yes,no"));
|
element->push_column("Snubbed:", te_command("if=$p.is_snubbed=,yes,no"));
|
||||||
|
|||||||
+4
-7
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <torrent/throttle.h>
|
#include <torrent/throttle.h>
|
||||||
#include <torrent/torrent.h>
|
#include <torrent/torrent.h>
|
||||||
@@ -355,9 +354,8 @@ Root::reset_input_history_attributes(ui::DownloadList::Input type) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
Root::set_input_history_size(int size) {
|
Root::set_input_history_size(int size) {
|
||||||
if (size < 1 || size > max_input_history_size)
|
if (size < 1)
|
||||||
throw torrent::input_error("Input history size must be between 1 and " +
|
throw torrent::input_error("Invalid input history size.");
|
||||||
std::to_string(max_input_history_size) + ".");
|
|
||||||
|
|
||||||
for (auto& [entry, category] : m_input_history) {
|
for (auto& [entry, category] : m_input_history) {
|
||||||
// Reserve the latest input history entries if new size is smaller than original.
|
// Reserve the latest input history entries if new size is smaller than original.
|
||||||
@@ -467,9 +465,6 @@ Root::save_input_history() {
|
|||||||
history_file << entry << "|" + category.at((pitr->second + i) % m_input_history_length) + "\n";
|
history_file << entry << "|" + category.at((pitr->second + i) % m_input_history_length) + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// The data only reaches the kernel here, so this is where a full disk is seen.
|
|
||||||
history_file.close();
|
|
||||||
|
|
||||||
if (!history_file.good()) {
|
if (!history_file.good()) {
|
||||||
lt_log_print(torrent::LOG_DEBUG, "input history file corrupted during writing, discarding (path:%s)", history_filename.c_str());
|
lt_log_print(torrent::LOG_DEBUG, "input history file corrupted during writing, discarding (path:%s)", history_filename.c_str());
|
||||||
return;
|
return;
|
||||||
@@ -477,6 +472,8 @@ Root::save_input_history() {
|
|||||||
lt_log_print(torrent::LOG_DEBUG, "input history file written (path:%s)", history_filename.c_str());
|
lt_log_print(torrent::LOG_DEBUG, "input history file written (path:%s)", history_filename.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
history_file.close();
|
||||||
|
|
||||||
std::rename(history_filename_tmp.c_str(), history_filename.c_str());
|
std::rename(history_filename_tmp.c_str(), history_filename.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -54,8 +54,6 @@ public:
|
|||||||
typedef std::vector<std::string> InputHistoryCategory;
|
typedef std::vector<std::string> InputHistoryCategory;
|
||||||
typedef std::map<int, InputHistoryCategory> InputHistory;
|
typedef std::map<int, InputHistoryCategory> InputHistory;
|
||||||
|
|
||||||
static constexpr int max_input_history_size = 4096;
|
|
||||||
|
|
||||||
Root();
|
Root();
|
||||||
|
|
||||||
void init(Control* c);
|
void init(Control* c);
|
||||||
@@ -70,6 +68,10 @@ public:
|
|||||||
void set_down_throttle(unsigned int throttle);
|
void set_down_throttle(unsigned int throttle);
|
||||||
void set_up_throttle(unsigned int throttle);
|
void set_up_throttle(unsigned int throttle);
|
||||||
|
|
||||||
|
// Rename to raw or something, make base function.
|
||||||
|
void set_down_throttle_i64(int64_t throttle) { set_down_throttle(throttle >> 10); }
|
||||||
|
void set_up_throttle_i64(int64_t throttle) { set_up_throttle(throttle >> 10); }
|
||||||
|
|
||||||
void adjust_down_throttle(int throttle);
|
void adjust_down_throttle(int throttle);
|
||||||
void adjust_up_throttle(int throttle);
|
void adjust_up_throttle(int throttle);
|
||||||
|
|
||||||
|
|||||||
+5
-27
@@ -5,7 +5,6 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <torrent/exceptions.h>
|
#include <torrent/exceptions.h>
|
||||||
@@ -14,24 +13,6 @@
|
|||||||
|
|
||||||
namespace utils {
|
namespace utils {
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
uint8_t
|
|
||||||
entry_type_from_mode(mode_t mode) {
|
|
||||||
if (S_ISREG(mode))
|
|
||||||
return DT_REG;
|
|
||||||
|
|
||||||
if (S_ISDIR(mode))
|
|
||||||
return DT_DIR;
|
|
||||||
|
|
||||||
if (S_ISLNK(mode))
|
|
||||||
return DT_LNK;
|
|
||||||
|
|
||||||
return DT_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
// Keep this?
|
// Keep this?
|
||||||
bool
|
bool
|
||||||
Directory::is_valid() const {
|
Directory::is_valid() const {
|
||||||
@@ -57,6 +38,9 @@ Directory::update(int flags) {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
struct dirent* entry;
|
struct dirent* entry;
|
||||||
|
#ifdef __sun__
|
||||||
|
struct stat s;
|
||||||
|
#endif
|
||||||
|
|
||||||
while ((entry = readdir(d)) != NULL) {
|
while ((entry = readdir(d)) != NULL) {
|
||||||
if ((flags & update_hide_dot) && entry->d_name[0] == '.')
|
if ((flags & update_hide_dot) && entry->d_name[0] == '.')
|
||||||
@@ -65,22 +49,16 @@ Directory::update(int flags) {
|
|||||||
iterator itr = base_type::insert(end(), value_type());
|
iterator itr = base_type::insert(end(), value_type());
|
||||||
|
|
||||||
#ifdef __sun__
|
#ifdef __sun__
|
||||||
|
stat(entry->d_name, &s);
|
||||||
itr->s_fileno = entry->d_ino;
|
itr->s_fileno = entry->d_ino;
|
||||||
itr->s_reclen = 0;
|
itr->s_reclen = 0;
|
||||||
itr->s_type = DT_UNKNOWN;
|
itr->s_type = s.st_mode;
|
||||||
#else
|
#else
|
||||||
itr->s_fileno = entry->d_fileno;
|
itr->s_fileno = entry->d_fileno;
|
||||||
itr->s_reclen = entry->d_reclen;
|
itr->s_reclen = entry->d_reclen;
|
||||||
itr->s_type = entry->d_type;
|
itr->s_type = entry->d_type;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (itr->s_type == DT_UNKNOWN) {
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
if (fstatat(dirfd(d), entry->d_name, &st, AT_SYMLINK_NOFOLLOW) == 0)
|
|
||||||
itr->s_type = entry_type_from_mode(st.st_mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DIRENT_NAMLEN_EXISTS_FOOBAR
|
#ifdef DIRENT_NAMLEN_EXISTS_FOOBAR
|
||||||
itr->s_name = std::string(entry->d_name, entry->d_name + entry->d_namlen);
|
itr->s_name = std::string(entry->d_name, entry->d_name + entry->d_namlen);
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
#define RTORRENT_UTILS_DIRECTORY_H
|
#define RTORRENT_UTILS_DIRECTORY_H
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <dirent.h>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace utils {
|
namespace utils {
|
||||||
|
|
||||||
struct directory_entry {
|
struct directory_entry {
|
||||||
bool is_file() const { return s_type == DT_REG; }
|
// Fix.
|
||||||
|
bool is_file() const { return true; }
|
||||||
|
|
||||||
// The name and types should match POSIX.
|
// The name and types should match POSIX.
|
||||||
uint32_t s_fileno;
|
uint32_t s_fileno;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user