Compare commits

...

189 Commits

Author SHA1 Message Date
xirvik a075001a73 Catch every json exception when processing a JSONRPC request.
A number overflow escaped the two handlers and killed the process.
2026-08-12 18:22:17 +02:00
xirvik 781520fef8 Close the connection when an SCGI header does not fit the buffer.
A malformed header aborted the process from the SCGI thread.
2026-08-12 15:48:39 +02:00
kunalkakade 224f06fdc2 Link against libatomic when 64-bit atomics require it. 2026-08-09 12:06:08 +02:00
xirvik 324811d6f0 Do not fail startup on unreadable session files.
The download is flagged as hash failed with a message instead of throwing.
2026-08-09 10:39:55 +02:00
Jari Sundell e51fad5bd2 Clean up comments in root.h
Removed comments regarding input history size limitations.
2026-08-09 10:19:35 +02:00
xirvik 44bb2d691f ui: bound the input history size.
Only the lower end was checked, so a large value threw bad_alloc and exited.
2026-08-09 10:19:35 +02:00
xirvik 161a36dedd Add system.sockets.<category>.min_alloc.limit.
A category can have a lower bound too, and a client needs to read it.
2026-08-08 19:39:59 +02:00
xirvik a69e44a096 Add system.sockets.<category>.max_alloc.limit.
A category can have a ceiling below the shared budget, and a client needs to read it.
2026-08-08 11:28:26 +02:00
Greg ORIOL 4dd8b1fd7e Update rtorrent.rc-example with newer network.listen 2026-08-08 10:44:41 +02:00
Greg ORIOL b5f6bb5652 Update rtorrent.rc with newer network.listen 2026-08-08 10:44:41 +02:00
kunalkakade 3ae6bbc394 Fix garbage throttle values in the status bar on 32-bit archs.
Closes #1683.
2026-08-07 15:45:47 +02:00
kunalkakade fdfca98f1c Add documentation for resolving paths. 2026-08-07 11:58:14 +02:00
kunalkakade 31196ff5f0 Add realpath variants of the commands that return paths.
Closes #1670.
2026-08-07 11:58:14 +02:00
kunalkakade cfb9e083d2 Add documentation for the string.* commands. 2026-08-07 11:35:35 +02:00
kunalkakade 5b4b607c98 Add string.* commands for text manipulation.
Closes #1366.
2026-08-07 11:35:35 +02:00
xirvik 87db1cf0f0 rpc: do not dereference a null result from the xmlrpc-c registry.
A failed call returns null, which was passed straight to xmlrpc_mem_block_contents.
2026-08-07 10:56:19 +02:00
rakshasa dd3ddf7c39 Tagged release 0.16.20. 2026-08-06 10:12:49 +02:00
rakshasa e56bffff4d Fixed socket category unit test. 2026-08-05 11:18:31 +02:00
xirvik d20c3234da Add system.sockets allocation budget commands.
They report the budget the socket manager enforces, so a client can show the limit.
2026-08-05 11:18:31 +02:00
Maks Verver dda5e4fa04 Fix deprecated command redirects (rtorrent -D) 2026-08-05 10:14:35 +02:00
Jari Sundell 9ab73352e1 Minor fixes to WaitpidQueue. 2026-08-05 00:59:33 +09:00
Jari Sundell f90a96d57a Added WaitpidQueue to handle background process reaping. 2026-08-04 19:03:26 +09:00
rakshasa 247ae7621a Tagged release 0.16.19. 2026-08-02 10:44:30 +02:00
xirvik 05831942a7 commands: don't register min_alloc/max_alloc for the generic socket category
SocketManager throws internal_error for category_generic, aborting rtorrent over RPC.
2026-07-30 12:42:24 +02:00
rakshasa ee18a1f3fc Added commit message linter. 2026-07-28 10:04:51 +02:00
Jari Sundell f52f20f1b6 Cleaned up system headers. 2026-07-26 19:53:18 +09:00
xirvik 41eef969ea commands: fix group.<name>.ratio.disable calling a removed command
group_insert() builds the disable method as "schedule_remove=...", but that
command no longer exists: the current name is schedule.remove, and the only
old-style aliases registered are schedule2 and schedule_remove2 (and those only
when deprecated commands are enabled at registration time). Every
group.<name>.ratio.disable therefore faults with

  Command "schedule_remove" does not exist.

including the built-in group.seeding.ratio.disable, so a ratio group can be
enabled but never disabled. The enable half already uses the current name.
2026-07-25 10:18:49 +02:00
fffe 1def5e8b36 Add log.print and enum.log_group methods 2026-07-22 11:08:32 +02:00
simonc56 8355870586 Mark 'd.timestamp.started' as safe in command download initialization 2026-07-20 08:21:32 +02:00
Jari Sundell ba2bc7e64d Various release bug fixes. 2026-07-19 18:53:57 +09:00
rakshasa 0c11deac50 Tagged release 0.16.18. 2026-07-17 10:15:28 +02:00
Jari Sundell f6b3ad0efd Minor cleanup of pex and other commands. 2026-07-15 19:40:13 +09:00
Jari Sundell 5d350f0bcc Use new encryption modes. 2026-07-14 17:34:14 +09:00
Jari Sundell 86fa0d195f Changing listen/dht port changes the listening/dht ports. 2026-07-12 17:19:33 +09:00
Jari Sundell 2354c9cdb5 Moved ChunkManager out of public header directory. 2026-07-10 17:54:37 +09:00
Jari Sundell 1b25bc7f56 Moved all sync/diskspace related methods to MemoryManager. 2026-07-10 02:21:08 +09:00
Jari Sundell c6bde213b4 Added runtime::MemoryManager to split out unrelated features from ChunkManager. 2026-07-09 01:56:30 +09:00
Jari Sundell 959448acda Fixed command_base t_pod align static asserts. 2026-07-08 17:15:56 +09:00
Jari Sundell 3b6da6feac Reordered http queue slots to avoid race conditions. 2026-07-07 16:34:19 +09:00
Jari Sundell ea2d22cb87 Removed unused add/remove error-event code. 2026-07-07 03:05:20 +09:00
rakshasa fa351c017d Tagged release 0.16.17. 2026-07-06 14:06:52 +02:00
Pluto Yang 1e1bdd551a Add LoongArch CPU support 2026-07-06 13:38:08 +02:00
Jari Sundell a07f57a703 Fixed fd close order in ExecFile. 2026-07-06 18:06:51 +09:00
Jari Sundell 0494ce70c8 Add http done/failed slots before starting request. 2026-07-06 17:18:39 +09:00
Xeonacid cfad36bf12 Add RISC-V cacheline fallbacks
The Linux cacheline probe first tries <linux/cache.h>, but that header is not part of the installed uapi headers on Arch Linux. When the compile probe fails, configure falls back to a host_cpu mapping and currently aborts for riscv64 with:

  Unrecognized CPU architecture (riscv64) on Linux fallback path.

Handle riscv* in both cacheline fallback maps and use a 64-byte cacheline. That matches the Linux RISC-V kernel default L1_CACHE_SHIFT value of 6 and the value reported on a riscv64 machine through getconf LEVEL1_DCACHE_LINESIZE and sysfs cache coherency_line_size.
2026-07-05 15:40:50 +02:00
rakshasa 123c327a8f Tagged release 0.16.16. 2026-07-03 16:20:27 +02:00
fffe 9884c9dd9b Add support for posix_spawn_file_actions_addclosefrom_np by @fffe 2026-07-03 20:52:26 +09:00
Jari Sundell d99cd5eb73 Added max http request size and limited redirect protocols to http/https. 2026-07-03 19:34:36 +09:00
Jari Sundell 786f1234d2 Fix background task execution. 2026-07-03 17:53:00 +09:00
Jari Sundell 23921cc97c Added new ProxyManager and support for socks5 for peer connections. 2026-06-30 19:55:45 +09:00
Jari Sundell 6558f0ad42 Fixed shutdown handling of stalled http requests. 2026-06-29 16:18:56 +09:00
simonc56 9431dd5acc mark d.complete & d.timestamp.finished as safe in command download 2026-06-28 10:11:06 +02:00
trim21 8598873627 Replace rak::regex with fnmatch from POSIX <fnmatch.h>
rak::regex was a hand-rolled glob matcher supporting only '*'
wildcards. Replace it with the standard POSIX fnmatch() which supports
the same glob patterns plus '?' and '[...]' character classes.

- src/core/manager.cc: use fnmatch for directory entry filtering
- src/command_download.cc: use fnmatch for file path pattern matching
- Makefile.am: remove rak/regex.h from EXTRA_DIST
- rak/regex.h: deleted, no longer needed
2026-06-25 09:47:11 +02:00
rakshasa ce4cd27697 Tagged release 0.16.15. 2026-06-22 10:28:54 +02:00
Jari Sundell 59fe93515e Only enable SIGCHLD on main thread. 2026-06-21 23:05:16 +09:00
Jari Sundell 8249070ff1 Remove shutdown comments for directory watch
Remove commented code regarding directory watch closure during shutdown.
2026-06-19 10:29:43 +02:00
Xirvik Support 517454b413 control: close directory watch on quick shutdown (fix SIGABRT at exit)
Control::handle_shutdown() closes m_directory_events (the inotify directory
watch) only in the normal-shutdown branch (!m_shutdownQuick). On a quick
shutdown (SIGTERM -> receive_quick_shutdown) the m_shutdownQuick branch skips
it, so the watch stays registered in the poll. ~Control() then destroys the
still-open directory_events and Event::~Event()'s assert(m_poll_event == nullptr)
(added with the 0.16.13 poll/event rework) aborts:

    main -> Control::~Control() -> ~directory_events -> ~Event() -> abort

Close it in Control::cleanup() instead, which runs on every shutdown path after
the session is saved; close() is idempotent, so the normal path that already
closed it in handle_shutdown() is unaffected.

Reproducible: configure a directory.watch.added watch, then SIGTERM the client
-> SIGABRT (core in Event::~Event); SIGINT (normal shutdown) is clean. With this
change SIGTERM exits cleanly too.
2026-06-19 10:29:43 +02:00
Jari Sundell a79aaad9ab Clean up comments in InputEvent methods
Removed commented-out code regarding error handling for stdin and event processing.
2026-06-19 09:12:22 +02:00
Xirvik Support c6de871a77 input: survive controlling-terminal/pty hangup on stdin
Since the 0.16.13 callback/poll rework, an EPOLLERR on stdin (controlling
terminal or pty hangup) reaches Poll::process(), which aborts the whole
client with an internal_error because InputEvent never registered for error
events:

    Poll::process() received error event for event not in error: input-fd:0

Register stdin for error events (insert_error) and handle event_error() by
dropping stdin from the poll set with this_thread::poll()->remove_and_close().
rtorrent then keeps running without keyboard input instead of dying.

remove() guards on the fd state (is_open()), mirroring SCgiTask: event_error()
clears the fd after remove_and_close(), and the shutdown path (Control::cleanup)
still calls remove(); without the guard the second remove_and_close() throws
'event not found' via event_mask(). insert()/remove() take the thread poll
implicitly (this_thread::poll()) instead of a Poll* argument.
2026-06-19 09:12:22 +02:00
Jari Sundell acb02379b8 Use a cache for free diskspace when sync'ing all downloads. 2026-06-18 23:45:55 +09:00
trim21 bf74686c29 fix: replace reinterpret_cast UB in command_base with aligned placement new
Replace the union-based reinterpret_cast type erasure in command_base
with an alignas char buffer + typed copy/destroy helper pointers.

set_function<T>() placement-news the correct std::function<T> type
at the buffer address, and stores per-type copy/destroy helpers so
that the copy ctor, assignment, and destructor always operate on the
actual type rather than assuming base_function.

_reinterpret_cast<T&> access of t_pod remains zero-overhead and is
now well-defined because the object was constructed at that address
as T via placement new.

Fixes #1818
2026-06-18 11:05:58 +02:00
Jari Sundell dbe7997131 Use posix_spawn for execute commands. 2026-06-18 01:01:52 +09:00
Jari Sundell d595ebf7d8 Renamed scgi socket manager category to rpc. 2026-06-17 03:43:26 +09:00
Jari Sundell 08828045f5 Added min/max alloc for SocketManager categories. 2026-06-16 22:39:39 +09:00
Jari Sundell 662d67e861 Cleaned up if/branch commands and made branch types stricter. 2026-06-15 18:55:30 +09:00
Jari Sundell d08d7de20d Added "system.sockets.<category>.{size,max_size}" commands. 2026-06-15 18:38:54 +09:00
rakshasa 83bdc271a4 Tagged release 0.16.14. 2026-06-14 09:38:10 +02:00
Jari Sundell 29edda2f15 Removed address based throttling. 2026-06-13 22:53:42 +09:00
Jari Sundell fd69baa2c1 Removed encoding name/path attribute handling. 2026-06-13 18:37:19 +09:00
Jari Sundell 1603ba0573 Use std::chrono::seconds in TrackerState. 2026-06-12 19:50:17 +09:00
trim21 19305ab662 fix: define LUA_OK for Lua 5.1 and LuaJIT compatibility
LUA_OK was introduced in Lua 5.2. Define it as 0 for compatibility
with Lua 5.1 and LuaJIT (which is based on Lua 5.1).
2026-06-12 09:59:42 +02:00
trim21 5b2e202452 fix: scheduler front()->time() -> front()->time after SchedulerHandle change
SchedulerHandle::time is now a data member instead of a method, so
front()->time() must be front()->time (no parentheses).
2026-06-11 18:31:17 +02:00
trim21 c63db9cf97 refactor: use torrent::heuristics_enum instead of choke_queue::heuristics_enum
Update to use the new top-level torrent::heuristics_enum from
torrent/download/types.h, removing the dependency on choke_queue.h
for enum values.
2026-06-10 13:02:06 +02:00
trim21 72f2197add fix: define default_resume_flags sentinel, clearing open_enable_fallocate
Replace the raw ~uint32_t sentinel with a named constant
Download::default_resume_flags that masks out the open_enable_fallocate
bit. This prevents flag_fallocate from being set on all files when
open_throw() reads resume_flags() before explicit flags are configured.

The sentinel value (~uint32_t & ~open_enable_fallocate) retains the
full range as a 'not set' marker while being safe to pass through
Download::open() without unintended fallocate.
2026-06-09 10:50:40 +02:00
fffe e96f594afc Added missing maybe_unuseds attributes and fixed pkg-config check by @fffe 2026-06-09 17:22:11 +09:00
trim21 bc8d0da70f set d.message on hash check failure
Use Download::hash_error_message() to get a descriptive error string when
hash check fails due to I/O error, instead of relying on errno which could
be zero.

Also fix Manager::receive_hashing_changed() where set_hash_failed(true) was
called without setting d.message when catching local_error during hash
check (e.g. 'too many open files').
2026-06-05 15:58:11 +02:00
Jari Sundell 75377817b4 Deprecate commands and set DHT to auto by default. 2026-06-05 21:50:08 +09:00
Jari Sundell 595617c408 Override destructor in ElementLogComplete class. 2026-06-05 10:59:54 +02:00
Jindrich Makovicka 578fe03ad6 Unregister destroyed ElementLogComplete and WindowLog 2026-06-05 10:59:54 +02:00
Xirvik efe258a137 fix(rpc): preserve c_str() stability of stored XMLRPC method names
Commit 6488131 ("Fix RPC/SCGI security and crash bugs by @sirus20x6")
replaced std::vector<std::unique_ptr<const char>> storage with
std::vector<std::string> and returned back().c_str() to the xmlrpc-c
registry as the per-method server_info pointer.

This is unsafe for any method name short enough to be SSO-stored
(<= 15 chars on libstdc++): such a string keeps its buffer inside the
std::string object itself. When a later push_back reallocates the
vector and move-constructs the existing elements into a new buffer,
the previously returned c_str() pointers — captured by xmlrpc-c at
registration time — dangle into freed memory.

Because xmlrpc-c does not dereference server_info until a call
dispatches, the failure surfaces later as nondeterministic garbage
in fault strings, e.g.

  faultString: Command "thod." does not exist.    (load.start, log.xmlrpc, log.execute)
  faultString: Command "in_rate" does not exist.  (log.add_output)
  faultString: Command ""       does not exist.   (log.open_file)
  faultString: Command "+U"     does not exist.   (method.set_key)

Long-named methods (e.g. system.client_version at 21 chars) are
heap-allocated above the SSO threshold and escape the bug because
the heap buffer's address is preserved across the vector move.

Switch the storage to std::deque<std::string>: per [deque.modifiers]
push_back does not invalidate references to existing elements, so
the std::string objects do not move and the c_str() pointers handed
to xmlrpc-c remain valid for the program's lifetime. The body of
store_command_name is unchanged.

Fixes the use-after-free; preserves the std::string-based storage
the original commit aimed for.
2026-06-05 10:55:51 +02:00
Jari Sundell 44d39713d5 Clean out old cruft in autoconf scripts. 2026-06-05 00:46:47 +09:00
Xirvik Support 49625ab5e5 rpc: close SCGI task on EPIPE to stop event_write() busy-loop
When an SCGI client closes the connection before rtorrent finishes sending
the response, send() in SCgiTask::event_write() returns -1 with errno EPIPE.
EPIPE was grouped with EAGAIN/EINTR as a non-fatal retry-later condition, so
the task was not closed and its descriptor stayed registered for EPOLLOUT. A
broken socket is reported writable immediately, so epoll_wait() returns it on
every iteration and the SCGI thread spins at 100% CPU on one core
indefinitely. The dead connection fd is also leaked (stays ESTAB).

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

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

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

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

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

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

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

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

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

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

Verified on gb4 with rtorrent 0.16.11 + this fix: 30/30 trusted calls
succeed, 30/30 untrusted correctly blocked, 30/30 trusted-after-untrusted
batch all succeed (previously 70%+ would fail in the same scenario).
2026-05-04 10:06:48 +02:00
rakshasa 2700b3141f Tagged release 0.16.11. 2026-05-01 11:29:09 +02:00
fffe 4fec56a243 assert valid read_length 2026-04-24 12:22:34 +02:00
fffe dcf24711ef refactor 2026-04-24 12:22:34 +02:00
fffe 4e3ad1ab62 fix off-by-one in SCgiTask::event_read 2026-04-24 12:22:34 +02:00
rakshasa b5a606649a Tagged release 0.16.10. 2026-04-23 10:31:51 +02:00
Jari Sundell eb96876eb2 Moved Thread header to torrent/system directory. 2026-04-22 23:02:05 +09:00
Jari Sundell a964b0a350 Added special syntax to config files to enable log categories early. 2026-04-20 01:07:38 +09:00
Jari Sundell 1adcf230d2 Fix core subsystem logic and safety bugs @Sirus20x6 2026-04-18 01:54:46 +09:00
rakshasa 4235507292 Fixed path expand when doing a single '~' when expanding directory entries. 2026-04-17 12:13:27 +02:00
Jari Sundell b7a21b1e9d Added support for gzip accepted_encoding in scgi headers. 2026-04-12 22:19:49 +09:00
Jari Sundell bd53959b40 Fixed session save getting stuck. 2026-04-11 16:50:58 +09:00
Jari Sundell 5a4ba8bc32 Removed deprecated rak header files. 2026-04-10 22:04:02 +09:00
Jari Sundell e64ec358fa Removed deprecated rak headers. 2026-04-10 03:49:38 +09:00
rakshasa b78fd9aef5 Tagged release 0.16.9. 2026-04-06 15:19:27 +02:00
rakshasa 21e399d099 Remove unneeded MSG_NOSIGNAL. 2026-04-06 10:48:35 +02:00
Xirvik 08a907b547 Whitelist additional read-only getters for untrusted connections
ruTorrent queries these commands for its settings and status pages.
They are all read-only getters with no side effects, safe to expose
for untrusted SCGI connections.

Tested against ruTorrent with both httprpc and multirpc plugins on
servers with active torrents — all modes (list, settings, totals,
open connections) work with zero blocked commands.
2026-03-30 11:55:24 +02:00
zqmfb a0b8702895 Address additional review feedback 2026-03-27 17:48:13 +01:00
zqmfb ad3c31862e Further clarify systemd socket selection 2026-03-27 17:48:13 +01:00
zqmfb 2711e4c044 Use libtorrent's fd_set_nonblock helper 2026-03-27 17:48:13 +01:00
zqmfb 8d50aee96f Add network.scgi.open_systemd command unconditionally 2026-03-27 17:48:13 +01:00
zqmfb 71bb51c3fd Clarify systemd socket selection logic 2026-03-27 17:48:13 +01:00
zqmfb 63ea08bde6 Add support for SCGI systemd socket activation
Add a new command, `network.scgi.open_systemd`, that binds to a file
descriptor passed in via systemd socket activation.
2026-03-27 17:48:13 +01:00
Xirvik Support a82bdf22ac Review fix: remove method.use_deprecated.set from untrusted whitelist 2026-03-23 15:11:07 +01:00
Xirvik 9f6731b4e2 Address review: tighten untrusted safelist and remove set_trusted 2026-03-23 15:11:07 +01:00
Xirvik d935e0ffe9 Address review feedback: explicit mark_safe whitelist and rpc trust flow 2026-03-23 15:11:07 +01:00
Xirvik ba239bc8c5 Address code review: fix setter exposure and narrow catch blocks
1. network.rpc.use_xmlrpc and network.rpc.use_jsonrpc: change from
   CMD2_VAR_BOOL_U (getter+setter both safe) to CMD2_VAR_BOOL_U_GET
   (getter safe, setter trusted-only). Untrusted callers could
   previously disable RPC transports entirely.

2. Remove broad catch(std::exception&) and catch(...) from xmlrpc_c.cc
   that masked real defects and altered fault semantics.

3. Revert SCGI callback catch-all to re-throw instead of swallowing
   exceptions with a generic error response.
2026-03-23 15:11:07 +01:00
Xirvik ea16276773 Fix crash on untrusted XMLRPC connections
Root cause: network.rpc.use_xmlrpc and network.rpc.use_jsonrpc were not
marked as untrusted-safe, but RpcManager::process() calls them before
dispatching to the protocol handler. When an untrusted request arrived,
call_command() threw untrusted_error for these gatekeepers, which escaped
the callback_interrupt_pollling callback and crashed rtorrent.

Fix: Mark network.rpc.use_xmlrpc/jsonrpc as safe (CMD2_VAR_BOOL_U).

Also harden exception safety:
- SCGI callback catch-all now sends a generic error response instead of
  re-throwing, since the callback infrastructure may not support
  exception propagation.
- xmlrpc_c.cc now has catch(std::exception&) and catch(...) safety nets
  after the specific exception handlers.
2026-03-23 15:11:07 +01:00
Xirvik f767053297 Mark safe commands with flag_untrusted_safe for whitelist enforcement
Annotate all commands that web UIs (ruTorrent) need for normal torrent
management with _U macro variants, which set flag_untrusted_safe.
Commands not marked are blocked by default for untrusted connections.

Safe commands include:
- d.* download getters, state, priorities, custom fields, start/stop
- f.* file getters, priority control
- p.* peer getters, disconnect, ban/snub
- t.* tracker getters, enable/disable
- throttle.* rate getters/setters, peer limits
- network.* read-only queries (getters safe, setters blocked)
- view.list, view.size, view.filter_all, ui.current_view
- load.*, download_list, d.multicall2, d.multicall.filtered
- convert.*, branch/if/and/or/not/cat/value/print
- system.* version/time/status queries (read-only)
- choke_group.* read-only queries
- method.has_key, method.const, method.list_keys, method.get, strings.*
- group.*.view, group.*.ratio.min/max/upload (dynamic, via flag propagation)

Blocked by default (not marked):
- execute*, method.insert/set/redirect, schedule*, import
- log.*, file.append, network.scgi.open_*, view.filter/sort/event_*
- system.shutdown, system.env, group.insert, choke_group.insert
- All user-defined commands (via method.insert)
2026-03-23 15:11:07 +01:00
Xirvik 598914908f Add untrusted connection security infrastructure (v3)
Replace the v2 blacklist approach with a per-command flag system.
Commands must opt in to being available for untrusted connections
via flag_untrusted_safe (0x400), checked in call_command() which
catches all execution paths including nested commands.

Infrastructure changes:
- Add flag_untrusted_safe to CommandMap
- Add untrusted_error exception type for proper error codes
- Enforce trust check in both call_command() overloads
- Add catch blocks in xmlrpc_c, xmlrpc_tinyxml2, and jsonrpc handlers
- Port SCGI trust state management from v2 (thread_local, header parsing)
- Add _U macro variants in command_helpers.h for safe command registration
- Add CMD2_VAR_*_U and CMD2_VAR_*_U_GET variants for variables
2026-03-23 15:11:07 +01:00
rakshasa 38fc815d52 Fix display/UI crash and correctness bugs (@sirus20x6) 2026-03-16 16:08:52 +01:00
Jari Sundell 674ae767aa Validate parsed int pair arguments for positivity 2026-03-16 13:56:43 +01:00
sirus20x6 0aaa470053 Fix resource leaks and minor issues
- Close pipe fds on fork failure in ExecFile::execute
- Add exception-safe fclose in cmd_file_append via try/catch
- Add overflow guards before K/M/G bit shifts in parse_whole_value
- Fix %u format for int* in sscanf (change to %d)
- Fix typo "atter"→"after" in error message
2026-03-16 13:56:43 +01:00
PiloUnk 6f27159627 Refactor full save scheduling with early return 2026-03-16 13:29:33 +01:00
PiloUnk de163293ab Avoid missing save scheduling after full save update 2026-03-16 13:29:33 +01:00
PiloUnk fb4e775689 Fix coalescing resume saves with pending full save 2026-03-16 13:29:33 +01:00
rakshasa 39f186e523 Tagged release 0.16.8. 2026-03-15 15:43:32 +01:00
Jari Sundell 70e6964823 Fixed various SCGI issues. 2026-03-10 23:25:28 +09:00
rakshasa 7ead88448b Removed rak/error_number.h from Makefile.am. 2026-03-04 11:39:25 +01:00
rakshasa 650f0299b5 Tagged release 0.16.7. 2026-03-04 10:47:30 +01:00
rakshasa 5dfb2ae938 Allow dht bootstrap nodes to be added when dht is off. 2026-03-02 09:45:09 +01:00
Jorge Israel Peña f05a2ae520 Re-send smkx on SIGWINCH to fix arrow keys after terminal reattach 2026-02-19 16:06:42 +01:00
Miroslav Marchev ecefdba734 dht_add_peer_node is empty, use dht_add_bootstrap_node instead
After a refactor dht_add_peer_node became empty function. Replace with dht_add_bootstrap_node to make adding bootstrap nodes work.
2026-02-14 14:32:48 +01:00
Jari Sundell 87666199b3 Added SocketManager to handle reuse of uninterested fd's by the kernel. 2026-01-29 04:00:31 +09:00
Jari Sundell 9489793dcb Created torrent/runtime include directory. 2026-01-27 08:21:05 +09:00
Jari Sundell 2fa7568165 Remove obsolete SocketFd class. 2026-01-26 19:13:03 +09:00
fffe f4b718f685 add separate commands for unbuffered logs 2026-01-16 16:10:02 +01:00
Jari Sundell b233e24465 Deprecated rak::path_expand. 2026-01-08 23:27:53 +09:00
Jari Sundell 289ab046bf Expand '~/' to $HOME in session path. 2026-01-06 20:44:40 +09:00
Jari Sundell a8b6a47054 Removed deprecated rak errno and file headers. 2026-01-04 03:22:02 +09:00
Zoltan Celedes 110591d5c1 Fix key/value pairs in Lua
Previously, the keys and values were swapped in d.custom.items()
2026-01-03 12:20:08 +01:00
rakshasa ae14baa357 Tagged release 0.16.6. 2026-01-02 15:22:29 +01:00
rakshasa 0b0c824b4b Changed magnet metadata handling and added 'magnet.path.set'. 2025-12-31 21:57:52 +01:00
Jari Sundell def6551488 Properly propagate errors from download session save. 2025-12-30 22:00:34 +09:00
Jari Sundell 787738e36a Make sure pending builds of session resume get processed. 2025-12-29 09:55:32 +09:00
Jari Sundell 40c2d90c45 Fixed dereferencing of potentially nullptr in SessionManager. 2025-12-25 23:05:08 +09:00
Jari Sundell 4a37fbde0b Session saving of resume data is added to a pre-queue. 2025-12-23 03:04:47 +09:00
Jari Sundell 4bdeb58eb6 Run multiple session save requests in parallel. 2025-12-22 06:26:35 +09:00
Jari Sundell 5dbb0020dc Replace ThreadWorker with scgi::ThreadScgi. 2025-12-18 07:43:43 +09:00
Jari Sundell 8f644e65dd Use separate thread for saving session data. 2025-12-18 06:42:44 +09:00
Jari Sundell 16ff32b88c Added missing Event::type_name() functions. 2025-12-13 19:06:02 +09:00
Jari Sundell 8806c06f9f Added timestamp helper commands. 2025-12-10 22:29:42 +09:00
rakshasa 60cfcd37c4 Release 0.16.5. 2025-12-02 17:51:26 +01:00
rakshasa a526ba58e9 Release 0.16.4. 2025-11-25 10:48:11 +01:00
rakshasa efd9507149 Release 0.16.3. 2025-11-21 14:39:27 +01:00
Jari Sundell 3d91dfdb33 Valgrind suppressions file for macos. 2025-11-20 15:57:30 +09:00
Jari Sundell 30cec98799 Moved Poll to net namespace. 2025-11-20 03:05:16 +09:00
rakshasa b825906034 Store copies of command names added to xmlrpc-c. 2025-11-17 20:30:13 +01:00
rakshasa b05ecb5c5a Push back views explicitly takes a string. 2025-11-17 09:36:32 +01:00
Jari Sundell 900be334dc Cleaned up xmlrpc-c string sanitization. 2025-11-17 02:48:32 +09:00
rakshasa 3a1da2fe34 Check if AF_INET6 is supported, or block IPv6 traffic. 2025-11-15 09:17:56 +01:00
Jari Sundell 70750a2bd3 Cleanup of DHT controller. 2025-11-13 00:01:22 +09:00
Jari Sundell 1d2c424a70 Remove throttle from DHT. 2025-11-08 17:51:00 +09:00
183 changed files with 8492 additions and 5911 deletions
+65
View File
@@ -0,0 +1,65 @@
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!"
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
permissions: permissions:
pull-requests: write pull-requests: write
# OPTIONAL: auto-closing conversations requires the `contents` permission # OPTIONAL: auto-closing conversations requires the `contents` permission
contents: write contents: read
steps: steps:
- name: Sleep for 30 seconds - name: Sleep for 30 seconds
run: sleep 30s run: sleep 30s
+2 -1
View File
@@ -16,7 +16,8 @@ jobs:
sudo apt-get install -y \ sudo apt-get install -y \
bear \ bear \
clang-tidy \ clang-tidy \
libcurl4-openssl-dev libcurl4-openssl-dev \
zlib1g-dev
- name: Fetch libtorrent - name: Fetch libtorrent
run: | run: |
git clone https://github.com/rakshasa/libtorrent git clone https://github.com/rakshasa/libtorrent
+70 -27
View File
@@ -4,9 +4,8 @@ on: pull_request
jobs: jobs:
unit-tests: ubuntu-base:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: Update Packages - name: Update Packages
run: | run: |
@@ -15,6 +14,7 @@ jobs:
run: | run: |
sudo apt-get install -y \ sudo apt-get install -y \
libcppunit-dev \ libcppunit-dev \
zlib1g-dev \
libcurl4-openssl-dev libcurl4-openssl-dev
- name: Fetch libtorrent - name: Fetch libtorrent
run: | run: |
@@ -22,43 +22,86 @@ jobs:
- name: Build libtorrent - name: Build libtorrent
run: | run: |
cd libtorrent cd libtorrent
libtoolize autoreconf -fiv
aclocal -I scripts
autoconf -i
autoheader
automake --add-missing
./configure ./configure
make make
sudo make install sudo make install DESTDIR=$GITHUB_WORKSPACE/libtorrent-dist
cd .. - name: Upload Build Artifacts
rm -rf libtorrent uses: actions/upload-artifact@v4
- uses: actions/checkout@v4
with: with:
ref: ${{ github.event.pull_request.head.sha }} name: installed-libtorrent
fetch-depth: 0 path: libtorrent-dist/
- name: Fetch base branch
unit-tests-default:
runs-on: ubuntu-22.04
needs: ubuntu-base
steps:
- name: Update Packages
run: | run: |
git remote add upstream "https://github.com/${{ github.event.pull_request.base.repo.full_name }}" sudo apt-get update
git fetch --no-tags --no-recurse-submodules upstream "${{ github.event.pull_request.base.ref }}" - name: Install Dependencies
run: |
sudo apt-get install -y \
libcppunit-dev \
zlib1g-dev \
libcurl4-openssl-dev
- name: Download Libtorrent Artifacts
uses: actions/download-artifact@v4
with:
name: installed-libtorrent
path: ./libtorrent-dist
- name: Move Artifacts to System Path
run: |
# Elevate permissions with sudo to safely place the files
sudo cp -r ./libtorrent-dist/usr/local/* /usr/local/
rm -rf ./libtorrent-dist
- uses: actions/checkout@v4
- name: Configure Project - name: Configure Project
run: | run: |
libtoolize autoreconf -fiv
aclocal -I scripts
autoconf -i
autoheader
automake --add-missing
./configure ./configure
- name: Build Project
run: |
make make
- name: Run Unit Tests
run: |
ls /usr/local/lib/ ls /usr/local/lib/
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib" LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib" make check
make check
- name: Archive test/test-suite.log - name: Archive test/test-suite.log
if: success() || failure() if: success() || failure()
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: test-suite.log name: test-suite.log
path: test/test-suite.log path: test/test-suite.log
unit-tests-variants:
runs-on: ubuntu-22.04
needs: unit-tests-default
strategy:
matrix:
config_flag: ["--with-xmlrpc-c", "--with-xmlrpc-tinyxml2"]
steps:
- name: Update Packages
run: |
sudo apt-get update
- name: Install Dependencies
run: |
sudo apt-get install -y \
libcppunit-dev \
zlib1g-dev \
libcurl4-openssl-dev \
libxmlrpc-c++8-dev
- name: Download Libtorrent Artifacts
uses: actions/download-artifact@v4
with:
name: installed-libtorrent
path: ./libtorrent-dist
- name: Move Artifacts to System Path
run: |
sudo cp -r ./libtorrent-dist/usr/local/* /usr/local/
rm -rf ./libtorrent-dist
- uses: actions/checkout@v4
- name: Configure Project
run: |
autoreconf -fiv
./configure ${{ matrix.config_flag }}
make
ls /usr/local/lib/
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib" make check
+2
View File
@@ -18,6 +18,7 @@
.libs .libs
Makefile Makefile
aclocal.m4 aclocal.m4
actmp.*
ar-lib ar-lib
autom4te.cache autom4te.cache
compile compile
@@ -69,4 +70,5 @@ TAGS
########################### ###########################
src/rtorrent src/rtorrent
test/rtorrent_Test* test/rtorrent_Test*
test/test-suite.log.tmp
test-driver test-driver
-9
View File
@@ -7,15 +7,6 @@ nobase_dist_pkgdata_DATA = \
lua/rtorrent.lua lua/rtorrent.lua
EXTRA_DIST= \ EXTRA_DIST= \
rak/address_info.h \
rak/algorithm.h \
rak/error_number.h \
rak/file_stat.h \
rak/path.h \
rak/partial_queue.h \
rak/regex.h \
rak/string_manip.h \
rak/unordered_vector.h \
scripts/checks.m4 \ scripts/checks.m4 \
scripts/common.m4 \ scripts/common.m4 \
scripts/attributes.m4 scripts/attributes.m4
+25 -19
View File
@@ -1,6 +1,6 @@
m4_pattern_allow([PKG_CHECK_EXISTS]) m4_pattern_allow([PKG_CHECK_EXISTS])
AC_INIT([rtorrent],[0.16.2],[sundell.software@gmail.com]) AC_INIT([rtorrent],[0.16.20],[sundell.software@gmail.com])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIRS([scripts]) AC_CONFIG_MACRO_DIRS([scripts])
@@ -10,15 +10,11 @@ AM_PROG_AR
LT_INIT LT_INIT
AC_PROG_CXX AC_PROG_CXX
AX_CXX_COMPILE_STDCXX(20, noext, mandatory)
AC_DEFINE([API_VERSION], [18], [api version]) PKG_PROG_PKG_CONFIG
# Filter out unwanted flags added by autoconf on some systems, e.g. MacOS. AC_DEFINE([API_VERSION], [25], [api version])
TORRENT_REMOVE_UNWANTED(CXX, $CXX, -std=c++11 -std=gnu++11)
AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory])
AC_SYS_LARGEFILE
RAK_CHECK_CFLAGS RAK_CHECK_CFLAGS
RAK_CHECK_CXXFLAGS RAK_CHECK_CXXFLAGS
@@ -26,8 +22,7 @@ RAK_ENABLE_DEBUG
RAK_ENABLE_EXTRA_DEBUG RAK_ENABLE_EXTRA_DEBUG
RAK_ENABLE_WERROR RAK_ENABLE_WERROR
TORRENT_ENABLE_ARCH TORRENT_CHECK_ATOMIC
TORRENT_WITH_SYSROOT
AC_ARG_ENABLE(execinfo, AC_ARG_ENABLE(execinfo,
AS_HELP_STRING([--disable-execinfo], AS_HELP_STRING([--disable-execinfo],
@@ -41,35 +36,46 @@ AC_ARG_ENABLE(execinfo,
]) ])
AX_PTHREAD([], AC_MSG_ERROR([requires pthread])) AX_PTHREAD([], AC_MSG_ERROR([requires pthread]))
AX_WITH_CURSES
if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then TORRENT_WITHOUT_NCURSES
AC_MSG_ERROR([requires either NcursesW or Ncurses library])
if test "x$with_ncurses" != xno; then
AX_WITH_CURSES
if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then
AC_MSG_ERROR([requires either NcursesW or Ncurses library])
fi
fi fi
PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"]) PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.2]) PKG_CHECK_MODULES([ZLIB], [zlib])
PKG_CHECK_MODULES([DEPENDENCIES], [libtorrent >= 0.16.20])
AC_LANG_PUSH(C++) AC_LANG_PUSH(C++)
TORRENT_WITH_XMLRPC_C TORRENT_WITH_XMLRPC_C
AC_LANG_POP(C++) AC_LANG_POP(C++)
dnl We don't have LuaJIT support, and the script breaks if not disabled.
AM_CONDITIONAL([LUAJIT], [false])
TORRENT_WITH_LUA TORRENT_WITH_LUA
TORRENT_WITH_TINYXML2 TORRENT_WITH_TINYXML2
TORRENT_WITH_SYSTEMD
if test ${with_xmlrpc_c+y} && test ${with_xmlrpc_tinyxml2+y}; then if test ${with_xmlrpc_c+y} && test ${with_xmlrpc_tinyxml2+y}; then
AC_MSG_ERROR([--with-xmlrpc-c and --with-xmlrpc-tinyxml2 cannot be used together. Please choose only one]) AC_MSG_ERROR([--with-xmlrpc-c and --with-xmlrpc-tinyxml2 cannot be used together. Please choose only one])
fi fi
AC_DEFINE(USER_AGENT, [std::string(PACKAGE "/" VERSION "/") + torrent::version()], Http user agent) AC_DEFINE(USER_AGENT, [std::string(PACKAGE "/" VERSION)], Http user agent)
dnl Only update global build variables immediately before generating the output, dnl 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="$PTHREAD_LIBS $CURSES_LIB $CURSES_LIBS $DEPENDENCIES_LIBS $LIBS" LIBS="$ATOMIC_LIBS $PTHREAD_LIBS $CURSES_LIB $CURSES_LIBS $ZLIB_LIBS $DEPENDENCIES_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS"
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $DEPENDENCIES_CFLAGS $CURSES_CFLAGS" CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS"
TORRENT_CHECK_POPCOUNT() TORRENT_CHECK_CACHELINE
TORRENT_CHECK_POSIX_SPAWN_ADDCLOSEFROM_NP
AC_CONFIG_FILES([ AC_CONFIG_FILES([
Makefile Makefile
+60
View File
@@ -0,0 +1,60 @@
# 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.
+139
View File
@@ -0,0 +1,139 @@
# 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))"))
+7 -3
View File
@@ -46,8 +46,12 @@
#session.path.set = ./session #session.path.set = ./session
# Watch a directory for new torrents, and stop those that have been # Watch a directory for new torrents, and stop those that have been
# deleted. # deleted. Use directory.watch.ready for network shares or other watch
# directories where files may be copied or written directly into place.
# Do not mix directory.watch.ready and directory.watch.added on the same
# watch directory.
# #
#directory.watch.ready = ./watch, load.start
#schedule2 = watch_directory,5,5,load.start=./watch/*.torrent #schedule2 = watch_directory,5,5,load.start=./watch/*.torrent
# Close torrents when disk-space is low. # Close torrents when disk-space is low.
@@ -67,11 +71,11 @@
# Port range to use for listening. # Port range to use for listening.
# #
#network.port_range.set = 6890-6999 #network.listen.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.port_random.set = no #network.listen.port.random.set = no
# Set RPC type # Set RPC type
#network.rpc.use_xmlrpc.set = true #network.rpc.use_xmlrpc.set = true
+2 -2
View File
@@ -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.port_range.set = 50000-50000 network.listen.port.range.set = 50000-50000
network.port_random.set = no network.listen.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')
+1
View File
@@ -0,0 +1 @@
valgrind --leak-check=full --track-origins=yes --show-reachable=yes --suppressions=/Users/rakshasa/projects/rtorrent/doc/valgrind.suppression --gen-suppressions=all --log-file=valgrind.log /usr/local/bin/rtorrent
+343
View File
@@ -0,0 +1,343 @@
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:__76-*
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:__76-*
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:__80-*
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:__93-*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:__93-*
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:__108-*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:__108-*
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:__108-*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[CFPrefsPlistSource setDomainIdentifier:]
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:-[CFPrefsSearchListSource alreadylocked_copyValueForKey:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[CFPrefsSearchListSource alreadylocked_copyValueForKey:]
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:-[CFPrefsSearchListSource alreadylocked_copyValueForKey:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[CFPrefsSearchListSource addNamedVolatileSourceForIdentifier:]
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:-[CFPrefsSearchListSource handleReply:toRequestNewDataMessage:onConnection:retryCount:error:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[CFPrefsSearchListSource addSourceForIdentifier:user:byHost:container:]
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:-[CFPrefsSearchListSource handleReply:toRequestNewDataMessage:onConnection:retryCount:error:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[CFPrefsSearchListSource handleReply:toRequestNewDataMessage:onConnection:retryCount:error:]
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:-[CFPrefsSearchListSource handleReply:toRequestNewDataMessage:onConnection:retryCount:error:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[_CFXPreferences copyAppValueForKey:identifier:container:configurationURL:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[_CFXPreferences withSearchListForIdentifier:container:cloudConfigurationURL:perform:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[_CFXPreferences withSearchLists:]
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:-[CFPrefsSource copyValueForKey:]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:-[OS_xpc_object dealloc]
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_CF*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:__CF*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_NS*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:getSuperclassMetadata
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_ZN5dyld*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_ZNK5dyld*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:___ZNK5dyld*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_dyld_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:*_dyld_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:___SC_getApplicationBundleID_block_invoke
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:xpc_*
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:xpc_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_xpc_*
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:_xpc_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_libxpc_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:si_module_with_name
}
{
<insert_a_suppression_name_here>
Memcheck:Cond
...
fun:_dispatch_*
}
{
<insert_a_suppression_name_here>
Memcheck:Value8
...
fun:_dispatch_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_dispatch_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:*_swift_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:libdispatch_init
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_os_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_od_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_objc_init
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_notify_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:bootstrap_*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:$ss*
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:loadlocale
fun:setlocale
fun:main
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:initscr
fun:_ZN7display6Canvas10initializeEv
fun:_ZN7Control10initializeEv
fun:main
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:assume_default_colors_sp
fun:_ZN7display6Canvas10initializeEv
fun:_ZN7Control10initializeEv
fun:main
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:start_color_sp
fun:_ZN7display6Canvas10initializeEv
fun:_ZN7Control10initializeEv
fun:main
}
{
<insert_a_suppression_name_here>
Memcheck:Leak
...
fun:_ZN7display6Canvas10initializeEv
}
-60
View File
@@ -1,60 +0,0 @@
// Wrapper for addrinfo with focus on zero-copy conversion to and from
// the c-type and wrapper.
//
// Do use the wrapper on a pre-existing struct addrinfo, cast the
// pointer rather than the base type.
#ifndef RAK_ADDRESS_INFO_H
#define RAK_ADDRESS_INFO_H
#include <netdb.h>
#include <cstring>
namespace rak {
class address_info {
public:
void clear() { std::memset(this, 0, sizeof(address_info)); }
int flags() const { return m_addrinfo.ai_flags; }
void set_flags(int f) { m_addrinfo.ai_flags = f; }
int family() const { return m_addrinfo.ai_family; }
void set_family(int f) { m_addrinfo.ai_family = f; }
int socket_type() const { return m_addrinfo.ai_socktype; }
void set_socket_type(int t) { m_addrinfo.ai_socktype = t; }
int protocol() const { return m_addrinfo.ai_protocol; }
void set_protocol(int p) { m_addrinfo.ai_protocol = p; }
size_t length() const { return m_addrinfo.ai_addrlen; }
addrinfo* c_addrinfo() { return &m_addrinfo; }
const addrinfo* c_addrinfo() const { return &m_addrinfo; }
address_info* next() { return reinterpret_cast<address_info*>(m_addrinfo.ai_next); }
static int get_address_info(const char* node, int domain, int type, address_info** ai);
static void free_address_info(address_info* ai) { ::freeaddrinfo(ai->c_addrinfo()); }
static const char* strerror(int err) { return gai_strerror(err); }
private:
addrinfo m_addrinfo;
};
inline int
address_info::get_address_info(const char* node, int pfamily, int stype, address_info** ai) {
address_info hints;
hints.clear();
hints.set_family(pfamily);
hints.set_socket_type(stype);
return ::getaddrinfo(node, NULL, hints.c_addrinfo(), reinterpret_cast<addrinfo**>(ai));
}
}
#endif
-195
View File
@@ -1,195 +0,0 @@
// rak - Rakshasa's toolbox
// Copyright (C) 2005-2007, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#ifndef RAK_ALGORITHM_H
#define RAK_ALGORITHM_H
#include <algorithm>
#include <limits>
namespace rak {
template <typename _InputIter, typename _Function>
_Function
for_each_pre(_InputIter __first, _InputIter __last, _Function __f) {
_InputIter __tmp;
while (__first != __last) {
__tmp = __first++;
__f(*__tmp);
}
return __f;
}
// Return a range with a distance of no more than __distance and
// between __first and __last, centered on __middle1.
template <typename _InputIter, typename _Distance>
std::pair<_InputIter, _InputIter>
advance_bidirectional(_InputIter __first, _InputIter __middle1, _InputIter __last, _Distance __distance) {
_InputIter __middle2 = __middle1;
do {
if (!__distance)
break;
if (__middle2 != __last) {
++__middle2;
--__distance;
} else if (__middle1 == __first) {
break;
}
if (!__distance)
break;
if (__middle1 != __first) {
--__middle1;
--__distance;
} else if (__middle2 == __last) {
break;
}
} while (true);
return std::make_pair(__middle1, __middle2);
}
template <typename _InputIter, typename _Distance>
_InputIter
advance_forward(_InputIter __first, _InputIter __last, _Distance __distance) {
while (__first != __last && __distance != 0) {
__first++;
__distance--;
}
return __first;
}
template <typename _InputIter, typename _Distance>
_InputIter
advance_backward(_InputIter __first, _InputIter __last, _Distance __distance) {
while (__first != __last && __distance != 0) {
__first--;
__distance--;
}
return __first;
}
template <typename _Value>
struct compare_base {
bool operator () (const _Value& complete, const _Value& base) const {
return !complete.compare(0, base.size(), base);
}
};
// Count the number of elements from the start of the containers to
// the first inequal element.
template <typename _InputIter1, typename _InputIter2>
typename std::iterator_traits<_InputIter1>::difference_type
count_base(_InputIter1 __first1, _InputIter1 __last1,
_InputIter2 __first2, _InputIter2 __last2) {
typename std::iterator_traits<_InputIter1>::difference_type __n = 0;
for ( ;__first1 != __last1 && __first2 != __last2; ++__first1, ++__first2, ++__n)
if (*__first1 != *__first2)
return __n;
return __n;
}
template <typename _Return, typename _InputIter, typename _Ftor>
_Return
make_base(_InputIter __first, _InputIter __last, _Ftor __ftor) {
if (__first == __last)
return "";
_Return __base = __ftor(*__first++);
for ( ;__first != __last; ++__first) {
typename std::iterator_traits<_InputIter>::difference_type __pos = count_base(__base.begin(), __base.end(),
__ftor(*__first).begin(), __ftor(*__first).end());
if (__pos < (typename std::iterator_traits<_InputIter>::difference_type)__base.size())
__base.resize(__pos);
}
return __base;
}
template<typename T>
inline int popcount_wrapper(T t) {
#if USE_BUILTIN_POPCOUNT
return __builtin_popcountll(t);
#else
#error __builtin_popcount not found.
unsigned int count = 0;
while (t) {
count += t & 0x1;
t >> 1;
}
return count;
#endif
}
// Get the median of an unordered set of numbers of arbitrary
// type by modifing the underlying dataset
template <typename T = double, typename _InputIter>
T median(_InputIter __first, _InputIter __last) {
T __med;
unsigned int __size = __last - __first;
unsigned int __middle = __size / 2;
_InputIter __target1 = __first + __middle;
std::nth_element(__first, __target1, __last);
__med = *__target1;
if (__size % 2 == 0) {
_InputIter __target2 = std::max_element(__first, __target1);
__med = (__med + *__target2) / 2.0;
}
return __med;
}
}
#endif
-85
View File
@@ -1,85 +0,0 @@
// rak - Rakshasa's toolbox
// Copyright (C) 2005-2007, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#ifndef RAK_ERROR_NUMBER_H
#define RAK_ERROR_NUMBER_H
#include <cerrno>
#include <cstring>
namespace rak {
class error_number {
public:
static const int e_access = EACCES;
static const int e_again = EAGAIN;
static const int e_connreset = ECONNRESET;
static const int e_connaborted = ECONNABORTED;
static const int e_deadlk = EDEADLK;
static const int e_noent = ENOENT;
static const int e_nodev = ENODEV;
static const int e_nomem = ENOMEM;
static const int e_notdir = ENOTDIR;
static const int e_isdir = EISDIR;
static const int e_intr = EINTR;
error_number() : m_errno(0) {}
error_number(int e) : m_errno(e) {}
bool is_valid() const { return m_errno != 0; }
int value() const { return m_errno; }
const char* c_str() const { return std::strerror(m_errno); }
bool is_blocked_momentary() const { return m_errno == e_again || m_errno == e_intr; }
bool is_blocked_prolonged() const { return m_errno == e_deadlk; }
bool is_closed() const { return m_errno == e_connreset || m_errno == e_connaborted; }
bool is_bad_path() const { return m_errno == e_noent || m_errno == e_notdir || m_errno == e_access; }
static error_number current() { return errno; }
static void clear_global() { errno = 0; }
static void set_global(error_number err) { errno = err.m_errno; }
bool operator == (const error_number& e) const { return m_errno == e.m_errno; }
private:
int m_errno;
};
}
#endif
-75
View File
@@ -1,75 +0,0 @@
// rak - Rakshasa's toolbox
// Copyright (C) 2005-2007, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#ifndef RAK_FILE_STAT_H
#define RAK_FILE_STAT_H
#include <string>
#include <cinttypes>
#include <sys/stat.h>
namespace rak {
class file_stat {
public:
// Consider storing rak::error_number.
bool update(int fd) { return fstat(fd, &m_stat) == 0; }
bool update(const char* filename) { return stat(filename, &m_stat) == 0; }
bool update(const std::string& filename) { return update(filename.c_str()); }
bool update_link(const char* filename) { return lstat(filename, &m_stat) == 0; }
bool update_link(const std::string& filename) { return update_link(filename.c_str()); }
bool is_regular() const { return S_ISREG(m_stat.st_mode); }
bool is_directory() const { return S_ISDIR(m_stat.st_mode); }
bool is_character() const { return S_ISCHR(m_stat.st_mode); }
bool is_block() const { return S_ISBLK(m_stat.st_mode); }
bool is_fifo() const { return S_ISFIFO(m_stat.st_mode); }
bool is_link() const { return S_ISLNK(m_stat.st_mode); }
bool is_socket() const { return S_ISSOCK(m_stat.st_mode); }
off_t size() const { return m_stat.st_size; }
time_t access_time() const { return m_stat.st_atime; }
time_t change_time() const { return m_stat.st_ctime; }
time_t modified_time() const { return m_stat.st_mtime; }
private:
struct stat m_stat;
};
}
#endif
-197
View File
@@ -1,197 +0,0 @@
// rak - Rakshasa's toolbox
// Copyright (C) 2005-2007, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#ifndef RAK_PARTIAL_QUEUE_H
#define RAK_PARTIAL_QUEUE_H
#include <array>
#include <cstring>
#include <stdexcept>
#include <cinttypes>
namespace rak {
// First step, don't allow overflowing to the next layer. Only disable
// the above layers for now.
// We also include 0 in a single layer as some chunk may be available
// only through seeders.
class partial_queue {
public:
typedef uint8_t key_type;
typedef uint32_t mapped_type;
typedef uint16_t size_type;
typedef std::pair<size_type, size_type> size_pair_type;
static const size_type num_layers = 8;
partial_queue() : m_data(NULL), m_maxLayerSize(0) {}
~partial_queue() { disable(); }
bool is_full() const { return m_ceiling == 0; }
bool is_layer_full(size_type l) const { return m_layers[l].second >= m_maxLayerSize; }
bool is_enabled() const { return m_data != NULL; }
// Add check to see if we can add more. Also make it possible to
// check how full we are in the lower parts so the caller knows when
// he can stop searching.
//
// Though propably not needed, as we must continue til the first
// layer is full.
size_type max_size() const { return m_maxLayerSize * num_layers; }
size_type max_layer_size() const { return m_maxLayerSize; }
// Must be less that or equal to (max size_type) / num_layers.
void enable(size_type ls);
void disable();
void clear();
// Safe to call while pop'ing and it will not reuse pop'ed indices
// so it is guaranteed to reach max_size at some point. This will
// ensure that the user needs to refill with new data at regular
// intervals.
bool insert(key_type key, mapped_type value);
// Only call this when pop'ing as it moves the index.
bool prepare_pop();
mapped_type pop();
private:
partial_queue(const partial_queue&);
void operator = (const partial_queue&);
static size_type ceiling(size_type layer) { return (2 << layer) - 1; }
void find_non_empty();
mapped_type* m_data;
size_type m_maxLayerSize;
size_type m_index;
size_type m_ceiling;
std::array<size_pair_type, num_layers> m_layers;
};
inline void
partial_queue::enable(size_type ls) {
if (ls == 0)
throw std::logic_error("partial_queue::enable(...) ls == 0.");
delete [] m_data;
m_data = new mapped_type[ls * num_layers];
m_maxLayerSize = ls;
}
inline void
partial_queue::disable() {
delete [] m_data;
m_data = NULL;
m_maxLayerSize = 0;
}
inline void
partial_queue::clear() {
if (m_data == NULL)
return;
m_index = 0;
m_ceiling = ceiling(num_layers - 1);
m_layers = {};
}
inline bool
partial_queue::insert(key_type key, mapped_type value) {
if (key >= m_ceiling)
return false;
size_type idx = 0;
// Hmm... since we already check the 'm_ceiling' above, we only need
// to find the target layer. Could this be calculated directly?
while (key >= ceiling(idx))
++idx;
m_index = std::min(m_index, idx);
// Currently don't allow overflow.
if (is_layer_full(idx))
throw std::logic_error("partial_queue::insert(...) layer already full.");
//return false;
m_data[m_maxLayerSize * idx + m_layers[idx].second] = value;
m_layers[idx].second++;
if (is_layer_full(idx))
// Set the ceiling to 0 when layer 0 is full so no more values can
// be inserted.
m_ceiling = idx > 0 ? ceiling(idx - 1) : 0;
return true;
}
// is_empty() will iterate to the first layer with un-popped elements
// and return true, else return false when it reaches a overflowed or
// the last layer.
inline bool
partial_queue::prepare_pop() {
while (m_layers[m_index].first == m_layers[m_index].second) {
if (is_layer_full(m_index) || m_index + 1 == num_layers)
return false;
m_index++;
}
return true;
}
inline partial_queue::mapped_type
partial_queue::pop() {
if (m_index >= num_layers || m_layers[m_index].first >= m_layers[m_index].second)
throw std::logic_error("partial_queue::pop() bad state.");
return m_data[m_index * m_maxLayerSize + m_layers[m_index].first++];
}
}
#endif
-105
View File
@@ -1,105 +0,0 @@
// rak - Rakshasa's toolbox
// Copyright (C) 2005-2007, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
// Various functions for manipulating file paths. Also consider making
// a directory iterator.
#ifndef RAK_PATH_H
#define RAK_PATH_H
#include <cstdlib>
#include <string>
namespace rak {
inline std::string
path_expand(const std::string& path) {
if (path.empty() || path[0] != '~')
return path;
char* home = std::getenv("HOME");
if (home == NULL)
return path;
return home + path.substr(1);
}
// Don't inline this...
//
// Same strlcpy as found in *bsd.
inline size_t
strlcpy(char *dest, const char *src, size_t size) {
size_t n = size;
const char* first = src;
if (n != 0) {
while (--n != 0)
if ((*dest++ = *src++) == '\0')
break;
}
if (n == 0) {
if (size != 0)
*dest = '\0';
while (*src++)
;
}
return src - first - 1;
}
inline char*
path_expand(const char* src, char* first, char* last) {
if (*src == '~') {
char* home = std::getenv("HOME");
if (home == NULL)
return first;
first += strlcpy(first, home, std::distance(first, last));
if (first > last)
return last;
src++;
}
return std::min(first + strlcpy(first, src, std::distance(first, last)), last);
}
}
#endif
-109
View File
@@ -1,109 +0,0 @@
// rak - Rakshasa's toolbox
// Copyright (C) 2005-2007, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
// This is a hacked up whole string pattern matching. Replace with
// TR1's regex when that becomes widely available. It is intended for
// small strings.
#ifndef RAK_REGEX_H
#define RAK_REGEX_H
#include <sys/types.h>
#include <algorithm>
#include <functional>
#include <string>
#include <list>
namespace rak {
class regex {
public:
regex() {}
regex(const std::string& p) : m_pattern(p) {}
const std::string& pattern() const { return m_pattern; }
bool operator () (const std::string& p) const;
private:
std::string m_pattern;
};
// This isn't optimized, or very clean. A simple hack that should work.
inline bool
regex::operator () (const std::string& text) const {
if (m_pattern.empty() ||
text.empty() ||
(m_pattern[0] != '*' && m_pattern[0] != text[0]))
return false;
// Replace with unordered_vector?
std::list<unsigned int> paths;
paths.push_front(0);
for (std::string::const_iterator itrText = ++text.begin(), lastText = text.end(); itrText != lastText; ++itrText) {
for (std::list<unsigned int>::iterator itrPaths = paths.begin(), lastPaths = paths.end(); itrPaths != lastPaths; ) {
unsigned int next = *itrPaths + 1;
if (m_pattern[*itrPaths] != '*')
itrPaths = paths.erase(itrPaths);
else
itrPaths++;
// When we reach the end of 'm_pattern', we don't have a whole
// match of 'text'.
if (next == m_pattern.size())
continue;
// Push to the back so that '*' will match zero length strings.
if (m_pattern[next] == '*')
paths.push_back(next);
if (m_pattern[next] == *itrText)
paths.push_front(next);
}
if (paths.empty())
return false;
}
return std::find(paths.begin(), paths.end(), m_pattern.size() - 1) != paths.end();
}
}
#endif
-425
View File
@@ -1,425 +0,0 @@
// rak - Rakshasa's toolbox
// Copyright (C) 2005-2007, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#ifndef RAK_STRING_MANIP_H
#define RAK_STRING_MANIP_H
#include <algorithm>
#include <cctype>
#include <climits>
#include <cstdlib>
#include <functional>
#include <iterator>
#include <locale>
#include <random>
namespace rak {
// Use these trim functions until n1872 is widely supported.
template <typename Sequence>
Sequence trim_begin(const Sequence& seq) {
if (seq.empty() || !std::isspace(*seq.begin()))
return seq;
typename Sequence::size_type pos = 0;
while (pos != seq.length() && std::isspace(seq[pos]))
pos++;
return seq.substr(pos, seq.length() - pos);
}
template <typename Sequence>
Sequence trim_end(const Sequence& seq) {
if (seq.empty() || !std::isspace(*(--seq.end())))
return seq;
typename Sequence::size_type pos = seq.size();
while (pos != 0 && std::isspace(seq[pos - 1]))
pos--;
return seq.substr(0, pos);
}
template <typename Sequence>
Sequence trim(const Sequence& seq) {
return trim_begin(trim_end(seq));
}
template <typename Sequence>
Sequence trim_begin_classic(const Sequence& seq) {
if (seq.empty() || !std::isspace(*seq.begin(), std::locale::classic()))
return seq;
typename Sequence::size_type pos = 0;
while (pos != seq.length() && std::isspace(seq[pos], std::locale::classic()))
pos++;
return seq.substr(pos, seq.length() - pos);
}
template <typename Sequence>
Sequence trim_end_classic(const Sequence& seq) {
if (seq.empty() || !std::isspace(*(--seq.end()), std::locale::classic()))
return seq;
typename Sequence::size_type pos = seq.size();
while (pos != 0 && std::isspace(seq[pos - 1], std::locale::classic()))
pos--;
return seq.substr(0, pos);
}
template <typename Sequence>
Sequence trim_classic(const Sequence& seq) {
return trim_begin_classic(trim_end_classic(seq));
}
// Consider rewritting such that m_seq is replaced by first/last.
template <typename Sequence>
class split_iterator_t {
public:
typedef typename Sequence::const_iterator const_iterator;
typedef typename Sequence::value_type value_type;
split_iterator_t() {}
split_iterator_t(const Sequence& seq, value_type delim) :
m_seq(&seq),
m_delim(delim),
m_pos(seq.begin()),
m_next(std::find(seq.begin(), seq.end(), delim)) {
}
Sequence operator * () { return Sequence(m_pos, m_next); }
split_iterator_t& operator ++ () {
m_pos = m_next;
if (m_pos == m_seq->end())
return *this;
m_pos++;
m_next = std::find(m_pos, m_seq->end(), m_delim);
return *this;
}
bool operator == (const split_iterator_t&) const { return m_pos == m_seq->end(); }
bool operator != (const split_iterator_t&) const { return m_pos != m_seq->end(); }
private:
const Sequence* m_seq;
value_type m_delim;
const_iterator m_pos;
const_iterator m_next;
};
template <typename Sequence>
inline split_iterator_t<Sequence>
split_iterator(const Sequence& seq, typename Sequence::value_type delim) {
return split_iterator_t<Sequence>(seq, delim);
}
template <typename Sequence>
inline split_iterator_t<Sequence>
split_iterator(const Sequence&) {
return split_iterator_t<Sequence>();
}
// Could optimize this abit.
inline char
hexchar_to_value(char c) {
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'A' && c <= 'F')
return 10 + c - 'A';
else
return 10 + c - 'a';
}
template <int pos, typename Value>
inline char
value_to_hexchar(Value v) {
v >>= pos * 4;
v &= 0xf;
if (v < 0xA)
return '0' + v;
else
return 'A' + v - 0xA;
}
template <typename InputIterator, typename OutputIterator>
OutputIterator
copy_escape_html(InputIterator first, InputIterator last, OutputIterator dest) {
while (first != last) {
if (std::isalpha(*first, std::locale::classic()) ||
std::isdigit(*first, std::locale::classic()) ||
*first == '-') {
*(dest++) = *first;
} else {
*(dest++) = '%';
*(dest++) = value_to_hexchar<1>(*first);
*(dest++) = value_to_hexchar<0>(*first);
}
++first;
}
return dest;
}
template <typename InputIterator, typename OutputIterator>
OutputIterator
copy_escape_html(InputIterator first1, InputIterator last1, OutputIterator first2, OutputIterator last2) {
while (first1 != last1) {
if (std::isalpha(*first1, std::locale::classic()) ||
std::isdigit(*first1, std::locale::classic()) ||
*first1 == '-') {
if (first2 == last2) break; else *(first2++) = *first1;
} else {
if (first2 == last2) break; else *(first2++) = '%';
if (first2 == last2) break; else *(first2++) = value_to_hexchar<1>(*first1);
if (first2 == last2) break; else *(first2++) = value_to_hexchar<0>(*first1);
}
++first1;
}
return first2;
}
template <typename Iterator>
inline std::string
copy_escape_html(Iterator first, Iterator last) {
std::string dest;
copy_escape_html(first, last, std::back_inserter(dest));
return dest;
}
template <typename Sequence>
inline Sequence
copy_escape_html(const Sequence& src) {
Sequence dest;
copy_escape_html(src.begin(), src.end(), std::back_inserter(dest));
return dest;
}
template <typename Sequence>
inline std::string
copy_escape_html_str(const Sequence& src) {
std::string dest;
copy_escape_html(src.begin(), src.end(), std::back_inserter(dest));
return dest;
}
// Consider support for larger than char type.
template <typename InputIterator, typename OutputIterator>
OutputIterator
transform_hex(InputIterator first, InputIterator last, OutputIterator dest) {
while (first != last) {
*(dest++) = value_to_hexchar<1>(*first);
*(dest++) = value_to_hexchar<0>(*first);
++first;
}
return dest;
}
template <typename InputIterator, typename OutputIterator>
OutputIterator
transform_hex(InputIterator first1, InputIterator last1, OutputIterator first2, OutputIterator last2) {
while (first1 != last1) {
if (first2 == last2) break; else *(first2++) = value_to_hexchar<1>(*first1);
if (first2 == last2) break; else *(first2++) = value_to_hexchar<0>(*first1);
++first1;
}
return first2;
}
template <typename Sequence>
inline Sequence
transform_hex(const Sequence& src) {
Sequence dest;
transform_hex(src.begin(), src.end(), std::back_inserter(dest));
return dest;
}
template <typename Iterator>
inline std::string
transform_hex(Iterator first, Iterator last) {
std::string dest;
transform_hex(first, last, std::back_inserter(dest));
return dest;
}
template <typename Sequence>
inline std::string
transform_hex_str(const Sequence& seq) {
std::string dest;
transform_hex(seq.begin(), seq.end(), std::back_inserter(dest));
return dest;
}
template <typename Sequence>
Sequence
generate_random(size_t length) {
std::random_device rd;
std::mt19937 mt(rd());
using bytes_randomizer = std::independent_bits_engine<std::mt19937, CHAR_BIT, uint8_t>;
bytes_randomizer bytes(mt);
Sequence s;
s.reserve(length);
std::generate_n(std::back_inserter(s), length, std::ref(bytes));
return s;
}
template <typename Iterator>
inline bool
is_all_alpha(Iterator first, Iterator last) {
while (first != last)
if (!std::isalpha(*first++, std::locale::classic()))
return false;
return true;
}
template <typename Sequence>
inline bool
is_all_alpha(const Sequence& src) {
return is_all_alpha(src.begin(), src.end());
}
template <typename Iterator>
inline bool
is_all_alnum(Iterator first, Iterator last) {
while (first != last)
if (!std::isalnum(*first++, std::locale::classic()))
return false;
return true;
}
template <typename Sequence>
inline bool
is_all_alnum(const Sequence& src) {
return is_all_alnum(src.begin(), src.end());
}
template <typename Iterator>
inline bool
is_all_name(Iterator first, Iterator last) {
while (first != last) {
if (!std::isalnum(*first, std::locale::classic()) && *first != '_')
return false;
first++;
}
return true;
}
template <typename Sequence>
inline bool
is_all_name(const Sequence& src) {
return is_all_name(src.begin(), src.end());
}
template <typename Iterator>
std::string
sanitize(Iterator first, Iterator last) {
std::string dest;
for (; first != last; ++first) {
if (std::isprint(*first) && *first != '\r' && *first != '\n' && *first != '\t')
dest += *first;
else
dest += " ";
}
return dest;
}
template <typename Sequence>
std::string
sanitize(const Sequence& src) {
return trim(sanitize(src.begin(), src.end()));
}
template <typename Iterator>
std::string striptags(Iterator first, Iterator last) {
bool copychar = true;
std::string dest;
for (; first != last; ++first) {
if (std::isprint(*first) && *first == '<') {
copychar = false;
} else if (std::isprint(*first) && *first == '>') {
copychar = true;
continue;
}
if (copychar)
dest += *first;
}
return dest;
}
template <typename Sequence>
std::string striptags(const Sequence& src) {
return striptags(src.begin(), src.end());
}
}
#endif
-107
View File
@@ -1,107 +0,0 @@
// rak - Rakshasa's toolbox
// Copyright (C) 2005-2007, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#ifndef RAK_UNORDERED_VECTOR_H
#define RAK_UNORDERED_VECTOR_H
#include <vector>
namespace rak {
template <typename _Tp>
class unordered_vector : private std::vector<_Tp> {
public:
typedef std::vector<_Tp> Base;
typedef typename Base::value_type value_type;
typedef typename Base::pointer pointer;
typedef typename Base::const_pointer const_pointer;
typedef typename Base::reference reference;
typedef typename Base::const_reference const_reference;
typedef typename Base::size_type size_type;
typedef typename Base::difference_type difference_type;
typedef typename Base::allocator_type allocator_type;
typedef typename Base::iterator iterator;
typedef typename Base::reverse_iterator reverse_iterator;
typedef typename Base::const_iterator const_iterator;
typedef typename Base::const_reverse_iterator const_reverse_iterator;
using Base::clear;
using Base::empty;
using Base::size;
using Base::reserve;
using Base::front;
using Base::back;
using Base::begin;
using Base::end;
using Base::rbegin;
using Base::rend;
using Base::push_back;
using Base::pop_back;
// Use the range erase function, the single element erase gets
// overloaded.
using Base::erase;
iterator insert(iterator position, const value_type& x);
iterator erase(iterator position);
private:
};
template <typename _Tp>
typename unordered_vector<_Tp>::iterator
unordered_vector<_Tp>::insert(iterator position, const value_type& x) {
Base::push_back(x);
return --end();
}
template <typename _Tp>
typename unordered_vector<_Tp>::iterator
unordered_vector<_Tp>::erase(iterator position) {
// We don't need to check if position == end - 1 since we then copy
// to the position we pop later.
*position = Base::back();
Base::pop_back();
return position;
}
}
#endif
+124 -16
View File
@@ -10,8 +10,8 @@
# #
# Check for baseline language coverage in the compiler for the specified # Check for baseline language coverage in the compiler for the specified
# version of the C++ standard. If necessary, add switches to CXX and # version of the C++ standard. If necessary, add switches to CXX and
# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) # CXXCPP to enable support. VERSION may be '11', '14', '17', '20', or
# or '14' (for the C++14 standard). # '23' for the respective C++ standard version.
# #
# The second argument, if specified, indicates whether you insist on an # The second argument, if specified, indicates whether you insist on an
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
@@ -36,13 +36,15 @@
# Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com> # Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
# Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.com> # Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.com>
# Copyright (c) 2020 Jason Merrill <jason@redhat.com> # Copyright (c) 2020 Jason Merrill <jason@redhat.com>
# Copyright (c) 2021, 2024 Jörn Heusipp <osmanx@problemloesungsmaschine.de>
# Copyright (c) 2015, 2022, 2023, 2024 Olly Betts
# #
# Copying and distribution of this file, with or without modification, are # Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice # permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any # and this notice are preserved. This file is offered as-is, without any
# warranty. # warranty.
#serial 12 #serial 25
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
dnl (serial version number 13). dnl (serial version number 13).
@@ -51,6 +53,8 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
[$1], [14], [ax_cxx_compile_alternatives="14 1y"], [$1], [14], [ax_cxx_compile_alternatives="14 1y"],
[$1], [17], [ax_cxx_compile_alternatives="17 1z"], [$1], [17], [ax_cxx_compile_alternatives="17 1z"],
[$1], [20], [ax_cxx_compile_alternatives="20"],
[$1], [23], [ax_cxx_compile_alternatives="23"],
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
m4_if([$2], [], [], m4_if([$2], [], [],
[$2], [ext], [], [$2], [ext], [],
@@ -102,9 +106,18 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
dnl HP's aCC needs +std=c++11 according to: dnl HP's aCC needs +std=c++11 according to:
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
dnl Cray's crayCC needs "-h std=c++11" dnl Cray's crayCC needs "-h std=c++11"
dnl MSVC needs -std:c++NN for C++17 and later (default is C++14)
for alternative in ${ax_cxx_compile_alternatives}; do for alternative in ${ax_cxx_compile_alternatives}; do
for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}" MSVC; do
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) if test x"$switch" = xMSVC; then
dnl AS_TR_SH maps both `:` and `=` to `_` so -std:c++17 would collide
dnl with -std=c++17. We suffix the cache variable name with _MSVC to
dnl avoid this.
switch=-std:c++${alternative}
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_${switch}_MSVC])
else
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
fi
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
$cachevar, $cachevar,
[ac_save_CXX="$CXX" [ac_save_CXX="$CXX"
@@ -148,23 +161,44 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
dnl Test body for checking C++11 support dnl Test body for checking C++11 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11 [_AX_CXX_COMPILE_STDCXX_testbody_new_in_11]
) )
dnl Test body for checking C++14 support dnl Test body for checking C++14 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11 [_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14]
) )
dnl Test body for checking C++17 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11 [_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17 _AX_CXX_COMPILE_STDCXX_testbody_new_in_17]
) )
dnl Test body for checking C++20 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_20],
[_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
_AX_CXX_COMPILE_STDCXX_testbody_new_in_20]
)
dnl Test body for checking C++23 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_23],
[_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
_AX_CXX_COMPILE_STDCXX_testbody_new_in_20
_AX_CXX_COMPILE_STDCXX_testbody_new_in_23]
)
dnl Tests for new features in C++11 dnl Tests for new features in C++11
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
@@ -176,7 +210,21 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
#error "This is not a C++ compiler" #error "This is not a C++ compiler"
#elif __cplusplus < 201103L // MSVC always sets __cplusplus to 199711L in older versions; newer versions
// only set it correctly if /Zc:__cplusplus is specified as well as a
// /std:c++NN switch:
//
// https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/
//
// The value __cplusplus ought to have is available in _MSVC_LANG since
// Visual Studio 2015 Update 3:
//
// https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros
//
// This was also the first MSVC version to support C++14 so we can't use the
// value of either __cplusplus or _MSVC_LANG to quickly rule out MSVC having
// C++11 or C++14 support, but we can check _MSVC_LANG for C++17 and later.
#elif __cplusplus < 201103L && !defined _MSC_VER
#error "This is not a C++11 compiler" #error "This is not a C++11 compiler"
@@ -467,7 +515,7 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
#error "This is not a C++ compiler" #error "This is not a C++ compiler"
#elif __cplusplus < 201402L #elif __cplusplus < 201402L && !defined _MSC_VER
#error "This is not a C++14 compiler" #error "This is not a C++14 compiler"
@@ -591,7 +639,7 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
#error "This is not a C++ compiler" #error "This is not a C++ compiler"
#elif __cplusplus < 201703L #elif (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 201703L
#error "This is not a C++17 compiler" #error "This is not a C++17 compiler"
@@ -957,6 +1005,66 @@ namespace cxx17
} // namespace cxx17 } // namespace cxx17
#endif // __cplusplus < 201703L #endif // (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 201703L
]])
dnl Tests for new features in C++20
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_20], [[
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 202002L
#error "This is not a C++20 compiler"
#else
#include <version>
namespace cxx20
{
// As C++20 supports feature test macros in the standard, there is no
// immediate need to actually test for feature availability on the
// Autoconf side.
} // namespace cxx20
#endif // (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 202002L
]])
dnl Tests for new features in C++23
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_23], [[
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 202302L
#error "This is not a C++23 compiler"
#else
#include <version>
namespace cxx23
{
// As C++23 supports feature test macros in the standard, there is no
// immediate need to actually test for feature availability on the
// Autoconf side.
} // namespace cxx23
#endif // (defined _MSVC_LANG ? _MSVC_LANG : __cplusplus) < 202302L
]]) ]])
+83 -34
View File
@@ -19,7 +19,7 @@
# header is checked to match the Lua interpreter version exactly. When # header is checked to match the Lua interpreter version exactly. When
# searching for Lua libraries, the version number is used as a suffix. # searching for Lua libraries, the version number is used as a suffix.
# This is done with the goal of supporting multiple Lua installs (5.1, # This is done with the goal of supporting multiple Lua installs (5.1,
# 5.2, and 5.3 side-by-side). # 5.2, 5.3, and 5.4 side-by-side).
# #
# A note on compatibility with previous versions: This file has been # A note on compatibility with previous versions: This file has been
# mostly rewritten for serial 18. Most developers should be able to use # mostly rewritten for serial 18. Most developers should be able to use
@@ -49,6 +49,14 @@
# interpreter. If LUA is blank, the user's path is searched for an # interpreter. If LUA is blank, the user's path is searched for an
# suitable interpreter. # suitable interpreter.
# #
# Optionally a LUAJIT option may be set ahead of time to look for and
# validate a LuaJIT install instead of PUC Lua. Usage might look like:
#
# AC_ARG_WITH(luajit, [AS_HELP_STRING([--with-luajit],
# [Prefer LuaJIT over PUC Lua, even if the latter is newer. Default: no])
# ])
# AM_CONDITIONAL([LUAJIT], [test "x$with_luajit" != 'xno'])
#
# If MINIMUM-VERSION is supplied, then only Lua interpreters with a # If MINIMUM-VERSION is supplied, then only Lua interpreters with a
# version number greater or equal to MINIMUM-VERSION will be accepted. If # version number greater or equal to MINIMUM-VERSION will be accepted. If
# TOO-BIG-VERSION is also supplied, then only Lua interpreters with a # TOO-BIG-VERSION is also supplied, then only Lua interpreters with a
@@ -82,7 +90,7 @@
# appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES. # appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES.
# #
# If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is # If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is
# performed, otherwise ACTION-IF-NOT-FOUND is preformed. If ACTION-IF-NOT- # performed, otherwise ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-
# FOUND is blank, then it will default to printing an error. To prevent # FOUND is blank, then it will default to printing an error. To prevent
# the default behavior, give ':' as an action. # the default behavior, give ':' as an action.
# #
@@ -152,6 +160,7 @@
# #
# LICENSE # LICENSE
# #
# Copyright (c) 2023 Caleb Maclennan <caleb@alerque.com>
# Copyright (c) 2015 Reuben Thomas <rrt@sc3d.org> # Copyright (c) 2015 Reuben Thomas <rrt@sc3d.org>
# Copyright (c) 2014 Tim Perkins <tprk77@gmail.com> # Copyright (c) 2014 Tim Perkins <tprk77@gmail.com>
# #
@@ -181,7 +190,7 @@
# modified version of the Autoconf Macro, you may extend this special # modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well. # exception to the GPL to apply to your modified version as well.
#serial 42 #serial 47
dnl ========================================================================= dnl =========================================================================
dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION], dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION],
@@ -197,13 +206,14 @@ AC_DEFUN([AX_PROG_LUA],
AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1]) AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1])
dnl Find a Lua interpreter. dnl Find a Lua interpreter.
m4_define_default([_AX_LUA_INTERPRETER_LIST], AM_COND_IF([LUAJIT],
[lua lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua50]) [_ax_lua_interpreter_list='luajit luajit-2.1.0-beta3 luajit-2.0.5 luajit-2.0.4 luajit-2.0.3'],
[_ax_lua_interpreter_list='lua lua5.4 lua54 lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua5.0 lua50'])
m4_if([$1], [], m4_if([$1], [],
[ dnl No version check is needed. Find any Lua interpreter. [ dnl No version check is needed. Find any Lua interpreter.
AS_IF([test "x$LUA" = 'x'], AS_IF([test "x$LUA" = 'x'],
[AC_PATH_PROGS([LUA], [_AX_LUA_INTERPRETER_LIST], [:])]) [AC_PATH_PROGS([LUA], [$_ax_lua_interpreter_list], [:])])
ax_display_LUA='lua' ax_display_LUA='lua'
AS_IF([test "x$LUA" != 'x:'], AS_IF([test "x$LUA" != 'x:'],
@@ -242,7 +252,7 @@ AC_DEFUN([AX_PROG_LUA],
[_ax_check_text="for a Lua interpreter with version >= $1, < $2"]) [_ax_check_text="for a Lua interpreter with version >= $1, < $2"])
AC_CACHE_CHECK([$_ax_check_text], AC_CACHE_CHECK([$_ax_check_text],
[ax_cv_pathless_LUA], [ax_cv_pathless_LUA],
[ for ax_cv_pathless_LUA in _AX_LUA_INTERPRETER_LIST none; do [ for ax_cv_pathless_LUA in $_ax_lua_interpreter_list none; do
test "x$ax_cv_pathless_LUA" = 'xnone' && break test "x$ax_cv_pathless_LUA" = 'xnone' && break
_AX_LUA_CHK_IS_INTRP([$ax_cv_pathless_LUA], [], [continue]) _AX_LUA_CHK_IS_INTRP([$ax_cv_pathless_LUA], [], [continue])
_AX_LUA_CHK_VER([$ax_cv_pathless_LUA], [$1], [$2], [break]) _AX_LUA_CHK_VER([$ax_cv_pathless_LUA], [$1], [$2], [break])
@@ -268,13 +278,25 @@ AC_DEFUN([AX_PROG_LUA],
ax_cv_lua_version=[`$LUA -e ' ax_cv_lua_version=[`$LUA -e '
-- return a version number in X.Y format -- return a version number in X.Y format
local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)") local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)")
print(ver)'`] print(ver or "")'`]
]) ])
AS_IF([test "x$ax_cv_lua_version" = 'x'], AS_IF([test "x$ax_cv_lua_version" = 'x'],
[AC_MSG_ERROR([invalid Lua version number])]) [AC_MSG_ERROR([invalid Lua version number])])
AC_SUBST([LUA_VERSION], [$ax_cv_lua_version]) AC_SUBST([LUA_VERSION], [$ax_cv_lua_version])
AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | $SED 's|\.||'`]) AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | $SED 's|\.||'`])
AM_COND_IF([LUAJIT], [
AC_CACHE_CHECK([for $ax_display_LUA jit version], [ax_cv_luajit_version],
[ ax_cv_luajit_version=[`$LUA -e '
local _, _, ver = string.find(jit and jit.version, "(%d+%..+)")
print(ver or "")'`]
])
AS_IF([test "x$ax_cv_luajit_version" = 'x'],
[AC_MSG_ERROR([invalid Lua jit version number])])
AC_SUBST([LUAJIT_VERSION], [$ax_cv_luajit_version])
AC_SUBST([LUAJIT_SHORT_VERSION], [$(echo "$LUAJIT_VERSION" | $SED 's|\.|§|;s|\..*||;s|§|.|')])
])
dnl The following check is not supported: dnl The following check is not supported:
dnl At times (like when building shared libraries) you may want to know dnl At times (like when building shared libraries) you may want to know
dnl which OS platform Lua thinks this is. dnl which OS platform Lua thinks this is.
@@ -464,33 +486,50 @@ AC_DEFUN([AX_LUA_HEADERS],
AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION]) AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION])
]) ])
AM_COND_IF([LUAJIT],[
dnl Check for LUAJIT_VERSION.
AC_MSG_CHECKING([if LUAJIT_VERSION is defined])
AS_IF([test "x$LUAJIT_VERSION" != 'x'],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([cannot check Lua jit headers without knowing LUAJIT_VERSION])
])
])
dnl Make LUA_INCLUDE a precious variable. dnl Make LUA_INCLUDE a precious variable.
AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1]) AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1])
dnl Some default directories to search. dnl Some default directories to search.
LUA_SHORT_VERSION=`echo "$LUA_VERSION" | $SED 's|\.||'` AM_COND_IF([LUAJIT],
m4_define_default([_AX_LUA_INCLUDE_LIST], [_ax_lua_include_list="
[ /usr/include/lua$LUA_VERSION \ /usr/include/luajit-$LUAJIT_VERSION
/usr/include/lua-$LUA_VERSION \ /usr/include/luajit-$LUAJIT_SHORT_VERSION
/usr/include/lua/$LUA_VERSION \ /usr/local/include/luajit-$LUAJIT_VERSION
/usr/include/lua$LUA_SHORT_VERSION \ /usr/local/include/luajit-$LUAJIT_SHORT_VERSION"],
/usr/local/include/lua$LUA_VERSION \ [_ax_lua_include_list="
/usr/local/include/lua-$LUA_VERSION \ /usr/include/lua$LUA_VERSION
/usr/local/include/lua/$LUA_VERSION \ /usr/include/lua-$LUA_VERSION
/usr/local/include/lua$LUA_SHORT_VERSION \ /usr/include/lua/$LUA_VERSION
]) /usr/include/lua$LUA_SHORT_VERSION
/usr/local/include/lua$LUA_VERSION
/usr/local/include/lua-$LUA_VERSION
/usr/local/include/lua/$LUA_VERSION
/usr/local/include/lua$LUA_SHORT_VERSION"])
dnl Try to find the headers. dnl Try to find the headers.
_ax_lua_saved_cppflags=$CPPFLAGS _ax_lua_saved_cppflags=$CPPFLAGS
CPPFLAGS="$CPPFLAGS $LUA_INCLUDE" CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h]) AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
AM_COND_IF([LUAJIT], [AC_CHECK_HEADERS([luajit.h])])
CPPFLAGS=$_ax_lua_saved_cppflags CPPFLAGS=$_ax_lua_saved_cppflags
dnl Try some other directories if LUA_INCLUDE was not set. dnl Try some other directories if LUA_INCLUDE was not set.
AS_IF([test "x$LUA_INCLUDE" = 'x' && AS_IF([test "x$LUA_INCLUDE" = 'x' &&
test "x$ac_cv_header_lua_h" != 'xyes'], test "x$ac_cv_header_lua_h" != 'xyes' ||
test "x$with_luajit" != 'xno' &&
test "x$ac_cv_header_luajit_h" != 'xyes'],
[ dnl Try some common include paths. [ dnl Try some common include paths.
for _ax_include_path in _AX_LUA_INCLUDE_LIST; do for _ax_include_path in $_ax_lua_include_list; do
test ! -d "$_ax_include_path" && continue test ! -d "$_ax_include_path" && continue
AC_MSG_CHECKING([for Lua headers in]) AC_MSG_CHECKING([for Lua headers in])
@@ -500,10 +539,12 @@ AC_DEFUN([AX_LUA_HEADERS],
AS_UNSET([ac_cv_header_lualib_h]) AS_UNSET([ac_cv_header_lualib_h])
AS_UNSET([ac_cv_header_lauxlib_h]) AS_UNSET([ac_cv_header_lauxlib_h])
AS_UNSET([ac_cv_header_luaconf_h]) AS_UNSET([ac_cv_header_luaconf_h])
AS_UNSET([ac_cv_header_luajit_h])
_ax_lua_saved_cppflags=$CPPFLAGS _ax_lua_saved_cppflags=$CPPFLAGS
CPPFLAGS="$CPPFLAGS -I$_ax_include_path" CPPFLAGS="$CPPFLAGS -I$_ax_include_path"
AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h]) AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
AM_COND_IF([LUAJIT], [AC_CHECK_HEADERS([luajit.h])])
CPPFLAGS=$_ax_lua_saved_cppflags CPPFLAGS=$_ax_lua_saved_cppflags
AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'], AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'],
@@ -621,18 +662,26 @@ AC_DEFUN([AX_LUA_LIBS],
dnl Try to find the Lua libs. dnl Try to find the Lua libs.
_ax_lua_saved_libs=$LIBS _ax_lua_saved_libs=$LIBS
LIBS="$LIBS $LUA_LIB" LIBS="$LIBS $LUA_LIB"
AC_SEARCH_LIBS([lua_load], AM_COND_IF([LUAJIT],
[ lua$LUA_VERSION \ [AC_SEARCH_LIBS([lua_load],
lua$LUA_SHORT_VERSION \ [ luajit$LUA_VERSION \
lua-$LUA_VERSION \ luajit$LUA_SHORT_VERSION \
lua-$LUA_SHORT_VERSION \ luajit-$LUA_VERSION \
:liblua-$LUA_VERSION.so.0 \ luajit-$LUA_SHORT_VERSION \
:liblua-$LUA_SHORT_VERSION.so.0 \ luajit],
lua \ [_ax_found_lua_libs='yes'],
], [_ax_found_lua_libs='no'],
[_ax_found_lua_libs='yes'], [$_ax_lua_extra_libs])],
[_ax_found_lua_libs='no'], [AC_SEARCH_LIBS([lua_load],
[$_ax_lua_extra_libs]) [ lua$LUA_VERSION \
lua$LUA_SHORT_VERSION \
lua-$LUA_VERSION \
lua-$LUA_SHORT_VERSION \
lua \
],
[_ax_found_lua_libs='yes'],
[_ax_found_lua_libs='no'],
[$_ax_lua_extra_libs])])
LIBS=$_ax_lua_saved_libs LIBS=$_ax_lua_saved_libs
AS_IF([test "x$ac_cv_search_lua_load" != 'xno' && AS_IF([test "x$ac_cv_search_lua_load" != 'xno' &&
+117 -127
View File
@@ -1,48 +1,3 @@
AC_DEFUN([TORRENT_CHECK_XFS], [
AC_MSG_CHECKING(for XFS support)
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <xfs/libxfs.h>
#include <sys/ioctl.h>
int main() {
struct xfs_flock64 l;
ioctl(0, XFS_IOC_RESVSP64, &l);
return 0;
}
])],
[
AC_DEFINE(USE_XFS, 1, Use XFS filesystem stuff.)
AC_MSG_RESULT(yes)
], [
AC_MSG_RESULT(no)
])
])
AC_DEFUN([TORRENT_WITHOUT_XFS], [
AC_ARG_WITH(xfs,
AS_HELP_STRING([--without-xfs],[do not check for XFS filesystem support]),
[
if test "$withval" = "yes"; then
TORRENT_CHECK_XFS
fi
], [
TORRENT_CHECK_XFS
])
])
AC_DEFUN([TORRENT_WITH_XFS], [
AC_ARG_WITH(xfs,
AS_HELP_STRING([--with-xfs],[check for XFS filesystem support]),
[
if test "$withval" = "yes"; then
TORRENT_CHECK_XFS
fi
])
])
AC_DEFUN([TORRENT_CHECK_EPOLL], [ AC_DEFUN([TORRENT_CHECK_EPOLL], [
AC_MSG_CHECKING(for epoll support) AC_MSG_CHECKING(for epoll support)
@@ -54,6 +9,8 @@ AC_DEFUN([TORRENT_CHECK_EPOLL], [
} }
])], ])],
[ [
use_epoll=yes
AC_DEFINE(USE_EPOLL, 1, Use epoll.) AC_DEFINE(USE_EPOLL, 1, Use epoll.)
AC_MSG_RESULT(yes) AC_MSG_RESULT(yes)
], [ ], [
@@ -86,6 +43,8 @@ AC_DEFUN([TORRENT_CHECK_KQUEUE], [
} }
])], ])],
[ [
use_kqueue=yes
AC_DEFINE(USE_KQUEUE, 1, Use kqueue.) AC_DEFINE(USE_KQUEUE, 1, Use kqueue.)
AC_MSG_RESULT(yes) AC_MSG_RESULT(yes)
], [ ], [
@@ -118,48 +77,9 @@ AC_DEFUN([TORRENT_WITHOUT_KQUEUE], [
]) ])
AC_DEFUN([TORRENT_CHECK_FALLOCATE], [
AC_MSG_CHECKING(for fallocate)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#define _GNU_SOURCE
#include <fcntl.h>
]], [[ fallocate(0, FALLOC_FL_KEEP_SIZE, 0, 0); return 0;
]])],[
AC_DEFINE(HAVE_FALLOCATE, 1, Linux's fallocate supported.)
AC_MSG_RESULT(yes)
],[
AC_MSG_RESULT(no)
])
])
AC_DEFUN([TORRENT_CHECK_POSIX_FALLOCATE], [
AC_MSG_CHECKING(for posix_fallocate)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <fcntl.h>
]], [[ posix_fallocate(0, 0, 0);
]])],[
AC_DEFINE(USE_POSIX_FALLOCATE, 1, posix_fallocate supported.)
AC_MSG_RESULT(yes)
],[
AC_MSG_RESULT(no)
])
])
AC_DEFUN([TORRENT_WITH_POSIX_FALLOCATE], [
AC_ARG_WITH(posix-fallocate,
AS_HELP_STRING([--with-posix-fallocate],[check for and use posix_fallocate to allocate files]),
[
if test "$withval" = "yes"; then
TORRENT_CHECK_POSIX_FALLOCATE
fi
])
])
AC_DEFUN([TORRENT_WITH_ADDRESS_SPACE], [ AC_DEFUN([TORRENT_WITH_ADDRESS_SPACE], [
AC_ARG_WITH(address-space, AC_ARG_WITH(address-space,
AS_HELP_STRING([--with-address-space=MB],[change the default address space size [[default=1024mb]]]), AS_HELP_STRING([--with-address-space=MB],[change the default address space size [default=1024mb-or-32768mb]]),
[ [
if test ! -z $withval -a "$withval" != "yes" -a "$withval" != "no"; then if test ! -z $withval -a "$withval" != "yes" -a "$withval" != "no"; then
AC_DEFINE_UNQUOTED(DEFAULT_ADDRESS_SPACE_SIZE, [$withval]) AC_DEFINE_UNQUOTED(DEFAULT_ADDRESS_SPACE_SIZE, [$withval])
@@ -171,51 +91,48 @@ AC_DEFUN([TORRENT_WITH_ADDRESS_SPACE], [
AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(long)
if test $ac_cv_sizeof_long = 8; then if test $ac_cv_sizeof_long = 8; then
AC_DEFINE(DEFAULT_ADDRESS_SPACE_SIZE, 4096, Default address space size.) AC_DEFINE(DEFAULT_ADDRESS_SPACE_SIZE, 32768, Default address space size.)
else else
AC_DEFINE(DEFAULT_ADDRESS_SPACE_SIZE, 1024, Default address space size.) AC_DEFINE(DEFAULT_ADDRESS_SPACE_SIZE, 1024, Default address space size.)
fi fi
]) ])
]) ])
AC_DEFUN([TORRENT_WITH_FASTCGI], [
AC_ARG_WITH(fastcgi, AC_DEFUN([TORRENT_CHECK_ATOMIC], [
AS_HELP_STRING([--with-fastcgi=PATH],[enable FastCGI RPC support (DO NOT USE)]), AC_MSG_CHECKING([whether 64-bit atomic operations require -latomic])
AC_LANG_PUSH(C++)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <cstdint>
#include <atomic>]],
[[std::atomic<uint64_t> x(0);
return x.load();]])],
[ [
AC_MSG_CHECKING([for FastCGI (DO NOT USE)]) AC_MSG_RESULT([no])
ATOMIC_LIBS=""
],
[
save_LIBS="$LIBS"
LIBS="$LIBS -latomic"
if test "$withval" = "no"; then AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <cstdint>
AC_MSG_RESULT(no) #include <atomic>]],
[[std::atomic<uint64_t> x(0);
elif test "$withval" = "yes"; then return x.load();]])],
CXXFLAGS="$CXXFLAGS" [
LIBS="$LIBS -lfcgi" AC_MSG_RESULT([yes])
ATOMIC_LIBS="-latomic"
AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <fcgiapp.h> ],
]], [[ FCGX_Init(); ]])],[ [
AC_MSG_RESULT(ok) AC_MSG_RESULT([unsupported])
],[ AC_MSG_ERROR([Compiler target lacks proper 64-bit atomic support.])
AC_MSG_RESULT(not found)
AC_MSG_ERROR(Could not compile FastCGI test.)
]) ])
AC_DEFINE(HAVE_FASTCGI, 1, Support for FastCGI.) LIBS="$save_LIBS"
else
CXXFLAGS="$CXXFLAGS -I$withval/include"
LIBS="$LIBS -lfcgi -L$withval/lib"
AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <fcgiapp.h>
]], [[ FCGX_Init(); ]])],[
AC_MSG_RESULT(ok)
],[
AC_MSG_RESULT(not found)
AC_MSG_ERROR(Could not compile FastCGI test.)
])
AC_DEFINE(HAVE_FASTCGI, 1, Support for FastCGI.)
fi
]) ])
AC_LANG_POP(C++)
AC_SUBST([ATOMIC_LIBS])
]) ])
@@ -260,6 +177,7 @@ AC_DEFUN([TORRENT_WITH_XMLRPC_C], [
]) ])
]) ])
AC_DEFUN([TORRENT_WITH_TINYXML2], [ AC_DEFUN([TORRENT_WITH_TINYXML2], [
AC_MSG_CHECKING(for tinyxml2) AC_MSG_CHECKING(for tinyxml2)
@@ -273,6 +191,7 @@ AC_DEFUN([TORRENT_WITH_TINYXML2], [
]) ])
]) ])
AC_DEFUN([TORRENT_WITH_LUA], [ AC_DEFUN([TORRENT_WITH_LUA], [
AC_ARG_WITH(lua, AC_ARG_WITH(lua,
AS_HELP_STRING([--with-lua],[enable LUA support]), AS_HELP_STRING([--with-lua],[enable LUA support]),
@@ -281,29 +200,45 @@ AC_DEFUN([TORRENT_WITH_LUA], [
AC_MSG_RESULT(no) AC_MSG_RESULT(no)
else else
AX_PROG_LUA AX_PROG_LUA
AX_LUA_LIBS
AX_LUA_HEADERS # 1. Override AX_LUA_LIBS default crash behavior
AC_DEFINE(HAVE_LUA, 1, Use LUA.) AX_LUA_LIBS([have_lua_libs=yes], [have_lua_libs=no])
AC_DEFINE(LUA_DATADIR, [PACKAGE_DATADIR "/lua"], [LUA data directory])
LIBS="$LIBS $LUA_LIB" # 2. Override AX_LUA_HEADERS default crash behavior
CXXFLAGS="$CXXFLAGS $LUA_INCLUDE" AX_LUA_HEADERS([have_lua_headers=yes], [have_lua_headers=no])
# 3. Only inject if both checks completely pass
if test "x$have_lua_libs" = "xyes" && test "x$have_lua_headers" = "xyes"; then
AC_DEFINE(HAVE_LUA, 1, Use LUA.)
AC_DEFINE(LUA_DATADIR, [PACKAGE_DATADIR "/lua"], [LUA data directory])
LIBS="$LIBS $LUA_LIB"
CXXFLAGS="$CXXFLAGS $LUA_INCLUDE"
else
# Throw fatal error ONLY if user strictly ran --with-lua=yes
if test "$withval" = "yes"; then
AC_MSG_ERROR([Lua support explicitly requested, but compatible Lua 5.3 libs/headers were not found.])
else
AC_MSG_WARN([Lua 5.3 libs or headers missing. Proceeding without Lua support.])
fi
fi
fi fi
],[ ],[
AC_MSG_RESULT(ignored) AC_MSG_RESULT(ignored)
]) ])
]) ])
AC_DEFUN([TORRENT_WITH_INOTIFY], [ AC_DEFUN([TORRENT_WITH_INOTIFY], [
AC_LANG_PUSH(C++) AC_LANG_PUSH(C++)
AC_CHECK_HEADERS([sys/inotify.h mcheck.h]) AC_CHECK_HEADERS([sys/inotify.h])
AC_MSG_CHECKING([whether sys/inotify.h actually works]) AC_MSG_CHECKING([whether sys/inotify.h actually works])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([ AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <sys/inotify.h> #include <sys/inotify.h>
int main(int,const char**) { return (-1 == inotify_init()); }]) int main(int,const char**) { return (-1 == inotify_init()); }])
],[ ],[
AC_DEFINE(HAVE_INOTIFY, 1, [sys/inotify.h exists and works correctly]) AC_DEFINE(USE_INOTIFY, 1, [sys/inotify.h exists and works correctly])
AC_MSG_RESULT(yes)], AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(failed)] [AC_MSG_RESULT(failed)]
) )
@@ -311,12 +246,14 @@ AC_DEFUN([TORRENT_WITH_INOTIFY], [
AC_LANG_POP(C++) AC_LANG_POP(C++)
]) ])
AC_DEFUN([TORRENT_CHECK_PTHREAD_SETNAME_NP], [ AC_DEFUN([TORRENT_CHECK_PTHREAD_SETNAME_NP], [
AC_CHECK_HEADERS(pthread.h) AC_CHECK_HEADERS(pthread.h)
AC_MSG_CHECKING(for pthread_setname_np type) AC_MSG_CHECKING(for pthread_setname_np type)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#define _GNU_SOURCE
#include <pthread.h> #include <pthread.h>
#include <sys/types.h> #include <sys/types.h>
]], [[ ]], [[
@@ -341,6 +278,7 @@ AC_DEFUN([TORRENT_CHECK_PTHREAD_SETNAME_NP], [
]) ])
]) ])
AC_DEFUN([TORRENT_DISABLE_PTHREAD_SETNAME_NP], [ AC_DEFUN([TORRENT_DISABLE_PTHREAD_SETNAME_NP], [
AC_MSG_CHECKING([for pthread_setname_no]) AC_MSG_CHECKING([for pthread_setname_no])
@@ -358,3 +296,55 @@ AC_DEFUN([TORRENT_DISABLE_PTHREAD_SETNAME_NP], [
] ]
) )
]) ])
AC_DEFUN([TORRENT_CHECK_POSIX_SPAWN_ADDCLOSEFROM_NP], [
AC_MSG_CHECKING(for posix_spawn_file_actions_addclosefrom_np)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
#define _GNU_SOURCE
#include <spawn.h>
]], [[
posix_spawn_file_actions_t actions;
posix_spawn_file_actions_addclosefrom_np(&actions, 3);
]])],[
AC_DEFINE(HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCLOSEFROM_NP, 1, [Define if posix_spawn_file_actions_addclosefrom_np is available.])
AC_MSG_RESULT(yes)
],[
AC_MSG_RESULT(no)
])
])
AC_DEFUN([TORRENT_WITH_SYSTEMD], [
AC_ARG_WITH(systemd,
AS_HELP_STRING([--with-systemd],[enable systemd socket activation support [[default=no]]]),
[
if test "$withval" = "yes"; then
PKG_CHECK_MODULES([SYSTEMD], [libsystemd],
[
CXXFLAGS="$CXXFLAGS $SYSTEMD_CFLAGS"
LIBS="$LIBS $SYSTEMD_LIBS"
AC_DEFINE(HAVE_SYSTEMD, 1, [Support for systemd socket activation.])
],
[AC_MSG_ERROR([libsystemd not found. Install libsystemd-dev (or the equivalent for your distribution).])])
fi
])
])
AC_DEFUN([TORRENT_WITHOUT_NCURSES], [
AC_ARG_WITH([ncurses],
[AS_HELP_STRING([--without-ncurses], [build without ncurses (daemon-only mode)])],
[with_ncurses=$withval],
[with_ncurses=yes])
if test "x$with_ncurses" = xno; then
AC_DEFINE([HAVE_NO_NCURSES], [1], [Define to 1 if building without ncurses])
CURSES_LIBS=""
CURSES_CFLAGS=""
CURSES_LIB=""
fi
AM_CONDITIONAL([NO_NCURSES], [test "x$with_ncurses" = xno])
])
+169 -145
View File
@@ -1,67 +1,3 @@
AC_DEFUN([TORRENT_WITH_SYSROOT], [
AC_ARG_WITH(sysroot,
AS_HELP_STRING([--with-sysroot=PATH],
[compile and link with a specific sysroot]),
[
AC_MSG_CHECKING(for sysroot)
if test "$withval" = "no"; then
AC_MSG_RESULT(no)
elif test "$withval" = "yes"; then
AC_MSG_RESULT(not a path)
AC_MSG_ERROR(The sysroot option must point to a directory, like f.ex "/Developer/SDKs/MacOSX10.4u.sdk".)
else
AC_MSG_RESULT($withval)
CXXFLAGS="$CXXFLAGS -isysroot $withval"
LDFLAGS="$LDFLAGS -Wl,-syslibroot,$withval"
fi
])
])
AC_DEFUN([TORRENT_REMOVE_UNWANTED],
[
AC_REQUIRE([AC_PROG_GREP])
values_to_check=`for i in $2; do echo $i; done`
unwanted_values=`for i in $3; do echo $i; done`
if test -z "${unwanted_values}"; then
$1="$2"
else
result=`echo "${values_to_check}" | $GREP -Fvx -- "${unwanted_values}" | $GREP -v '^$'`
# join with spaces, squeeze repeats, and trim trailing space
$1=$(printf '%s\n' "$result" | tr '\n' ' ' | sed 's/ */ /g; s/ *$//')
fi
])
AC_DEFUN([TORRENT_ENABLE_ARCH], [
AC_ARG_ENABLE(arch,
AS_HELP_STRING([--enable-arch=ARCH],
[comma seprated list of architectures to compile for]),
[
AC_MSG_CHECKING(for target architectures)
if test "$enableval" = "yes"; then
AC_MSG_ERROR(no arch supplied)
elif test "$enableval" = "no"; then
AC_MSG_RESULT(using default)
else
AC_MSG_RESULT($enableval)
for i in `IFS=,; echo $enableval`; do
CFLAGS="$CFLAGS -march=$i"
CXXFLAGS="$CXXFLAGS -march=$i"
LDFLAGS="$LDFLAGS -march=$i"
done
fi
])
])
AC_DEFUN([TORRENT_MINCORE_SIGNEDNESS], [ AC_DEFUN([TORRENT_MINCORE_SIGNEDNESS], [
AC_LANG_PUSH(C++) AC_LANG_PUSH(C++)
AC_MSG_CHECKING(signedness of mincore parameter) AC_MSG_CHECKING(signedness of mincore parameter)
@@ -90,7 +26,7 @@ AC_DEFUN([TORRENT_MINCORE_SIGNEDNESS], [
AC_MSG_RESULT(signed) AC_MSG_RESULT(signed)
], ],
[ [
AC_MSG_ERROR([failed, do *not* attempt fix this with --disable-mincore unless you are running Win32.]) AC_MSG_ERROR([failed, do *not* attempt fix this with --disable-mincore unless you are running Win32 or OpenBSD.])
]) ])
]) ])
@@ -144,108 +80,196 @@ AC_DEFUN([TORRENT_CHECK_POSIX_FADVISE], [
]) ])
]) ])
AC_DEFUN([TORRENT_CHECK_POPCOUNT], [
AC_MSG_CHECKING(for __builtin_popcount)
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
int f() { return __builtin_popcount(0); }
])],
[
AC_MSG_RESULT(yes)
AC_DEFINE(USE_BUILTIN_POPCOUNT, 1, Use __builtin_popcount.)
], [
AC_MSG_RESULT(no)
])
])
AC_DEFUN([TORRENT_CHECK_CACHELINE], [ AC_DEFUN([TORRENT_CHECK_CACHELINE], [
AC_MSG_CHECKING(for cacheline) AC_REQUIRE([AC_CANONICAL_HOST])
AC_MSG_CHECKING([for target cacheline size])
AC_COMPILE_IFELSE([AC_LANG_SOURCE([ case "$host_os" in
#include <stdlib.h> linux*)
#include <linux/cache.h> # REGION: Linux Kernel Extraction Loop
void* vptr __cacheline_aligned; AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
void f() { posix_memalign(&vptr, SMP_CACHE_BYTES, 42); } #include <stdlib.h>
])], #include <linux/cache.h>
[ void* vptr;
AC_MSG_RESULT(found builtin) void f() {
dnl Need to fix this so that it uses the stuff defined by the system. int res = posix_memalign(&vptr, SMP_CACHE_BYTES, 42);
(void)res;
}
]])],[
# We need an explicit variable fallback condition inside AC_COMPUTE_INT
AC_COMPUTE_INT([torrent_cv_cacheline_size], [SMP_CACHE_BYTES], [#include <linux/cache.h>], [torrent_cv_cacheline_size=0])
AC_DEFINE(LT_SMP_CACHE_BYTES, 128, Largest L1 cache size we know of should work on all archs.) if test "$torrent_cv_cacheline_size" -gt 0; then
], [ AC_MSG_RESULT([linux builtin ($torrent_cv_cacheline_size bytes)])
AC_MSG_RESULT(using default 128 bytes) AC_DEFINE_UNQUOTED([LT_SMP_CACHE_BYTES], [$torrent_cv_cacheline_size], [System-defined Linux L1 SMP cacheline size.])
AC_DEFINE(LT_SMP_CACHE_BYTES, 128, Largest L1 cache size we know of should work on all archs.) else
]) # Handle scenarios where macro maps to a complex runtime expression or fails
]) AC_MSG_RESULT([failed to parse SMP_CACHE_BYTES value])
AC_MSG_FAILURE([Linux kernel headers found, but cacheline constant could not be computed at compile-time.])
AC_DEFUN([TORRENT_CHECK_ALIGNED], [
AC_MSG_CHECKING(the byte alignment)
AC_RUN_IFELSE([AC_LANG_SOURCE([
#include <inttypes.h>
int main() {
char buf@<:@8@:>@ = { 0, 0, 0, 0, 1, 0, 0, 0 };
int i;
for (i = 1; i < 4; ++i)
if (*(uint32_t*)(buf + i) == 0) return -1;
return 0;
}
])],
[
AC_MSG_RESULT(none needed)
], [
AC_DEFINE(USE_ALIGNED, 1, Require byte alignment)
AC_MSG_RESULT(required)
])
])
AC_DEFUN([TORRENT_ENABLE_ALIGNED], [
AC_ARG_ENABLE(aligned,
AS_HELP_STRING([--enable-aligned],
[enable alignment safe code [[default=check]]]),
[
if test "$enableval" = "yes"; then
AC_DEFINE(USE_ALIGNED, 1, Require byte alignment)
fi fi
],[ ],[
TORRENT_CHECK_ALIGNED # Explicitly validate the CPU type even on Linux if the header check fails
]) case "$host_cpu" in
x86_64*|amd64*|i386*|i486*|i586*|i686*)
AC_MSG_RESULT([linux fallback x86 64 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Fallback 64-byte alignment for Linux x86 hardware.])
;;
arm*|aarch64*|powerpc*|ppc*|s390x*)
AC_MSG_RESULT([linux fallback enterprise 128 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 128, [Fallback 128-byte alignment for Linux enterprise hardware.])
;;
riscv32*|riscv64*)
AC_MSG_RESULT([linux fallback RISC-V 64 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Fallback 64-byte alignment for Linux RISC-V hardware.])
;;
loongarch32*|loongarch64*)
AC_MSG_RESULT([linux fallback LoongArch 64 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Fallback 64-byte alignment for Linux LoongArch hardware.])
;;
*)
AC_MSG_RESULT([unrecognized CPU arch on Linux header fallback])
AC_MSG_FAILURE([Unrecognized CPU architecture ($host_cpu) on Linux fallback path. Aborting build.])
;;
esac
])
;;
*)
# REGION: Cross-Platform Strict Hardware Mapping (macOS, FreeBSD, OpenBSD, NetBSD)
case "$host_cpu" in
x86_64*|amd64*|i386*|i486*|i586*|i686*)
# Explicit x86 desktop hardware baseline block
AC_MSG_RESULT([$host_os ($host_cpu) standard x86 64 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Standard 64-byte alignment for stable x86 hardware layout.])
;;
arm*|aarch64*|powerpc*|ppc*|s390x*)
# Explicit modern enterprise and Apple Silicon hardware baseline block
AC_MSG_RESULT([$host_os ($host_cpu) stable enterprise 128 bytes])
AC_DEFINE([LT_SMP_CACHE_BYTES], 128, [Optimized 128-byte alignment for newer high-performance chipsets.])
;;
*)
# STRICT ENFORCEMENT: Fail the build immediately if the CPU isn't explicitly known
AC_MSG_RESULT([unrecognized architecture])
AC_MSG_FAILURE([The target CPU architecture ($host_cpu) is unrecognized. Aborting configuration to prevent fatal runtime false-sharing or memory misalignment errors.])
;;
esac
;;
esac
]) ])
AC_DEFUN([TORRENT_DISABLE_INSTRUMENTATION], [ AC_DEFUN([TORRENT_CHECK_CUSTOM_ENDIAN64], [
AC_CHECK_HEADERS([endian.h sys/endian.h libkern/OSByteOrder.h])
AC_C_BIGENDIAN
AH_TEMPLATE([CUSTOM_ENDIAN_HEADER], [The system header to include for endian conversions.])
AH_TEMPLATE([custom_ntohll], [Convert 64-bit integer from network to host byte order.])
AH_TEMPLATE([custom_htonll], [Convert 64-bit integer from host to network byte order.])
dnl 1. Initialize our header variable tracking
endian_header_file='<stdint.h>'
dnl 2. Test if native be64toh works natively via <endian.h> (Standard Linux/glibc)
AC_MSG_CHECKING([for native be64toh via endian.h])
AC_LINK_IFELSE([AC_LANG_SOURCE([
#include <stdint.h>
#ifdef HAVE_ENDIAN_H
# include <endian.h>
#endif
int main() { uint64_t x = be64toh(1); return 0; }
])],
[has_native_be64=yes; endian_header_file='<endian.h>'],
[has_native_be64=no]
)
AC_MSG_RESULT([$has_native_be64])
dnl 3. If missing, test if it works via <sys/endian.h> (True BSD systems like FreeBSD)
if test "$has_native_be64" = "no"; then
AC_MSG_CHECKING([for native be64toh via sys/endian.h])
AC_LINK_IFELSE([AC_LANG_SOURCE([
#include <stdint.h>
#ifdef HAVE_SYS_ENDIAN_H
# include <sys/endian.h>
#endif
int main() { uint64_t x = be64toh(1); return 0; }
])],
[has_native_be64=yes; endian_header_file='<sys/endian.h>'],
[has_native_be64=no]
)
AC_MSG_RESULT([$has_native_be64])
fi
dnl 4. Route definitions cleanly based on actual testing results
if test "$has_native_be64" = "yes"; then
dnl Linux or true BSD environment
AC_DEFINE_UNQUOTED([CUSTOM_ENDIAN_HEADER], [$endian_header_file], [The header containing native endian macros.])
AC_DEFINE([custom_ntohll(x)], [(be64toh(x))], [Use native platform be64toh])
AC_DEFINE([custom_htonll(x)], [(htobe64(x))], [Use native platform htobe64])
else
dnl Fallback block: Check if we are on macOS using Apple's optimized hardware libraries
if test "$ac_cv_header_libkern_OSByteOrder_h" = "yes"; then
AC_DEFINE([CUSTOM_ENDIAN_HEADER], [<libkern/OSByteOrder.h>], [The header containing native endian macros.])
AC_DEFINE([custom_ntohll(x)], [(OSSwapBigToHostInt64(x))], [Map missing be64toh to macOS native swap])
AC_DEFINE([custom_htonll(x)], [(OSSwapHostToBigInt64(x))], [Map missing htobe64 to macOS native swap])
else
dnl Fallback for platforms with no built-in architecture macros (compiler built-ins)
AC_DEFINE([CUSTOM_ENDIAN_HEADER], [<stdint.h>], [The header containing native endian macros.])
if test "$ac_cv_c_bigendian" = "yes"; then
AC_DEFINE([custom_ntohll(x)], [((uint64_t)(x))], [Fallback 64-bit conversion.])
AC_DEFINE([custom_htonll(x)], [((uint64_t)(x))], [Fallback 64-bit conversion.])
else
AC_DEFINE([custom_ntohll(x)], [(__builtin_bswap64((uint64_t)(x)))], [Fallback 64-bit conversion.])
AC_DEFINE([custom_htonll(x)], [(__builtin_bswap64((uint64_t)(x)))], [Fallback 64-bit conversion.])
fi
fi
fi
])
AC_DEFUN([TORRENT_ENABLE_INSTRUMENTATION], [
AC_MSG_CHECKING([if instrumentation should be included]) AC_MSG_CHECKING([if instrumentation should be included])
AC_ARG_ENABLE(instrumentation, AC_ARG_ENABLE(instrumentation,
AS_HELP_STRING([--disable-instrumentation], AS_HELP_STRING([--enable-instrumentation],
[disable instrumentation [[default=enabled]]]), [enable instrumentation [[default=disabled]]]),
[ [
if test "$enableval" = "yes"; then if test "$enableval" = "yes"; then
AC_DEFINE(LT_INSTRUMENTATION, 1, enable instrumentation) AC_DEFINE(LT_INSTRUMENTATION, 1, enable instrumentation)
AC_MSG_RESULT(yes) AC_MSG_RESULT(yes)
else else
AC_MSG_RESULT(no) AC_MSG_RESULT(no)
fi fi
],[ ],[
AC_DEFINE(LT_INSTRUMENTATION, 1, enable instrumentation) AC_MSG_RESULT(no)
AC_MSG_RESULT(yes)
]) ])
]) ])
AC_DEFUN([TORRENT_ENABLE_INTERRUPT_SOCKET], [ AC_DEFUN([TORRENT_ENABLE_CUSTOM_STACK_SIZE], [
AC_ARG_ENABLE(interrupt-socket, AC_ARG_ENABLE([pthread-setstacksize],
AS_HELP_STRING([--enable-interrupt-socket], [AS_HELP_STRING([--enable-pthread-setstacksize], [explicitly set pthread stack size (auto-enabled for musl)])],
[enable interrupt socket [[default=no]]]), [enable_pthread_setstacksize=$enableval],
[ [enable_pthread_setstacksize=auto])
if test "$enableval" = "yes"; then
AC_DEFINE(USE_INTERRUPT_SOCKET, 1, Use interrupt socket instead of pthread_kill) AC_CANONICAL_HOST
fi if test "x$enable_pthread_setstacksize" = "xauto"; then
] case "$host" in
) *-musl*)
enable_pthread_setstacksize=yes
;;
*)
enable_pthread_setstacksize=no
;;
esac
fi
if test "x$enable_pthread_setstacksize" = "xyes"; then
AC_DEFINE([USE_PTHREAD_SETSTACKSIZE], [1], [Use explicit pthread stack size])
AC_DEFINE([DEFAULT_PTHREAD_STACKSIZE], [(8 * 1024 * 1024)], [Default pthread stack size in bytes])
fi
]) ])
AC_DEFUN([TORRENT_DISABLE_IPV6], [ AC_DEFUN([TORRENT_DISABLE_IPV6], [
AC_ARG_ENABLE(ipv6, AC_ARG_ENABLE(ipv6,
AS_HELP_STRING([--enable-ipv6], AS_HELP_STRING([--enable-ipv6],
+24 -7
View File
@@ -13,8 +13,6 @@ libsub_root_a_SOURCES = \
core/download_factory.h \ core/download_factory.h \
core/download_list.cc \ core/download_list.cc \
core/download_list.h \ core/download_list.h \
core/download_store.cc \
core/download_store.h \
core/http_queue.cc \ core/http_queue.cc \
core/http_queue.h \ core/http_queue.h \
core/manager.cc \ core/manager.cc \
@@ -125,6 +123,16 @@ libsub_root_a_SOURCES = \
rpc/tinyxml2/tinyxml2.cc \ rpc/tinyxml2/tinyxml2.cc \
rpc/nlohmann/json.h \ rpc/nlohmann/json.h \
\ \
scgi/thread_scgi.cc \
scgi/thread_scgi.h \
\
session/download_storer.cc \
session/download_storer.h \
session/session_manager.cc \
session/session_manager.h \
session/thread_session.cc \
session/thread_session.h \
\
ui/download.cc \ ui/download.cc \
ui/download.h \ ui/download.h \
ui/download_list.cc \ ui/download_list.cc \
@@ -161,11 +169,15 @@ libsub_root_a_SOURCES = \
utils/file_status_cache.cc \ utils/file_status_cache.cc \
utils/file_status_cache.h \ utils/file_status_cache.h \
utils/functional.h \ utils/functional.h \
utils/gzip.cc \
utils/gzip.h \
utils/list_focus.h \ utils/list_focus.h \
utils/lockfile.cc \ utils/lockfile.cc \
utils/lockfile.h \ utils/lockfile.h \
utils/socket_fd.cc \ utils/waitpid_queue.cc \
utils/socket_fd.h \ utils/waitpid_queue.h \
utils/watch_ready_queue.cc \
utils/watch_ready_queue.h \
\ \
command_download.cc \ command_download.cc \
command_dynamic.cc \ command_dynamic.cc \
@@ -182,6 +194,7 @@ libsub_root_a_SOURCES = \
command_throttle.cc \ command_throttle.cc \
command_tracker.cc \ command_tracker.cc \
command_scheduler.cc \ command_scheduler.cc \
command_string.cc \
command_ui.cc \ command_ui.cc \
control.cc \ control.cc \
control.h \ control.h \
@@ -189,10 +202,14 @@ libsub_root_a_SOURCES = \
globals.h \ globals.h \
option_parser.cc \ option_parser.cc \
option_parser.h \ option_parser.h \
setup.cc \
setup.h \
signal_handler.cc \ signal_handler.cc \
signal_handler.h \ signal_handler.h
thread_worker.cc \
thread_worker.h
if NO_NCURSES
libsub_root_a_SOURCES += display/curses_stub.cc display/curses_stub.h
endif
AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir) -DPACKAGE_DATADIR=\"$(pkgdatadir)\" AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir) -DPACKAGE_DATADIR=\"$(pkgdatadir)\"
+256 -97
View File
@@ -5,14 +5,10 @@
#include <functional> #include <functional>
#include <netdb.h> #include <netdb.h>
#include <unistd.h> #include <unistd.h>
#include <rak/file_stat.h> #include <fnmatch.h>
#include <rak/path.h>
#include <rak/string_manip.h>
#include <rak/regex.h>
#include <torrent/rate.h> #include <torrent/rate.h>
#include <torrent/throttle.h> #include <torrent/throttle.h>
#include <torrent/tracker/tracker.h> #include <torrent/tracker/tracker.h>
#include <torrent/connection_manager.h>
#include <torrent/data/download_data.h> #include <torrent/data/download_data.h>
#include <torrent/data/file.h> #include <torrent/data/file.h>
#include <torrent/data/file_list.h> #include <torrent/data/file_list.h>
@@ -21,41 +17,49 @@
#include <torrent/net/types.h> #include <torrent/net/types.h>
#include <torrent/peer/connection_list.h> #include <torrent/peer/connection_list.h>
#include <torrent/peer/peer_list.h> #include <torrent/peer/peer_list.h>
#include <torrent/system/callbacks.h>
#include <torrent/utils/file_stat.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h> #include <torrent/utils/option_strings.h>
#include <torrent/utils/string_manip.h>
#include "core/download.h" #include "core/download.h"
#include "core/download_store.h"
#include "core/manager.h" #include "core/manager.h"
#include "rpc/parse.h" #include "rpc/parse.h"
#include "session/session_manager.h"
#include "globals.h" #include "globals.h"
#include "control.h" #include "control.h"
#include "command_helpers.h" #include "command_helpers.h"
std::string torrent::string_utf8
retrieve_d_base_path(core::Download* download) { retrieve_d_base_path(core::Download* download) {
if (download->file_list()->is_multi_file()) if (download->file_list()->is_multi_file())
return download->file_list()->frozen_root_dir(); return download->file_list()->frozen_root_dir();
else
return download->file_list()->empty() ? std::string() : download->file_list()->at(0)->frozen_path(); if (download->file_list()->empty())
return {};
return download->file_list()->at(0)->frozen_path();
} }
std::string torrent::string_utf8
retrieve_d_base_filename(core::Download* download) { retrieve_d_base_filename(core::Download* download) {
const std::string* base; torrent::string_utf8 base_path;
if (download->file_list()->is_multi_file()) if (download->file_list()->is_multi_file())
base = &download->file_list()->frozen_root_dir(); base_path = download->file_list()->frozen_root_dir();
else if (!download->file_list()->empty())
base_path = download->file_list()->at(0)->frozen_path();
else else
base = &download->file_list()->at(0)->frozen_path(); return {};
std::string::size_type split = base->rfind('/'); auto split = base_path.str().rfind('/');
if (split == std::string::npos) if (split == std::string::npos)
return *base; return base_path;
else
return base->substr(split + 1); return torrent::string_utf8::from_string(base_path.str().substr(split + 1));
} }
torrent::Object torrent::Object
@@ -77,23 +81,23 @@ apply_d_change_link(core::Download* download, const torrent::Object::list_type&
if (type == "base_path") { if (type == "base_path") {
target = rpc::call_command_string("d.base_path", rpc::make_target(download)); target = rpc::call_command_string("d.base_path", rpc::make_target(download));
link = rak::path_expand(prefix + rpc::call_command_string("d.base_path", rpc::make_target(download)) + postfix); link = expand_path(prefix + rpc::call_command_string("d.base_path", rpc::make_target(download)) + postfix);
} else if (type == "base_filename") { } else if (type == "base_filename") {
target = rpc::call_command_string("d.base_path", rpc::make_target(download)); target = rpc::call_command_string("d.base_path", rpc::make_target(download));
link = rak::path_expand(prefix + rpc::call_command_string("d.base_filename", rpc::make_target(download)) + postfix); link = expand_path(prefix + rpc::call_command_string("d.base_filename", rpc::make_target(download)) + postfix);
// } else if (type == "directory_path") { // } else if (type == "directory_path") {
// target = rpc::call_command_string("d.directory", rpc::make_target(download)); // target = rpc::call_command_string("d.directory", rpc::make_target(download));
// link = rak::path_expand(prefix + rpc::call_command_string("d.base_path", rpc::make_target(download)) + postfix); // link = path_expand(prefix + rpc::call_command_string("d.base_path", rpc::make_target(download)) + postfix);
} else if (type == "tied") { } else if (type == "tied") {
link = rak::path_expand(rpc::call_command_string("d.tied_to_file", rpc::make_target(download))); link = expand_path(rpc::call_command_string("d.tied_to_file", rpc::make_target(download)));
if (link.empty()) if (link.empty())
return torrent::Object(); return torrent::Object();
link = rak::path_expand(prefix + link + postfix); link = expand_path(prefix + link + postfix);
target = rpc::call_command_string("d.base_path", rpc::make_target(download)); target = rpc::call_command_string("d.base_path", rpc::make_target(download));
} else { } else {
@@ -109,7 +113,7 @@ apply_d_change_link(core::Download* download, const torrent::Object::list_type&
case 1: case 1:
{ {
rak::file_stat fileStat; torrent::utils::FileStat fileStat;
errno = 0; errno = 0;
if (!fileStat.update_link(link) || !fileStat.is_link() || unlink(link.c_str()) == -1) if (!fileStat.update_link(link) || !fileStat.is_link() || unlink(link.c_str()) == -1)
@@ -131,7 +135,7 @@ apply_d_delete_tied(core::Download* download) {
if (tie.empty()) if (tie.empty())
return torrent::Object(); return torrent::Object();
if (::unlink(rak::path_expand(tie).c_str()) == -1) if (::unlink(expand_path(tie).c_str()) == -1)
control->core()->push_log_std("Could not unlink tied file: " + std::string(std::strerror(errno))); control->core()->push_log_std("Could not unlink tied file: " + std::string(std::strerror(errno)));
rpc::call_command("d.tied_to_file.set", std::string(), rpc::make_target(download)); rpc::call_command("d.tied_to_file.set", std::string(), rpc::make_target(download));
@@ -143,9 +147,9 @@ apply_d_directory(core::Download* download, const std::string& name) {
if (!download->file_list()->is_multi_file()) if (!download->file_list()->is_multi_file())
download->set_root_directory(name); download->set_root_directory(name);
else if (name.empty() || *name.rbegin() == '/') else if (name.empty() || *name.rbegin() == '/')
download->set_root_directory(name + download->info()->name()); download->set_root_directory(name + download->info()->name().str());
else else
download->set_root_directory(name + "/" + download->info()->name()); download->set_root_directory(name + "/" + download->info()->name().str());
} }
torrent::Object torrent::Object
@@ -159,8 +163,8 @@ apply_d_connection_type(core::Download* download, const std::string& name) {
torrent::Object torrent::Object
apply_d_choke_heuristics(core::Download* download, const std::string& name, bool is_down) { apply_d_choke_heuristics(core::Download* download, const std::string& name, bool is_down) {
torrent::Download::HeuristicType t = torrent::heuristics_enum t =
(torrent::Download::HeuristicType)torrent::option_find_string(torrent::OPTION_CHOKE_HEURISTICS, name.c_str()); static_cast<torrent::heuristics_enum>(torrent::option_find_string(torrent::OPTION_CHOKE_HEURISTICS, name.c_str()));
if (is_down) if (is_down)
download->download()->set_download_choke_heuristic(t); download->download()->set_download_choke_heuristic(t);
@@ -270,16 +274,6 @@ retrieve_d_custom_map(core::Download* download, bool keys_only, const torrent::O
return result; return result;
} }
torrent::Object
retrieve_d_bitfield(core::Download* download) {
const torrent::Bitfield* bitField = download->download()->file_list()->bitfield();
if (bitField->empty())
return torrent::Object("");
return torrent::Object(rak::transform_hex(bitField->begin(), bitField->end()));
}
void void
apply_d_add_peer(core::Download* download, const std::string& arg) { apply_d_add_peer(core::Download* download, const std::string& arg) {
int port, ret; int port, ret;
@@ -302,10 +296,12 @@ apply_d_add_peer(core::Download* download, const std::string& arg) {
if (port < 1 || port > 65535) if (port < 1 || port > 65535)
throw torrent::input_error("Invalid port number."); throw torrent::input_error("Invalid port number.");
assert(std::this_thread::get_id() == torrent::main_thread::thread()->thread_id()); assert(std::this_thread::get_id() == torrent::main_thread::thread_id());
auto callback_id = torrent::system::make_callback_id();
// Currently discarding SOCK_STREAM. // Currently discarding SOCK_STREAM.
torrent::this_thread::resolver()->resolve_preferred(NULL, host, AF_UNSPEC, AF_INET, [download, port](torrent::c_sa_shared_ptr sa, int err) { torrent::this_thread::resolver()->resolve_preferred(callback_id, host, AF_UNSPEC, AF_INET, [download, port](torrent::c_sa_shared_ptr sa, int err) {
if (sa == nullptr) { if (sa == nullptr) {
lt_log_print(torrent::LOG_TORRENT_WARN, "could not resolve hostname for added peer: %s", gai_strerror(err)); lt_log_print(torrent::LOG_TORRENT_WARN, "could not resolve hostname for added peer: %s", gai_strerror(err));
return; return;
@@ -327,7 +323,8 @@ d_chunks_seen(core::Download* download) {
std::string result; std::string result;
result.resize(size * 2); result.resize(size * 2);
rak::transform_hex((const char*)seen, (const char*)seen + size, result.begin()); torrent::utils::transform_to_hex(seen, seen + size, result);
return result; return result;
} }
@@ -343,7 +340,7 @@ f_multicall(core::Download* download, const torrent::Object::list_type& args) {
// parsing and searching command map for every single call. // parsing and searching command map for every single call.
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();
std::vector<rak::regex> regex_list; std::vector<std::string> regex_list;
bool use_regex = true; bool use_regex = true;
@@ -357,7 +354,7 @@ f_multicall(core::Download* download, const torrent::Object::list_type& args) {
for (const auto& file : *download->file_list()) { for (const auto& file : *download->file_list()) {
if (use_regex && if (use_regex &&
std::none_of(regex_list.begin(), regex_list.end(), [&file](const auto& r) { return r(file->path()->as_string()); })) std::none_of(regex_list.begin(), regex_list.end(), [&file](const auto& pattern) { return fnmatch(pattern.c_str(), file->path()->as_string().c_str(), 0) == 0; }))
continue; 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();
@@ -381,18 +378,19 @@ t_multicall(core::Download* download, 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.
torrent::Object result_raw = torrent::Object::create_list();
torrent::Object::list_type& result = result_raw.as_list(); auto result_raw = torrent::Object::create_list();
auto& result = result_raw.as_list();
for (uint32_t idx = 0, last = download->tracker_list_size(); idx < last; idx++) { for (uint32_t idx = 0, last = download->tracker_list_size(); idx < last; idx++) {
auto& row = result.insert(result.end(), torrent::Object::create_list())->as_list(); auto& row = result.insert(result.end(), torrent::Object::create_list())->as_list();
auto tracker = download->tracker_controller().at(idx); auto tracker = download->tracker_controller().at(idx);
if (!tracker.is_valid()) if (!tracker.is_valid())
continue; continue;
for (torrent::Object::list_const_iterator cItr = ++args.begin(); cItr != args.end(); cItr++) { for (auto cItr = ++args.begin(); cItr != args.end(); cItr++) {
const std::string& cmd = cItr->as_string(); auto& cmd = cItr->as_string();
row.push_back(rpc::parse_command(rpc::make_target(&tracker), cmd.c_str(), cmd.c_str() + cmd.size()).first); row.push_back(rpc::parse_command(rpc::make_target(&tracker), cmd.c_str(), cmd.c_str() + cmd.size()).first);
} }
@@ -411,13 +409,13 @@ p_multicall(core::Download* download, 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.
torrent::Object resultRaw = torrent::Object::create_list(); auto resultRaw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list(); auto& result = resultRaw.as_list();
for (const auto& connection : *download->connection_list()) { for (const auto& connection : *download->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 (torrent::Object::list_const_iterator 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);
@@ -445,9 +443,11 @@ p_call_target(const torrent::Object::list_type& args) {
torrent::HashString hash; torrent::HashString hash;
if (peer_id.size() != 40 || if (peer_id.size() != 40)
torrent::hash_string_from_hex_c_str(peer_id.c_str(), hash) == peer_id.c_str()) throw torrent::input_error("invalid argument: peer id target is not 40 bytes long");
throw torrent::input_error("Not a hash string.");
if (torrent::utils::transform_from_hex(peer_id.c_str(), peer_id.c_str() + 40, hash) != hash.end())
throw torrent::input_error("invalid argument: peer id target is not a hex string");
torrent::ConnectionList::iterator peerItr = download->connection_list()->find(hash.c_str()); torrent::ConnectionList::iterator peerItr = download->connection_list()->find(hash.c_str());
@@ -468,12 +468,14 @@ download_tracker_insert(core::Download* download, const torrent::Object::list_ty
if (args.size() != 2) if (args.size() != 2)
throw torrent::input_error("Wrong argument count."); throw torrent::input_error("Wrong argument count.");
int64_t group; int64_t group = 0;
if (args.front().is_string()) if (args.front().is_string()) {
rpc::parse_whole_value_nothrow(args.front().as_string().c_str(), &group); if (!rpc::parse_whole_value_nothrow(args.front().as_string().c_str(), &group))
else throw torrent::input_error("Invalid tracker group number.");
} else {
group = args.front().as_value(); group = args.front().as_value();
}
if (group < 0 || group > 32) if (group < 0 || group > 32)
throw torrent::input_error("Tracker group number invalid."); throw torrent::input_error("Tracker group number invalid.");
@@ -495,6 +497,19 @@ download_get_variable(core::Download* download, const char* first_key, const cha
return download->bencode()->get_key(first_key).get_key(second_key); return download->bencode()->get_key(first_key).get_key(second_key);
} }
torrent::Object
download_get_value_or_zero(core::Download* download, const char* first_key, const char* second_key = NULL) {
auto object = download_get_variable(download, first_key, second_key);
if (object.is_empty())
return int64_t(0);
if (!object.is_value())
throw torrent::bencode_error("Download variable is not a value.");
return object;
}
torrent::Object torrent::Object
download_set_variable(core::Download* download, const torrent::Object& rawArgs, const char* first_key, const char* second_key = NULL) { download_set_variable(core::Download* download, const torrent::Object& rawArgs, const char* first_key, const char* second_key = NULL) {
if (second_key == NULL) if (second_key == NULL)
@@ -539,16 +554,13 @@ download_set_variable_string(core::Download* download, const torrent::Object::st
// //
torrent::Object torrent::Object
d_list_push_back_string(core::Download* download, const torrent::Object& rawArgs, const char* first_key, const char* second_key) { d_list_push_back_string(core::Download* download, const std::string& arg, const char* first_key, const char* second_key) {
auto& arg = rawArgs.as_string();
download_get_variable(download, first_key, second_key).as_list().push_back(arg); download_get_variable(download, first_key, second_key).as_list().push_back(arg);
return torrent::Object(); return torrent::Object();
} }
torrent::Object torrent::Object
d_list_push_back_unique_string(core::Download* download, const torrent::Object& rawArgs, const char* first_key, const char* second_key) { d_list_push_back_unique_string(core::Download* download, const std::string& arg, const char* first_key, const char* second_key) {
auto& arg = rawArgs.as_string();
torrent::Object::list_type& list = download_get_variable(download, first_key, second_key).as_list(); torrent::Object::list_type& list = download_get_variable(download, first_key, second_key).as_list();
if (std::none_of(list.begin(), list.end(), [arg](const torrent::Object& obj) { return torrent::object_equal(obj, arg); })) if (std::none_of(list.begin(), list.end(), [arg](const torrent::Object& obj) { return torrent::object_equal(obj, arg); }))
@@ -603,12 +615,16 @@ d_list_remove(core::Download* download, const torrent::Object& rawArgs, const ch
#define CMD2_DL_TIMESTAMP(key, first_key, second_key) \ #define CMD2_DL_TIMESTAMP(key, first_key, second_key) \
CMD2_DL(key, std::bind(&download_get_variable, std::placeholders::_1, first_key, second_key)); \ CMD2_DL(key, std::bind(&download_get_variable, std::placeholders::_1, first_key, second_key)); \
CMD2_DL_VALUE_P(key ".set", std::bind(&download_set_variable_value, \ CMD2_DL_VALUE_P(key ".set", std::bind(&download_set_variable_value, \
std::placeholders::_1, std::placeholders::_2, \ std::placeholders::_1, std::placeholders::_2, \
first_key, second_key)); \ first_key, second_key)); \
CMD2_DL_VALUE_P(key ".set_if_z", std::bind(&download_set_variable_value_ifz, \ CMD2_DL_VALUE_P(key ".set_if_z", std::bind(&download_set_variable_value_ifz, \
std::placeholders::_1, std::placeholders::_2, \ std::placeholders::_1, std::placeholders::_2, \
first_key, second_key)); \ first_key, second_key)); \
CMD2_DL(key ".or_zero", [](core::Download* download, auto) { \
return download_get_value_or_zero(download, first_key, second_key); }); \
CMD2_DL_VALUE(key ".elapsed", [](core::Download* download, auto value) { \
return download_get_value_or_zero(download, first_key, second_key).as_value() > value; });
#define CMD2_DL_VAR_STRING(key, first_key, second_key) \ #define CMD2_DL_VAR_STRING(key, first_key, second_key) \
CMD2_DL(key, std::bind(&download_get_variable, std::placeholders::_1, first_key, second_key)); \ CMD2_DL(key, std::bind(&download_get_variable, std::placeholders::_1, first_key, second_key)); \
@@ -628,30 +644,50 @@ void cg_d_group_set(core::Download* download, const torrent::Objec
void void
initialize_command_download() { initialize_command_download() {
CMD2_DL("d.hash", std::bind(&rak::transform_hex_str<torrent::HashString>, CMD2_ON_INFO(hash))); CMD2_DL("d.hash", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(download->info()->hash()); });
CMD2_DL("d.local_id", std::bind(&rak::transform_hex_str<torrent::HashString>, CMD2_ON_INFO(local_id))); CMD2_DL("d.local_id", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(download->info()->local_id()); });
CMD2_DL("d.local_id_html", std::bind(&rak::copy_escape_html_str<torrent::HashString>, CMD2_ON_INFO(local_id))); CMD2_DL("d.local_id_html", [](auto* download, auto) { return torrent::utils::copy_escape_html_str(download->info()->local_id()); });
CMD2_DL("d.bitfield", std::bind(&retrieve_d_bitfield, std::placeholders::_1)); CMD2_DL("d.bitfield", [](auto* download, auto) { return torrent::utils::transform_to_hex_str(*download->download()->file_list()->bitfield()); });
CMD2_DL("d.base_path", std::bind(&retrieve_d_base_path, std::placeholders::_1)); CMD2_DL("d.base_path", [](auto* download, auto) { return retrieve_d_base_path(download).str(); });
CMD2_DL("d.base_filename", std::bind(&retrieve_d_base_filename, std::placeholders::_1)); CMD2_DL("d.base_path.hex", [](auto* download, auto) { return retrieve_d_base_path(download).object_hex(); });
CMD2_DL("d.base_path.base64", [](auto* download, auto) { return retrieve_d_base_path(download).object_base64(); });
CMD2_DL("d.base_path.base64_as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_base64_as_binary(); });
CMD2_DL("d.base_path.as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_as_binary(); });
CMD2_DL("d.base_path.or_base64", [](auto* download, auto) { return retrieve_d_base_path(download).object_utf8_or_base64(); });
CMD2_DL("d.base_path.or_as_binary", [](auto* download, auto) { return retrieve_d_base_path(download).object_utf8_or_as_binary(); });
CMD2_DL("d.base_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.hex", [](auto* download, auto) { return retrieve_d_base_filename(download).object_hex(); });
CMD2_DL("d.base_filename.base64", [](auto* download, auto) { return retrieve_d_base_filename(download).object_base64(); });
CMD2_DL("d.base_filename.base64_as_binary", [](auto* download, auto) { return retrieve_d_base_filename(download).object_base64_as_binary(); });
CMD2_DL("d.base_filename.as_binary", [](auto* download, auto) { return retrieve_d_base_filename(download).object_as_binary(); });
CMD2_DL("d.base_filename.or_base64", [](auto* download, auto) { return retrieve_d_base_filename(download).object_utf8_or_base64(); });
CMD2_DL("d.base_filename.or_as_binary", [](auto* download, auto) { return retrieve_d_base_filename(download).object_utf8_or_as_binary(); });
CMD2_DL("d.name", CMD2_ON_INFO(name)); CMD2_DL("d.name", [](auto* download, auto) { return download->info()->name().str(); });
CMD2_DL("d.creation_date", CMD2_ON_INFO(creation_date)); CMD2_DL("d.name.hex", [](auto* download, auto) { return download->info()->name().object_hex(); });
CMD2_DL("d.load_date", CMD2_ON_INFO(load_date)); CMD2_DL("d.name.base64", [](auto* download, auto) { return download->info()->name().object_base64(); });
CMD2_DL("d.name.base64_as_binary", [](auto* download, auto) { return download->info()->name().object_base64_as_binary(); });
CMD2_DL("d.name.as_binary", [](auto* download, auto) { return download->info()->name().object_as_binary(); });
CMD2_DL("d.name.or_base64", [](auto* download, auto) { return download->info()->name().object_utf8_or_base64(); });
CMD2_DL("d.name.or_as_binary", [](auto* download, auto) { return download->info()->name().object_utf8_or_as_binary(); });
CMD2_DL("d.creation_date", [](auto* download, auto) { return download->info()->creation_date(); });
CMD2_DL("d.load_date", [](auto* download, auto) { return download->info()->load_date(); });
// //
// Network related: // Network related:
// //
CMD2_DL ("d.up.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(up_rate))); CMD2_DL ("d.up.rate", [](auto* download, auto) { return download->info()->up_rate()->rate(); });
CMD2_DL ("d.up.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(up_rate))); CMD2_DL ("d.up.total", [](auto* download, auto) { return download->info()->up_rate()->total(); });
CMD2_DL ("d.down.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(down_rate))); CMD2_DL ("d.down.rate", [](auto* download, auto) { return download->info()->down_rate()->rate(); });
CMD2_DL ("d.down.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(down_rate))); CMD2_DL ("d.down.total", [](auto* download, auto) { return download->info()->down_rate()->total(); });
CMD2_DL ("d.skip.rate", std::bind(&torrent::Rate::rate, CMD2_ON_INFO(skip_rate))); CMD2_DL ("d.skip.rate", [](auto* download, auto) { return download->info()->skip_rate()->rate(); });
CMD2_DL ("d.skip.total", std::bind(&torrent::Rate::total, CMD2_ON_INFO(skip_rate))); CMD2_DL ("d.skip.total", [](auto* download, auto) { return download->info()->skip_rate()->total(); });
CMD2_DL ("d.peer_exchange", CMD2_ON_INFO(is_pex_enabled)); CMD2_DL ("d.peer_exchange", [](auto* download, auto) { return download->info()->is_pex_enabled(); });
CMD2_DL_VALUE_V ("d.peer_exchange.set", std::bind(&torrent::Download::set_pex_enabled, CMD2_BIND_DL, std::placeholders::_2)); CMD2_DL_VALUE_V ("d.peer_exchange.set", [](auto* download, auto value) { download->download()->set_pex_enabled(value); });
CMD2_DL_LIST ("d.create_link", std::bind(&apply_d_change_link, std::placeholders::_1, std::placeholders::_2, 0)); CMD2_DL_LIST ("d.create_link", std::bind(&apply_d_change_link, std::placeholders::_1, std::placeholders::_2, 0));
CMD2_DL_LIST ("d.delete_link", std::bind(&apply_d_change_link, std::placeholders::_1, std::placeholders::_2, 1)); CMD2_DL_LIST ("d.delete_link", std::bind(&apply_d_change_link, std::placeholders::_1, std::placeholders::_2, 1));
@@ -667,16 +703,16 @@ initialize_command_download() {
// Control functinos: // Control functinos:
// //
CMD2_DL ("d.is_open", CMD2_ON_INFO(is_open)); CMD2_DL ("d.is_open", [](auto* download, auto) { return download->info()->is_open(); });
CMD2_DL ("d.is_active", CMD2_ON_INFO(is_active)); CMD2_DL ("d.is_active", [](auto* download, auto) { return download->info()->is_active(); });
CMD2_DL ("d.is_hash_checked", std::bind(&torrent::Download::is_hash_checked, CMD2_BIND_DL)); CMD2_DL ("d.is_hash_checked", std::bind(&torrent::Download::is_hash_checked, CMD2_BIND_DL));
CMD2_DL ("d.is_hash_checking", std::bind(&torrent::Download::is_hash_checking, CMD2_BIND_DL)); CMD2_DL ("d.is_hash_checking", std::bind(&torrent::Download::is_hash_checking, CMD2_BIND_DL));
CMD2_DL ("d.is_multi_file", std::bind(&torrent::FileList::is_multi_file, CMD2_BIND_FL)); CMD2_DL ("d.is_multi_file", std::bind(&torrent::FileList::is_multi_file, CMD2_BIND_FL));
CMD2_DL ("d.is_private", CMD2_ON_INFO(is_private)); CMD2_DL ("d.is_private", [](auto* download, auto) { return download->info()->is_private(); });
CMD2_DL ("d.is_pex_active", CMD2_ON_INFO(is_pex_active)); CMD2_DL ("d.is_pex_active", [](auto* download, auto) { return download->info()->is_pex_active(); });
CMD2_DL ("d.is_partially_done", CMD2_ON_DATA(is_partially_done)); CMD2_DL ("d.is_partially_done", CMD2_ON_DATA(is_partially_done));
CMD2_DL ("d.is_not_partially_done", CMD2_ON_DATA(is_not_partially_done)); CMD2_DL ("d.is_not_partially_done", CMD2_ON_DATA(is_not_partially_done));
CMD2_DL ("d.is_meta", CMD2_ON_INFO(is_meta_download)); CMD2_DL ("d.is_meta", [](auto* download, auto) { return download->info()->is_meta_download(); });
CMD2_DL_V ("d.resume", std::bind(&core::DownloadList::resume_default, control->core()->download_list(), std::placeholders::_1)); CMD2_DL_V ("d.resume", std::bind(&core::DownloadList::resume_default, control->core()->download_list(), std::placeholders::_1));
CMD2_DL_V ("d.pause", std::bind(&core::DownloadList::pause_default, control->core()->download_list(), std::placeholders::_1)); CMD2_DL_V ("d.pause", std::bind(&core::DownloadList::pause_default, control->core()->download_list(), std::placeholders::_1));
@@ -686,8 +722,8 @@ initialize_command_download() {
CMD2_DL_V ("d.erase", std::bind(&core::DownloadList::erase_ptr, control->core()->download_list(), std::placeholders::_1)); CMD2_DL_V ("d.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));
CMD2_DL ("d.save_resume", std::bind(&core::DownloadStore::save_resume, control->core()->download_store(), std::placeholders::_1)); CMD2_DL_V ("d.save_resume", [](core::Download* download, auto) { session_thread::manager()->save_resume_download(download); });
CMD2_DL ("d.save_full_session", std::bind(&core::DownloadStore::save_full, control->core()->download_store(), std::placeholders::_1)); CMD2_DL_V ("d.save_full_session", [](core::Download* download, auto) { session_thread::manager()->save_full_download(download); });
CMD2_DL_V ("d.update_priorities", CMD2_ON_DL(update_priorities)); CMD2_DL_V ("d.update_priorities", CMD2_ON_DL(update_priorities));
@@ -737,6 +773,11 @@ 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.
@@ -747,15 +788,15 @@ initialize_command_download() {
CMD2_DL_TIMESTAMP("d.timestamp.started", "rtorrent", "timestamp.started"); CMD2_DL_TIMESTAMP("d.timestamp.started", "rtorrent", "timestamp.started");
CMD2_DL_TIMESTAMP("d.timestamp.finished", "rtorrent", "timestamp.finished"); CMD2_DL_TIMESTAMP("d.timestamp.finished", "rtorrent", "timestamp.finished");
CMD2_DL ("d.connection_current", std::bind(&torrent::option_as_string, torrent::OPTION_CONNECTION_TYPE, CMD2_ON_DL(connection_type))); CMD2_DL ("d.connection_current", [](auto* d, auto) { return torrent::option_to_c_str_or_throw(torrent::OPTION_CONNECTION_TYPE, d->download()->connection_type()); });
CMD2_DL_STRING("d.connection_current.set", std::bind(&apply_d_connection_type, std::placeholders::_1, std::placeholders::_2)); CMD2_DL_STRING_V("d.connection_current.set", [](auto* d, auto arg) { apply_d_connection_type(d, arg); });
CMD2_DL_VAR_STRING("d.connection_leech", "rtorrent", "connection_leech"); CMD2_DL_VAR_STRING("d.connection_leech", "rtorrent", "connection_leech");
CMD2_DL_VAR_STRING("d.connection_seed", "rtorrent", "connection_seed"); CMD2_DL_VAR_STRING("d.connection_seed", "rtorrent", "connection_seed");
CMD2_DL ("d.up.choke_heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS, CMD2_ON_DL(upload_choke_heuristic))); CMD2_DL ("d.up.choke_heuristics", [](auto* d, auto) { return torrent::option_to_c_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, d->download()->upload_choke_heuristic()); });
CMD2_DL_STRING("d.up.choke_heuristics.set", std::bind(&apply_d_choke_heuristics, std::placeholders::_1, std::placeholders::_2, false)); CMD2_DL_STRING("d.up.choke_heuristics.set", std::bind(&apply_d_choke_heuristics, std::placeholders::_1, std::placeholders::_2, false));
CMD2_DL ("d.down.choke_heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS, CMD2_ON_DL(download_choke_heuristic))); CMD2_DL ("d.down.choke_heuristics", [](auto* d, auto) { return torrent::option_to_c_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, d->download()->download_choke_heuristic()); });
CMD2_DL_STRING("d.down.choke_heuristics.set", std::bind(&apply_d_choke_heuristics, std::placeholders::_1, std::placeholders::_2, true)); CMD2_DL_STRING("d.down.choke_heuristics.set", std::bind(&apply_d_choke_heuristics, std::placeholders::_1, std::placeholders::_2, true));
CMD2_DL_VAR_STRING("d.up.choke_heuristics.leech", "rtorrent", "choke_heuristics.up.leech"); CMD2_DL_VAR_STRING("d.up.choke_heuristics.leech", "rtorrent", "choke_heuristics.up.leech");
@@ -769,8 +810,8 @@ initialize_command_download() {
CMD2_DL ("d.views", std::bind(&download_get_variable, std::placeholders::_1, "rtorrent", "views")); CMD2_DL ("d.views", std::bind(&download_get_variable, std::placeholders::_1, "rtorrent", "views"));
CMD2_DL ("d.views.has", std::bind(&d_list_has, std::placeholders::_1, std::placeholders::_2, "rtorrent", "views")); CMD2_DL ("d.views.has", std::bind(&d_list_has, std::placeholders::_1, std::placeholders::_2, "rtorrent", "views"));
CMD2_DL ("d.views.remove", std::bind(&d_list_remove, std::placeholders::_1, std::placeholders::_2, "rtorrent", "views")); CMD2_DL ("d.views.remove", std::bind(&d_list_remove, std::placeholders::_1, std::placeholders::_2, "rtorrent", "views"));
CMD2_DL ("d.views.push_back", std::bind(&d_list_push_back_string, std::placeholders::_1, std::placeholders::_2, "rtorrent", "views")); CMD2_DL_STRING ("d.views.push_back", std::bind(&d_list_push_back_string, std::placeholders::_1, std::placeholders::_2, "rtorrent", "views"));
CMD2_DL ("d.views.push_back_unique", std::bind(&d_list_push_back_unique_string, std::placeholders::_1, std::placeholders::_2, "rtorrent", "views")); CMD2_DL_STRING ("d.views.push_back_unique", std::bind(&d_list_push_back_unique_string, std::placeholders::_1, std::placeholders::_2, "rtorrent", "views"));
// This command really needs to be improved, so we have proper // This command really needs to be improved, so we have proper
// logging support. // logging support.
@@ -810,7 +851,7 @@ initialize_command_download() {
CMD2_DL ("d.bytes_done", CMD2_ON_DL(bytes_done)); CMD2_DL ("d.bytes_done", CMD2_ON_DL(bytes_done));
CMD2_DL ("d.ratio", std::bind(&retrieve_d_ratio, std::placeholders::_1)); CMD2_DL ("d.ratio", std::bind(&retrieve_d_ratio, std::placeholders::_1));
CMD2_DL ("d.chunks_hashed", CMD2_ON_DL(chunks_hashed)); CMD2_DL ("d.chunks_hashed", CMD2_ON_DL(chunks_hashed));
CMD2_DL ("d.free_diskspace", CMD2_ON_FL(free_diskspace)); CMD2_DL ("d.free_diskspace", [](auto* download, auto) { return download->file_list()->free_diskspace_no_cache(); });
CMD2_DL ("d.size_files", CMD2_ON_FL(size_files)); CMD2_DL ("d.size_files", CMD2_ON_FL(size_files));
CMD2_DL ("d.size_bytes", CMD2_ON_FL(size_bytes)); CMD2_DL ("d.size_bytes", CMD2_ON_FL(size_bytes));
@@ -844,6 +885,8 @@ 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 ("d.directory.realpath.or_throw", [](auto* download, auto) { return resolve_path_or_throw(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_STRING_V("d.directory.set", std::bind(&apply_d_directory, std::placeholders::_1, std::placeholders::_2));
CMD2_DL ("d.directory_base", CMD2_ON_FL(root_dir)); CMD2_DL ("d.directory_base", CMD2_ON_FL(root_dir));
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", std::bind(&core::Download::set_root_directory, std::placeholders::_1, std::placeholders::_2));
@@ -871,4 +914,120 @@ initialize_command_download() {
CMD2_DL_LIST ("t.multicall", std::bind(&t_multicall, std::placeholders::_1, std::placeholders::_2)); CMD2_DL_LIST ("t.multicall", std::bind(&t_multicall, std::placeholders::_1, std::placeholders::_2));
CMD2_ANY_LIST ("p.call_target", std::bind(&p_call_target, std::placeholders::_2)); CMD2_ANY_LIST ("p.call_target", std::bind(&p_call_target, std::placeholders::_2));
rpc::rpc.mark_safe("add_peer");
rpc::rpc.mark_safe("d.hash");
rpc::rpc.mark_safe("d.local_id");
rpc::rpc.mark_safe("d.local_id_html");
rpc::rpc.mark_safe("d.bitfield");
rpc::rpc.mark_safe("d.base_path");
rpc::rpc.mark_safe("d.base_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.base64");
rpc::rpc.mark_safe("d.base_path.base64_as_binary");
rpc::rpc.mark_safe("d.base_path.as_binary");
rpc::rpc.mark_safe("d.base_path.or_base64");
rpc::rpc.mark_safe("d.base_path.or_as_binary");
rpc::rpc.mark_safe("d.base_filename");
rpc::rpc.mark_safe("d.base_filename.hex");
rpc::rpc.mark_safe("d.base_filename.base64");
rpc::rpc.mark_safe("d.base_filename.base64_as_binary");
rpc::rpc.mark_safe("d.base_filename.as_binary");
rpc::rpc.mark_safe("d.base_filename.or_base64");
rpc::rpc.mark_safe("d.base_filename.or_as_binary");
rpc::rpc.mark_safe("d.name");
rpc::rpc.mark_safe("d.name.hex");
rpc::rpc.mark_safe("d.name.base64");
rpc::rpc.mark_safe("d.name.base64_as_binary");
rpc::rpc.mark_safe("d.name.as_binary");
rpc::rpc.mark_safe("d.name.or_base64");
rpc::rpc.mark_safe("d.name.or_as_binary");
rpc::rpc.mark_safe("d.directory");
rpc::rpc.mark_safe("d.directory_base");
rpc::rpc.mark_safe("d.creation_date");
rpc::rpc.mark_safe("d.load_date");
rpc::rpc.mark_safe("d.up.rate");
rpc::rpc.mark_safe("d.up.total");
rpc::rpc.mark_safe("d.down.rate");
rpc::rpc.mark_safe("d.down.total");
rpc::rpc.mark_safe("d.skip.rate");
rpc::rpc.mark_safe("d.skip.total");
rpc::rpc.mark_safe("d.is_open");
rpc::rpc.mark_safe("d.is_active");
rpc::rpc.mark_safe("d.is_hash_checked");
rpc::rpc.mark_safe("d.is_hash_checking");
rpc::rpc.mark_safe("d.is_multi_file");
rpc::rpc.mark_safe("d.is_private");
rpc::rpc.mark_safe("d.is_pex_active");
rpc::rpc.mark_safe("d.is_partially_done");
rpc::rpc.mark_safe("d.is_not_partially_done");
rpc::rpc.mark_safe("d.is_meta");
rpc::rpc.mark_safe("d.peer_exchange");
rpc::rpc.mark_safe("d.resume");
rpc::rpc.mark_safe("d.pause");
rpc::rpc.mark_safe("d.open");
rpc::rpc.mark_safe("d.close");
rpc::rpc.mark_safe("d.close.directly");
rpc::rpc.mark_safe("d.erase");
rpc::rpc.mark_safe("d.check_hash");
rpc::rpc.mark_safe("d.save_resume");
rpc::rpc.mark_safe("d.save_full_session");
rpc::rpc.mark_safe("d.update_priorities");
rpc::rpc.mark_safe("d.custom");
rpc::rpc.mark_safe("d.custom1");
rpc::rpc.mark_safe("d.custom2");
rpc::rpc.mark_safe("d.custom3");
rpc::rpc.mark_safe("d.custom4");
rpc::rpc.mark_safe("d.custom5");
rpc::rpc.mark_safe("d.size_bytes");
rpc::rpc.mark_safe("d.size_chunks");
rpc::rpc.mark_safe("d.size_pex");
rpc::rpc.mark_safe("d.completed_bytes");
rpc::rpc.mark_safe("d.complete");
rpc::rpc.mark_safe("d.timestamp.started");
rpc::rpc.mark_safe("d.timestamp.finished");
rpc::rpc.mark_safe("d.bytes_done");
rpc::rpc.mark_safe("d.peers_accounted");
rpc::rpc.mark_safe("d.chunks_hashed");
rpc::rpc.mark_safe("d.tracker_size");
rpc::rpc.mark_safe("d.completed_chunks");
rpc::rpc.mark_safe("d.left_bytes");
rpc::rpc.mark_safe("d.chunk_size");
rpc::rpc.mark_safe("d.priority");
rpc::rpc.mark_safe("d.priority_str");
rpc::rpc.mark_safe("d.state");
rpc::rpc.mark_safe("d.state_changed");
rpc::rpc.mark_safe("d.state_counter");
rpc::rpc.mark_safe("d.connection_current");
rpc::rpc.mark_safe("d.connection_leech");
rpc::rpc.mark_safe("d.connection_seed");
rpc::rpc.mark_safe("d.throttle_name");
rpc::rpc.mark_safe("d.uploads_max");
rpc::rpc.mark_safe("d.downloads_max");
rpc::rpc.mark_safe("d.peers_min");
rpc::rpc.mark_safe("d.peers_max");
rpc::rpc.mark_safe("d.peers_connected");
rpc::rpc.mark_safe("d.peers_not_connected");
rpc::rpc.mark_safe("d.peers_complete");
rpc::rpc.mark_safe("d.tracker_numwant");
rpc::rpc.mark_safe("d.tracker_focus");
rpc::rpc.mark_safe("d.message");
rpc::rpc.mark_safe("d.hashing");
rpc::rpc.mark_safe("d.hashing_failed");
rpc::rpc.mark_safe("d.free_diskspace");
rpc::rpc.mark_safe("d.views");
rpc::rpc.mark_safe("d.views.remove");
rpc::rpc.mark_safe("d.views.push_back_unique");
rpc::rpc.mark_safe("d.ratio");
rpc::rpc.mark_safe("f.multicall");
rpc::rpc.mark_safe("p.multicall");
rpc::rpc.mark_safe("p.call_target");
rpc::rpc.mark_safe("t.multicall");
} }
+39 -10
View File
@@ -444,15 +444,44 @@ 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 ("strings.choke_heuristics", std::bind(&torrent::option_list_strings, torrent::OPTION_CHOKE_HEURISTICS)); 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.upload", std::bind(&torrent::option_list_strings, torrent::OPTION_CHOKE_HEURISTICS_UPLOAD));
CMD2_ANY ("strings.choke_heuristics.download", std::bind(&torrent::option_list_strings, torrent::OPTION_CHOKE_HEURISTICS_DOWNLOAD)); CMD2_ANY ("strings.choke_heuristics", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS); });
CMD2_ANY ("strings.connection_type", std::bind(&torrent::option_list_strings, torrent::OPTION_CONNECTION_TYPE)); CMD2_ANY ("strings.choke_heuristics.upload", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS_UPLOAD); });
CMD2_ANY ("strings.encryption", std::bind(&torrent::option_list_strings, torrent::OPTION_ENCRYPTION)); CMD2_ANY ("strings.choke_heuristics.download", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CHOKE_HEURISTICS_DOWNLOAD); });
CMD2_ANY ("strings.ip_filter", std::bind(&torrent::option_list_strings, torrent::OPTION_IP_FILTER)); CMD2_ANY ("strings.connection_type", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_CONNECTION_TYPE); });
CMD2_ANY ("strings.ip_tos", std::bind(&torrent::option_list_strings, torrent::OPTION_IP_TOS)); CMD2_ANY ("strings.encryption", [](auto, auto) { return torrent::Object::create_list(); });
CMD2_ANY ("strings.log_group", std::bind(&torrent::option_list_strings, torrent::OPTION_LOG_GROUP)); CMD2_ANY ("strings.encryption.handshake", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_ENCRYPTION_HANDSHAKE); });
CMD2_ANY ("strings.tracker_event", std::bind(&torrent::option_list_strings, torrent::OPTION_TRACKER_EVENT)); CMD2_ANY ("strings.encryption.stream", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_ENCRYPTION_STREAM); });
CMD2_ANY ("strings.tracker_mode", std::bind(&torrent::option_list_strings, torrent::OPTION_TRACKER_MODE)); CMD2_ANY ("strings.ip_filter", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_IP_FILTER); });
CMD2_ANY ("strings.ip_tos", [](auto, auto) { return torrent::option_list_strings(torrent::OPTION_IP_TOS); });
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
rpc::rpc.mark_safe("system.listMethods");
#endif
rpc::rpc.mark_safe("method.use_deprecated");
rpc::rpc.mark_safe("method.const");
rpc::rpc.mark_safe("method.has_key");
rpc::rpc.mark_safe("method.list_keys");
rpc::rpc.mark_safe("method.get");
rpc::rpc.mark_safe("method.rlookup");
rpc::rpc.mark_safe("catch");
rpc::rpc.mark_safe("enum.log_group");
rpc::rpc.mark_safe("strings.choke_heuristics");
rpc::rpc.mark_safe("strings.choke_heuristics.upload");
rpc::rpc.mark_safe("strings.choke_heuristics.download");
rpc::rpc.mark_safe("strings.connection_type");
rpc::rpc.mark_safe("strings.encryption");
rpc::rpc.mark_safe("strings.ip_filter");
rpc::rpc.mark_safe("strings.ip_tos");
rpc::rpc.mark_safe("strings.log_group");
rpc::rpc.mark_safe("strings.tracker_event");
rpc::rpc.mark_safe("strings.tracker_mode");
} }
+83 -52
View File
@@ -2,14 +2,14 @@
#include <functional> #include <functional>
#include <cstdio> #include <cstdio>
#include <rak/error_number.h> #include <string>
#include <rak/file_stat.h> #include <vector>
#include <rak/path.h>
#include <rak/string_manip.h>
#include <torrent/rate.h> #include <torrent/rate.h>
#include <torrent/hash_string.h> #include <torrent/hash_string.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include <torrent/utils/directory_events.h> #include <torrent/utils/directory_events.h>
#include <torrent/utils/file_stat.h>
#include <torrent/utils/string_manip.h>
#include "globals.h" #include "globals.h"
#include "control.h" #include "control.h"
@@ -21,6 +21,7 @@
#include "rpc/command_scheduler.h" #include "rpc/command_scheduler.h"
#include "rpc/parse.h" #include "rpc/parse.h"
#include "rpc/parse_commands.h" #include "rpc/parse_commands.h"
#include "utils/watch_ready_queue.h"
torrent::Object torrent::Object
apply_on_ratio(const torrent::Object& rawArgs) { apply_on_ratio(const torrent::Object& rawArgs) {
@@ -67,10 +68,10 @@ apply_start_tied() {
if (rpc::call_command_value("d.state", rpc::make_target(download)) == 1) if (rpc::call_command_value("d.state", rpc::make_target(download)) == 1)
continue; continue;
rak::file_stat fs; torrent::utils::FileStat fs;
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download)); const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download));
if (!tied_to_file.empty() && fs.update(rak::path_expand(tied_to_file))) if (!tied_to_file.empty() && fs.update(expand_path(tied_to_file)))
rpc::parse_command_single(rpc::make_target(download), "d.try_start="); rpc::parse_command_single(rpc::make_target(download), "d.try_start=");
} }
@@ -83,10 +84,10 @@ apply_stop_untied() {
if (rpc::call_command_value("d.state", rpc::make_target(download)) == 0) if (rpc::call_command_value("d.state", rpc::make_target(download)) == 0)
continue; continue;
rak::file_stat fs; torrent::utils::FileStat fs;
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download)); const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download));
if (!tied_to_file.empty() && !fs.update(rak::path_expand(tied_to_file))) if (!tied_to_file.empty() && !fs.update(expand_path(tied_to_file)))
rpc::parse_command_single(rpc::make_target(download), "d.try_stop="); rpc::parse_command_single(rpc::make_target(download), "d.try_stop=");
} }
@@ -96,10 +97,10 @@ apply_stop_untied() {
torrent::Object torrent::Object
apply_close_untied() { apply_close_untied() {
for (const auto& download : *control->core()->download_list()) { for (const auto& download : *control->core()->download_list()) {
rak::file_stat fs; torrent::utils::FileStat fs;
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download)); const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(download));
if (rpc::call_command_value("d.ignore_commands", rpc::make_target(download)) == 0 && !tied_to_file.empty() && !fs.update(rak::path_expand(tied_to_file))) if (rpc::call_command_value("d.ignore_commands", rpc::make_target(download)) == 0 && !tied_to_file.empty() && !fs.update(expand_path(tied_to_file)))
rpc::parse_command_single(rpc::make_target(download), "d.try_close="); rpc::parse_command_single(rpc::make_target(download), "d.try_close=");
} }
@@ -109,10 +110,10 @@ apply_close_untied() {
torrent::Object torrent::Object
apply_remove_untied() { apply_remove_untied() {
for (auto itr = control->core()->download_list()->begin(); itr != control->core()->download_list()->end(); ) { for (auto itr = control->core()->download_list()->begin(); itr != control->core()->download_list()->end(); ) {
rak::file_stat fs; torrent::utils::FileStat fs;
const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(*itr)); const std::string& tied_to_file = rpc::call_command_string("d.tied_to_file", rpc::make_target(*itr));
if (!tied_to_file.empty() && !fs.update(rak::path_expand(tied_to_file))) { if (!tied_to_file.empty() && !fs.update(expand_path(tied_to_file))) {
// Need to clear tied_to_file so it doesn't try to delete it. // Need to clear tied_to_file so it doesn't try to delete it.
rpc::call_command("d.tied_to_file.set", std::string(), rpc::make_target(*itr)); rpc::call_command("d.tied_to_file.set", std::string(), rpc::make_target(*itr));
@@ -167,12 +168,14 @@ torrent::Object
apply_close_low_diskspace(int64_t arg, uint32_t skip_priority) { apply_close_low_diskspace(int64_t arg, uint32_t skip_priority) {
bool closed = false; bool closed = false;
torrent::FileList::cache_list cache;
for (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)
continue; continue;
if (download->file_list()->free_diskspace() >= (uint64_t)arg) if (download->file_list()->free_diskspace(cache) >= (uint64_t)arg)
continue; continue;
control->core()->download_list()->close(download); control->core()->download_list()->close(download);
@@ -210,7 +213,7 @@ apply_download_list(const torrent::Object::list_type& args) {
for (core::View::const_iterator itr = (*view_itr)->begin_visible(), last = (*view_itr)->end_visible(); itr != last; itr++) { for (core::View::const_iterator itr = (*view_itr)->begin_visible(), last = (*view_itr)->end_visible(); itr != last; itr++) {
const torrent::HashString* hashString = &(*itr)->info()->hash(); const torrent::HashString* hashString = &(*itr)->info()->hash();
resultList.push_back(rak::transform_hex(hashString->begin(), hashString->end())); resultList.push_back(torrent::utils::transform_to_hex_str(*hashString));
} }
return result; return result;
@@ -255,11 +258,12 @@ torrent::Object
d_multicall_filtered(const torrent::Object::list_type& args) { d_multicall_filtered(const torrent::Object::list_type& args) {
if (args.size() < 2) if (args.size() < 2)
throw torrent::input_error("d.multicall.filtered requires at least 2 arguments."); throw torrent::input_error("d.multicall.filtered requires at least 2 arguments.");
torrent::Object::list_const_iterator arg = args.begin();
auto arg = args.begin();
// Find the given view // Find the given view
core::ViewManager* viewManager = control->view_manager(); auto* viewManager = control->view_manager();
core::ViewManager::iterator view_itr = viewManager->find(arg->as_string().empty() ? "default" : arg->as_string()); auto view_itr = viewManager->find(arg->as_string().empty() ? "default" : arg->as_string());
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() + "'.");
@@ -269,8 +273,9 @@ d_multicall_filtered(const torrent::Object::list_type& args) {
(*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
torrent::Object resultRaw = torrent::Object::create_list(); auto resultRaw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list(); auto& result = resultRaw.as_list();
++arg; // skip to first command ++arg; // skip to first command
for (const auto& item : dlist) { for (const auto& item : dlist) {
@@ -293,57 +298,83 @@ call_watch_command(const std::string& command, const std::string& path) {
} }
torrent::Object torrent::Object
directory_watch_added(const torrent::Object::list_type& args) { directory_watch(const torrent::Object::list_type& args, int flags) {
if (args.size() != 2) if (args.size() != 2)
throw torrent::input_error("Too few arguments."); throw torrent::input_error("Too few arguments.");
auto& path = args.front().as_string(); auto& path = args.front().as_string();
auto& command = args.back().as_string(); auto& command = args.back().as_string();
std::string expanded_path = expand_path(path);
if (!control->directory_events()->open()) if (!control->directory_events()->open())
throw torrent::input_error("Could not open inotify:" + std::string(rak::error_number::current().c_str())); throw torrent::input_error("Could not open inotify:" + std::string(std::strerror(errno)));
control->directory_events()->notify_on(path.c_str(), torrent::watch_descriptor::slot_string slot =
torrent::directory_events::flag_on_added | torrent::directory_events::flag_on_updated, flags == torrent::directory_events::flag_on_ready ?
std::bind(&call_watch_command, command, std::placeholders::_1)); torrent::watch_descriptor::slot_string([command](const auto& arg) { control->watch_ready_queue()->push(command, arg); }) :
torrent::watch_descriptor::slot_string(std::bind(&call_watch_command, command, std::placeholders::_1));
control->directory_events()->notify_on(expanded_path.c_str(), flags, slot);
return torrent::Object(); return torrent::Object();
} }
torrent::Object
directory_watch_added(const torrent::Object::list_type& args) {
return directory_watch(args,
torrent::directory_events::flag_on_added |
torrent::directory_events::flag_on_updated);
}
torrent::Object
directory_watch_ready(const torrent::Object::list_type& args) {
return directory_watch(args,
torrent::directory_events::flag_on_ready);
}
void void
initialize_command_events() { initialize_command_events() {
CMD2_ANY_STRING ("on_ratio", std::bind(&apply_on_ratio, std::placeholders::_2)); CMD2_ANY_STRING ("on_ratio", [](auto, auto& args) { return apply_on_ratio(args); });
CMD2_ANY ("start_tied", std::bind(&apply_start_tied)); CMD2_ANY ("start_tied", [](auto, auto) { return apply_start_tied(); });
CMD2_ANY ("stop_untied", std::bind(&apply_stop_untied)); CMD2_ANY ("stop_untied", [](auto, auto) { return apply_stop_untied(); });
CMD2_ANY ("close_untied", std::bind(&apply_close_untied)); CMD2_ANY ("close_untied", [](auto, auto) { return apply_close_untied(); });
CMD2_ANY ("remove_untied", std::bind(&apply_remove_untied)); CMD2_ANY ("remove_untied", [](auto, auto) { return apply_remove_untied(); });
// TODO: Deprecate schedule2 in the future. CMD2_ANY_LIST ("schedule", [](auto, auto& args) { return apply_schedule(args); });
CMD2_ANY_LIST ("schedule", std::bind(&apply_schedule, std::placeholders::_2)); CMD2_ANY_STRING_V("schedule.remove", [](auto, auto& str) { return control->command_scheduler()->erase_str(str); });
CMD2_ANY_LIST ("schedule2", std::bind(&apply_schedule, std::placeholders::_2));
CMD2_ANY_STRING_V("schedule.remove", std::bind(&rpc::CommandScheduler::erase_str, control->command_scheduler(), std::placeholders::_2));
CMD2_ANY_STRING_V("schedule_remove2", std::bind(&rpc::CommandScheduler::erase_str, control->command_scheduler(), std::placeholders::_2));
CMD2_ANY_STRING_V("import", std::bind(&apply_import, std::placeholders::_2)); CMD2_ANY_STRING_V("import", [](auto, auto& str) { return apply_import(str); });
CMD2_ANY_STRING_V("try_import", std::bind(&apply_try_import, std::placeholders::_2)); CMD2_ANY_STRING_V("try_import", [](auto, auto& str) { return apply_try_import(str); });
CMD2_ANY_LIST ("load.normal", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_quiet | core::Manager::create_tied)); CMD2_ANY_LIST ("load.normal", [](auto, auto& args) { return apply_load(args, core::Manager::create_quiet | core::Manager::create_tied); });
CMD2_ANY_LIST ("load.verbose", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_tied)); CMD2_ANY_LIST ("load.verbose", [](auto, auto& args) { return apply_load(args, core::Manager::create_tied); });
CMD2_ANY_LIST ("load.start", std::bind(&apply_load, std::placeholders::_2, CMD2_ANY_LIST ("load.start", [](auto, auto& args) { return apply_load(args, core::Manager::create_quiet | core::Manager::create_tied | core::Manager::create_start); });
core::Manager::create_quiet | core::Manager::create_tied | core::Manager::create_start)); CMD2_ANY_LIST ("load.start_verbose", [](auto, auto& args) { return apply_load(args, core::Manager::create_tied | core::Manager::create_start); });
CMD2_ANY_LIST ("load.start_verbose", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_tied | core::Manager::create_start)); CMD2_ANY_LIST ("load.raw", [](auto, auto& args) { return apply_load(args, core::Manager::create_quiet | core::Manager::create_raw_data); });
CMD2_ANY_LIST ("load.raw", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_quiet | core::Manager::create_raw_data)); CMD2_ANY_LIST ("load.raw_verbose", [](auto, auto& args) { return apply_load(args, core::Manager::create_raw_data); });
CMD2_ANY_LIST ("load.raw_verbose", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_raw_data)); CMD2_ANY_LIST ("load.raw_start", [](auto, auto& args) { return apply_load(args, core::Manager::create_quiet | core::Manager::create_start | core::Manager::create_raw_data); });
CMD2_ANY_LIST ("load.raw_start", std::bind(&apply_load, std::placeholders::_2, CMD2_ANY_LIST ("load.raw_start_verbose", [](auto, auto& args) { return apply_load(args, core::Manager::create_start | core::Manager::create_raw_data); });
core::Manager::create_quiet | core::Manager::create_start | core::Manager::create_raw_data));
CMD2_ANY_LIST ("load.raw_start_verbose", std::bind(&apply_load, std::placeholders::_2, core::Manager::create_start | core::Manager::create_raw_data));
CMD2_ANY_VALUE ("close_low_diskspace", std::bind(&apply_close_low_diskspace, std::placeholders::_2, 99)); CMD2_ANY_VALUE ("close_low_diskspace", [](auto, auto& arg) { return apply_close_low_diskspace(arg, 99); });
CMD2_ANY_VALUE ("close_low_diskspace.normal", std::bind(&apply_close_low_diskspace, std::placeholders::_2, 3)); CMD2_ANY_VALUE ("close_low_diskspace.normal", [](auto, auto& arg) { return apply_close_low_diskspace(arg, 3); });
CMD2_ANY_LIST ("download_list", std::bind(&apply_download_list, std::placeholders::_2)); CMD2_ANY_LIST ("download_list", [](auto, auto& args) { return apply_download_list(args); });
CMD2_ANY_LIST ("d.multicall2", std::bind(&d_multicall, std::placeholders::_2));
CMD2_ANY_LIST ("d.multicall.filtered", std::bind(&d_multicall_filtered, std::placeholders::_2));
CMD2_ANY_LIST ("directory.watch.added", std::bind(&directory_watch_added, std::placeholders::_2)); // TODO: Deprecate d.multicall2. (6/2026)
CMD2_ANY_LIST ("d.multicall", [](auto, auto& args) { return d_multicall(args); });
CMD2_ANY_LIST ("d.multicall.filtered", [](auto, auto& args) { return d_multicall_filtered(args); });
CMD2_ANY_LIST ("directory.watch.added", [](auto, auto& args) { return directory_watch_added(args); });
CMD2_ANY_LIST ("directory.watch.ready", [](auto, auto& args) { return directory_watch_ready(args); });
rpc::rpc.mark_safe("start_tied");
rpc::rpc.mark_safe("stop_untied");
rpc::rpc.mark_safe("close_untied");
rpc::rpc.mark_safe("remove_untied");
rpc::rpc.mark_safe("close_low_diskspace");
rpc::rpc.mark_safe("close_low_diskspace.normal");
rpc::rpc.mark_safe("download_list");
rpc::rpc.mark_safe("d.multicall");
rpc::rpc.mark_safe("d.multicall.filtered");
} }
+82 -59
View File
@@ -1,41 +1,5 @@
// rTorrent - BitTorrent client
// Copyright (C) 2005-2011, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#include "config.h" #include "config.h"
#include <rak/error_number.h>
#include <rak/path.h>
#include <torrent/data/file.h> #include <torrent/data/file.h>
#include <torrent/data/file_list.h> #include <torrent/data/file_list.h>
#include <torrent/data/file_list_iterator.h> #include <torrent/data/file_list_iterator.h>
@@ -60,24 +24,34 @@ apply_f_path(torrent::File* file) {
if (file->path()->empty()) if (file->path()->empty())
return std::string(); return std::string();
torrent::Object resultRaw(*file->path()->begin()); torrent::Object result_raw(file->path()->begin()->str());
torrent::Object::string_type& result = resultRaw.as_string(); auto& result = result_raw.as_string();
for (torrent::Path::const_iterator itr = ++file->path()->begin(), last = file->path()->end(); itr != last; itr++) for (torrent::Path::const_iterator itr = ++file->path()->begin(), last = file->path()->end(); itr != last; itr++)
result += '/' + *itr; result += '/' + itr->str();
return resultRaw; return result_raw;
} }
torrent::Object torrent::Object
apply_f_path_components(torrent::File* file) { apply_f_path_components(torrent::File* file, int output_type) {
torrent::Object resultRaw = torrent::Object::create_list(); auto result_raw = torrent::Object::create_list();
torrent::Object::list_type& result = resultRaw.as_list(); auto& result = result_raw.as_list();
for (const auto& itr : *file->path()) for (const auto& itr : *file->path()) {
result.push_back(itr); switch (output_type) {
case 0: result.push_back(itr.str()); break;
case 1: result.push_back(itr.object_hex()); break;
case 2: result.push_back(itr.object_base64()); break;
case 3: result.push_back(itr.object_base64_as_binary()); break;
case 4: result.push_back(itr.object_as_binary()); break;
case 5: result.push_back(itr.object_utf8_or_base64()); break;
case 6: result.push_back(itr.object_utf8_or_as_binary()); break;
default: throw torrent::input_error("apply_f_path_components(): invalid output type");
};
}
return resultRaw; return result_raw;
} }
torrent::Object torrent::Object
@@ -93,7 +67,7 @@ apply_fi_filename_last(torrent::FileListIterator* itr) {
if (itr->depth() >= itr->file()->path()->size()) if (itr->depth() >= itr->file()->path()->size())
return "ERROR"; return "ERROR";
return itr->file()->path()->at(itr->depth()); return itr->file()->path()->at(itr->depth()).str();
} }
void void
@@ -116,21 +90,34 @@ initialize_command_file() {
CMD2_FILE_V("f.prioritize_last.enable", std::bind(&torrent::File::set_flags, std::placeholders::_1, torrent::File::flag_prioritize_last)); CMD2_FILE_V("f.prioritize_last.enable", std::bind(&torrent::File::set_flags, std::placeholders::_1, torrent::File::flag_prioritize_last));
CMD2_FILE_V("f.prioritize_last.disable", std::bind(&torrent::File::unset_flags, std::placeholders::_1, torrent::File::flag_prioritize_last)); CMD2_FILE_V("f.prioritize_last.disable", std::bind(&torrent::File::unset_flags, std::placeholders::_1, torrent::File::flag_prioritize_last));
CMD2_FILE("f.size_bytes", std::bind(&torrent::File::size_bytes, std::placeholders::_1)); CMD2_FILE("f.size_bytes", [](auto* file, auto) { return file->size_bytes(); });
CMD2_FILE("f.size_chunks", std::bind(&torrent::File::size_chunks, std::placeholders::_1)); CMD2_FILE("f.size_chunks", [](auto* file, auto) { return file->size_chunks(); });
CMD2_FILE("f.completed_chunks", std::bind(&torrent::File::completed_chunks, std::placeholders::_1)); CMD2_FILE("f.completed_chunks", [](auto* file, auto) { return file->completed_chunks(); });
CMD2_FILE("f.offset", std::bind(&torrent::File::offset, std::placeholders::_1)); CMD2_FILE("f.offset", [](auto* file, auto) { return file->offset(); });
CMD2_FILE("f.range_first", std::bind(&torrent::File::range_first, std::placeholders::_1)); CMD2_FILE("f.range_first", [](auto* file, auto) { return file->range_first(); });
CMD2_FILE("f.range_second", std::bind(&torrent::File::range_second, std::placeholders::_1)); CMD2_FILE("f.range_second", [](auto* file, auto) { return file->range_second(); });
CMD2_FILE("f.priority", std::bind(&torrent::File::priority, std::placeholders::_1)); CMD2_FILE("f.priority", [](auto* file, auto) { return file->priority(); });
CMD2_FILE_VALUE_V("f.priority.set", std::bind(&apply_f_set_priority, std::placeholders::_1, std::placeholders::_2)); CMD2_FILE_VALUE_V("f.priority.set", [](auto* file, auto v) { apply_f_set_priority(file, v); });
CMD2_FILE("f.path", std::bind(&apply_f_path, std::placeholders::_1)); CMD2_FILE("f.path", [](auto* file, auto) { return apply_f_path(file); });
CMD2_FILE("f.path_components", std::bind(&apply_f_path_components, std::placeholders::_1)); CMD2_FILE("f.path_components", [](auto* file, auto) { return apply_f_path_components(file, 0); });
CMD2_FILE("f.path_depth", std::bind(&apply_f_path_depth, std::placeholders::_1)); CMD2_FILE("f.path_components.hex", [](auto* file, auto) { return apply_f_path_components(file, 1); });
CMD2_FILE("f.frozen_path", std::bind(&torrent::File::frozen_path, std::placeholders::_1)); CMD2_FILE("f.path_components.base64", [](auto* file, auto) { return apply_f_path_components(file, 2); });
CMD2_FILE("f.path_components.base64_as_binary", [](auto* file, auto) { return apply_f_path_components(file, 3); });
CMD2_FILE("f.path_components.as_binary", [](auto* file, auto) { return apply_f_path_components(file, 4); });
CMD2_FILE("f.path_components.or_base64", [](auto* file, auto) { return apply_f_path_components(file, 5); });
CMD2_FILE("f.path_components.or_as_binary", [](auto* file, auto) { return apply_f_path_components(file, 6); });
CMD2_FILE("f.path_depth", [](auto* file, auto) { return apply_f_path_depth(file); });
CMD2_FILE("f.frozen_path", [](auto* file, auto) { return file->frozen_path().str(); });
CMD2_FILE("f.frozen_path.hex", [](auto* file, auto) { return file->frozen_path().object_hex(); });
CMD2_FILE("f.frozen_path.base64", [](auto* file, auto) { return file->frozen_path().object_base64(); });
CMD2_FILE("f.frozen_path.as_binary", [](auto* file, auto) { return file->frozen_path().object_as_binary(); });
CMD2_FILE("f.frozen_path.or_base64", [](auto* file, auto) { return file->frozen_path().object_utf8_or_base64(); });
CMD2_FILE("f.frozen_path.or_as_binary", [](auto* file, auto) { return file->frozen_path().object_utf8_or_as_binary(); });
CMD2_FILE("f.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));
@@ -139,4 +126,40 @@ initialize_command_file() {
CMD2_FILEITR("fi.filename_last", std::bind(&apply_fi_filename_last, std::placeholders::_1)); CMD2_FILEITR("fi.filename_last", std::bind(&apply_fi_filename_last, std::placeholders::_1));
CMD2_FILEITR("fi.is_file", std::bind(&torrent::FileListIterator::is_file, std::placeholders::_1)); CMD2_FILEITR("fi.is_file", std::bind(&torrent::FileListIterator::is_file, std::placeholders::_1));
rpc::rpc.mark_safe("f.path");
rpc::rpc.mark_safe("f.path_components");
rpc::rpc.mark_safe("f.path_depth");
rpc::rpc.mark_safe("f.frozen_path");
rpc::rpc.mark_safe("f.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.base64");
rpc::rpc.mark_safe("f.frozen_path.base64_as_binary");
rpc::rpc.mark_safe("f.frozen_path.as_binary");
rpc::rpc.mark_safe("f.frozen_path.or_base64");
rpc::rpc.mark_safe("f.frozen_path.or_as_binary");
rpc::rpc.mark_safe("f.offset");
rpc::rpc.mark_safe("f.size_bytes");
rpc::rpc.mark_safe("f.size_chunks");
rpc::rpc.mark_safe("f.completed_chunks");
rpc::rpc.mark_safe("f.range_first");
rpc::rpc.mark_safe("f.range_second");
rpc::rpc.mark_safe("f.priority");
rpc::rpc.mark_safe("f.priority.set");
rpc::rpc.mark_safe("f.is_created");
rpc::rpc.mark_safe("f.is_open");
rpc::rpc.mark_safe("f.is_create_queued");
rpc::rpc.mark_safe("f.is_resize_queued");
rpc::rpc.mark_safe("f.prioritize_first");
rpc::rpc.mark_safe("f.prioritize_first.enable");
rpc::rpc.mark_safe("f.prioritize_first.disable");
rpc::rpc.mark_safe("f.prioritize_last");
rpc::rpc.mark_safe("f.prioritize_last.enable");
rpc::rpc.mark_safe("f.prioritize_last.disable");
rpc::rpc.mark_safe("f.last_touched");
rpc::rpc.mark_safe("f.match_depth_prev");
rpc::rpc.mark_safe("f.match_depth_next");
rpc::rpc.mark_safe("fi.filename_last");
rpc::rpc.mark_safe("fi.is_file");
} }
+51 -34
View File
@@ -186,8 +186,8 @@ apply_cg_insert(const std::string& arg) {
cg_list_hack.push_back(new 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::choke_queue::HEURISTICS_UPLOAD_LEECH); cg_list_hack.back()->up_queue()->set_heuristics(torrent::HEURISTICS_UPLOAD_LEECH);
cg_list_hack.back()->down_queue()->set_heuristics(torrent::choke_queue::HEURISTICS_DOWNLOAD_LEECH); cg_list_hack.back()->down_queue()->set_heuristics(torrent::HEURISTICS_DOWNLOAD_LEECH);
return torrent::Object(); return torrent::Object();
} }
@@ -337,52 +337,69 @@ options.
void void
initialize_command_groups() { initialize_command_groups() {
CMD2_ANY ("choke_group.list", std::bind(&apply_cg_list)); CMD_ANY ("choke_group.list", std::bind(&apply_cg_list));
CMD2_ANY_STRING ("choke_group.insert", std::bind(&apply_cg_insert, std::placeholders::_2)); CMD_ANY_STRING ("choke_group.insert", std::bind(&apply_cg_insert, std::placeholders::_2));
#if USE_CHOKE_GROUP #if USE_CHOKE_GROUP
CMD2_ANY ("choke_group.size", std::bind(&torrent::ResourceManager::group_size, torrent::resource_manager())); CMD_ANY ("choke_group.size", std::bind(&torrent::ResourceManager::group_size, torrent::resource_manager()));
CMD2_ANY_STRING ("choke_group.index_of", std::bind(&torrent::ResourceManager::group_index_of, torrent::resource_manager(), std::placeholders::_2)); CMD_ANY_STRING ("choke_group.index_of", std::bind(&torrent::ResourceManager::group_index_of, torrent::resource_manager(), std::placeholders::_2));
#else #else
apply_cg_insert("default"); apply_cg_insert("default");
CMD2_ANY ("choke_group.size", std::bind(&std::vector<torrent::choke_group*>::size, cg_list_hack)); CMD_ANY ("choke_group.size", std::bind(&std::vector<torrent::choke_group*>::size, cg_list_hack));
CMD2_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
// Commands specific for a group. Supports as the first argument the // Commands specific for a group. Supports as the first argument the
// name, the index or a negative index. // name, the index or a negative index.
CMD2_ANY ("choke_group.general.size", std::bind(&torrent::choke_group::size, CG_GROUP_AT())); CMD_ANY ("choke_group.general.size", std::bind(&torrent::choke_group::size, CG_GROUP_AT()));
CMD2_ANY ("choke_group.tracker.mode", std::bind(&torrent::option_as_string, torrent::OPTION_TRACKER_MODE, CMD_ANY ("choke_group.tracker.mode", [](auto, auto arg) { return torrent::option_to_str_or_throw(torrent::OPTION_TRACKER_MODE, cg_get_group(arg)->tracker_mode()); });
std::bind(&torrent::choke_group::tracker_mode, CG_GROUP_AT()))); CMD_ANY_LIST ("choke_group.tracker.mode.set", [](auto, auto arg) { return apply_cg_tracker_mode_set(arg); });
CMD2_ANY_LIST ("choke_group.tracker.mode.set", std::bind(&apply_cg_tracker_mode_set, std::placeholders::_2));
CMD2_ANY ("choke_group.all.up.update_balance", std::bind(&apply_cg_all_update_balance, true)); CMD_ANY ("choke_group.all.up.update_balance", std::bind(&apply_cg_all_update_balance, true));
CMD2_ANY ("choke_group.all.down.update_balance", std::bind(&apply_cg_all_update_balance, false)); CMD_ANY ("choke_group.all.down.update_balance", std::bind(&apply_cg_all_update_balance, false));
CMD2_ANY ("choke_group.up.rate", std::bind(&torrent::choke_group::up_rate, CG_GROUP_AT())); CMD_ANY ("choke_group.up.rate", std::bind(&torrent::choke_group::up_rate, CG_GROUP_AT()));
CMD2_ANY ("choke_group.down.rate", std::bind(&torrent::choke_group::down_rate, CG_GROUP_AT())); CMD_ANY ("choke_group.down.rate", std::bind(&torrent::choke_group::down_rate, CG_GROUP_AT()));
CMD2_ANY ("choke_group.up.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::up_queue))); CMD_ANY ("choke_group.up.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::up_queue)));
CMD2_ANY ("choke_group.up.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::up_queue))); CMD_ANY ("choke_group.up.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::up_queue)));
CMD2_ANY_LIST ("choke_group.up.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, true)); CMD_ANY_LIST ("choke_group.up.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, true));
CMD2_ANY ("choke_group.up.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::up_queue))); CMD_ANY ("choke_group.up.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::up_queue)));
CMD2_ANY ("choke_group.up.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::up_queue))); CMD_ANY ("choke_group.up.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::up_queue)));
CMD2_ANY ("choke_group.up.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::up_queue))); CMD_ANY ("choke_group.up.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::up_queue)));
CMD2_ANY ("choke_group.up.heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS, CMD_ANY ("choke_group.up.heuristics", [](auto, auto arg) { return torrent::option_to_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, cg_get_group(arg)->up_queue()->heuristics()); });
std::bind(&torrent::choke_queue::heuristics, CHOKE_GROUP(&torrent::choke_group::up_queue)))); CMD_ANY_LIST ("choke_group.up.heuristics.set", [](auto, auto arg) { return apply_cg_heuristics_set(arg, true); });
CMD2_ANY_LIST ("choke_group.up.heuristics.set", std::bind(&apply_cg_heuristics_set, std::placeholders::_2, true));
CMD2_ANY ("choke_group.down.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::down_queue))); CMD_ANY ("choke_group.down.max.unlimited", std::bind(&torrent::choke_queue::is_unlimited, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD2_ANY ("choke_group.down.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::down_queue))); CMD_ANY ("choke_group.down.max", std::bind(&torrent::choke_queue::max_unchoked_signed, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD2_ANY_LIST ("choke_group.down.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, false)); CMD_ANY_LIST ("choke_group.down.max.set", std::bind(&apply_cg_max_set, std::placeholders::_2, false));
CMD2_ANY ("choke_group.down.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::down_queue))); CMD_ANY ("choke_group.down.total", std::bind(&torrent::choke_queue::size_total, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD2_ANY ("choke_group.down.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::down_queue))); CMD_ANY ("choke_group.down.queued", std::bind(&torrent::choke_queue::size_queued, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD2_ANY ("choke_group.down.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::down_queue))); CMD_ANY ("choke_group.down.unchoked", std::bind(&torrent::choke_queue::size_unchoked, CHOKE_GROUP(&torrent::choke_group::down_queue)));
CMD2_ANY ("choke_group.down.heuristics", std::bind(&torrent::option_as_string, torrent::OPTION_CHOKE_HEURISTICS, CMD_ANY ("choke_group.down.heuristics", [](auto, auto arg) { return torrent::option_to_str_or_throw(torrent::OPTION_CHOKE_HEURISTICS, cg_get_group(arg)->down_queue()->heuristics()); });
std::bind(&torrent::choke_queue::heuristics, CHOKE_GROUP(&torrent::choke_group::down_queue)))); CMD_ANY_LIST ("choke_group.down.heuristics.set", [](auto, auto arg) { return apply_cg_heuristics_set(arg, false); });
CMD2_ANY_LIST ("choke_group.down.heuristics.set", std::bind(&apply_cg_heuristics_set, std::placeholders::_2, false));
rpc::rpc.mark_safe("choke_group.list");
rpc::rpc.mark_safe("choke_group.size");
rpc::rpc.mark_safe("choke_group.index_of");
rpc::rpc.mark_safe("choke_group.general.size");
rpc::rpc.mark_safe("choke_group.tracker.mode");
rpc::rpc.mark_safe("choke_group.up.rate");
rpc::rpc.mark_safe("choke_group.down.rate");
rpc::rpc.mark_safe("choke_group.up.max");
rpc::rpc.mark_safe("choke_group.up.max.unlimited");
rpc::rpc.mark_safe("choke_group.up.total");
rpc::rpc.mark_safe("choke_group.up.queued");
rpc::rpc.mark_safe("choke_group.up.unchoked");
rpc::rpc.mark_safe("choke_group.up.heuristics");
rpc::rpc.mark_safe("choke_group.down.max");
rpc::rpc.mark_safe("choke_group.down.max.unlimited");
rpc::rpc.mark_safe("choke_group.down.total");
rpc::rpc.mark_safe("choke_group.down.queued");
rpc::rpc.mark_safe("choke_group.down.unchoked");
rpc::rpc.mark_safe("choke_group.down.heuristics");
} }
+2
View File
@@ -19,6 +19,7 @@ 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_ui(); void initialize_command_ui();
void void
@@ -37,4 +38,5 @@ initialize_commands() {
initialize_command_throttle(); initialize_command_throttle();
initialize_command_tracker(); initialize_command_tracker();
initialize_command_scheduler(); initialize_command_scheduler();
initialize_command_string();
} }
+19 -1
View File
@@ -7,6 +7,25 @@
void initialize_commands(); void initialize_commands();
//
// Aliases with CMD_* for the below
//
#define CMD_ANY(key, slot) CMD2_ANY(key, slot)
#define CMD_ANY_P(key, slot) CMD2_ANY_P(key, slot)
#define CMD_REDIRECT(key, slot) CMD2_REDIRECT(key, slot)
#define CMD_REDIRECT_NO_EXPORT(key, slot) CMD2_REDIRECT_NO_EXPORT(key, slot)
#define CMD_ANY_STRING(key, slot) CMD2_ANY_STRING(key, slot)
#define CMD_ANY_STRING_V(key, slot) CMD2_ANY_STRING_V(key, slot)
#define CMD_ANY_LIST(key, slot) CMD2_ANY_LIST(key, slot)
#define CMD_VAR_VALUE(key, value) CMD2_VAR_VALUE(key, value)
#define CMD_VAR_BOOL(key, value) CMD2_VAR_BOOL(key, value)
#define CMD_VAR_STRING(key, value) CMD2_VAR_STRING(key, value)
#define CMD_VAR_C_STRING(key, value) CMD2_VAR_C_STRING(key, value)
#define CMD_VAR_LIST(key) CMD2_VAR_LIST(key)
#define CMD_ANY_V(key, slot) CMD2_ANY_V(key, slot)
#define CMD_ANY_VALUE_V(key, slot) CMD2_ANY_VALUE_V(key, slot)
// //
// New std::function based command_base helper functions: // New std::function based command_base helper functions:
// //
@@ -115,7 +134,6 @@ void initialize_commands();
#define CMD2_REDIRECT_TRACKER(from_key, to_key) \ #define CMD2_REDIRECT_TRACKER(from_key, to_key) \
rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_tracker_target | rpc::CommandMap::flag_dont_delete); rpc::commands.create_redirect(from_key, to_key, rpc::CommandMap::flag_public_rpc | rpc::CommandMap::flag_tracker_target | rpc::CommandMap::flag_dont_delete);
// //
// Conversion of return types: // Conversion of return types:
// //
+16 -47
View File
@@ -1,54 +1,18 @@
// rTorrent - BitTorrent client
// Copyright (C) 2005-2011, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#include "config.h" #include "config.h"
#include <fstream> #include <fstream>
#include <rak/path.h>
#include <torrent/peer/peer_list.h>
#include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
#include "globals.h"
#include "command_helpers.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <torrent/peer/peer_list.h>
#include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h>
#include "globals.h"
#include "command_helpers.h"
bool ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address_end); bool ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address_end);
@@ -65,7 +29,7 @@ torrent::Object
apply_ip_tables_size_data(const std::string& args) { apply_ip_tables_size_data(const std::string& args) {
rpc::ip_table_list::const_iterator itr = ip_tables.find(args); rpc::ip_table_list::const_iterator itr = ip_tables.find(args);
if (itr != ip_tables.end()) if (itr == ip_tables.end())
throw torrent::input_error("IP table does not exist."); throw torrent::input_error("IP table does not exist.");
uint32_t size = itr->table.sizeof_data(); uint32_t size = itr->table.sizeof_data();
@@ -239,9 +203,14 @@ ipv4_range_parse(const char* address, uint32_t* address_start, uint32_t* address
uint32_t mask=0; uint32_t mask=0;
uint32_t end_mask=0; uint32_t end_mask=0;
mask = (~mask) << (32-mask_bits); if (mask_bits == 0) {
mask = 0;
end_mask = ~(uint32_t)0;
} else {
mask = (~mask) << (32-mask_bits);
end_mask = (~end_mask) >> mask_bits;
}
*address_start = ip & mask; *address_start = ip & mask;
end_mask = (~end_mask) >> mask_bits;
*address_end = (ip & mask) | end_mask; *address_end = (ip & mask) | end_mask;
valid=true; valid=true;
@@ -326,7 +295,7 @@ apply_ipv4_filter_load(const torrent::Object::list_type& args) {
std::string value_name = args.back().as_string(); std::string value_name = args.back().as_string();
int value = torrent::option_find_string(torrent::OPTION_IP_FILTER, value_name.c_str()); int value = torrent::option_find_string(torrent::OPTION_IP_FILTER, value_name.c_str());
std::fstream file(rak::path_expand(filename).c_str(), std::ios::in); std::fstream file(expand_path(filename).c_str(), std::ios::in);
if (!file.is_open()) if (!file.is_open())
throw torrent::input_error("Could not open ip filter file: " + filename); throw torrent::input_error("Could not open ip filter file: " + filename);
@@ -384,7 +353,7 @@ apply_ipv4_filter_dump() {
inet_ntop(AF_INET, &net_start, start_str, INET_ADDRSTRLEN); inet_ntop(AF_INET, &net_start, start_str, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &net_end, end_str, INET_ADDRSTRLEN); inet_ntop(AF_INET, &net_end, end_str, INET_ADDRSTRLEN);
snprintf(buffer, 64, "%s-%s %s", start_str, end_str, torrent::option_as_string(torrent::OPTION_IP_FILTER, value)); snprintf(buffer, 64, "%s-%s %s", start_str, end_str, torrent::option_to_c_str_or_throw(torrent::OPTION_IP_FILTER, value));
result.push_back((std::string)buffer); result.push_back((std::string)buffer);
+199 -121
View File
@@ -1,27 +1,27 @@
#include "config.h" #include "config.h"
#include <cerrno>
#include <fcntl.h> #include <fcntl.h>
#include <functional> #include <functional>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <rak/path.h>
#include <rak/error_number.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <torrent/torrent.h> #include <torrent/torrent.h>
#include <torrent/chunk_manager.h>
#include <torrent/data/file_manager.h> #include <torrent/data/file_manager.h>
#include <torrent/data/chunk_utils.h> #include <torrent/data/chunk_utils.h>
#include <torrent/runtime/runtime.h>
#include <torrent/runtime/memory_manager.h>
#include <torrent/runtime/socket_manager.h>
#include <torrent/utils/chrono.h> #include <torrent/utils/chrono.h>
#include <torrent/utils/option_strings.h> #include <torrent/utils/option_strings.h>
#include "core/download.h" #include "core/download.h"
#include "core/download_list.h" #include "core/download_list.h"
#include "core/download_store.h"
#include "core/manager.h" #include "core/manager.h"
#include "rak/string_manip.h"
#include "rpc/parse_commands.h" #include "rpc/parse_commands.h"
#include "rpc/scgi.h" #include "rpc/scgi.h"
#include "session/session_manager.h"
#include "utils/file_status_cache.h" #include "utils/file_status_cache.h"
#include "globals.h" #include "globals.h"
@@ -96,8 +96,12 @@ post_increment(torrent::Object::list_const_iterator& itr, const torrent::Object:
inline const std::string& inline const std::string&
check_name(const std::string& str) { check_name(const std::string& str) {
if (!rak::is_all_name(str)) auto itr = std::find_if(str.begin(), str.end(), [](char c) {
throw torrent::input_error("Non-alphanumeric characters found."); return !std::isalnum(c, std::locale::classic()) && c != '_';
});
if (itr != str.end())
throw torrent::input_error("Invalid characters found in name.");
return str; return str;
} }
@@ -111,9 +115,9 @@ group_insert(const torrent::Object::list_type& args) {
const std::string& view = check_name(post_increment(itr, last)->as_string()); const std::string& view = check_name(post_increment(itr, last)->as_string());
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",
"schedule2=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_remove2=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));
@@ -121,18 +125,14 @@ group_insert(const torrent::Object::list_type& args) {
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.max", "value", (int64_t)300)); rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.max", "value", (int64_t)300));
rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.upload", "value", (int64_t)20 << 20)); rpc::commands.call("method.insert", rpc::create_object_list("group." + name + ".ratio.upload", "value", (int64_t)20 << 20));
if (rpc::call_command_value("method.use_intermediate") == 3) { rpc::rpc.mark_safe("group." + name + ".view");
// Cleaned up in 0.16.1: rpc::rpc.mark_safe("group." + name + ".view.set");
rpc::rpc.mark_safe("group." + name + ".ratio.min");
CMD2_REDIRECT_MUTABLE("group2." + name + ".view", "group." + name + ".view"); rpc::rpc.mark_safe("group." + name + ".ratio.min.set");
CMD2_REDIRECT_MUTABLE("group2." + name + ".view.set", "group." + name + ".view.set"); rpc::rpc.mark_safe("group." + name + ".ratio.max");
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.min", "group." + name + ".ratio.min"); rpc::rpc.mark_safe("group." + name + ".ratio.max.set");
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.min.set", "group." + name + ".ratio.min.set"); rpc::rpc.mark_safe("group." + name + ".ratio.upload");
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.max", "group." + name + ".ratio.max"); rpc::rpc.mark_safe("group." + name + ".ratio.upload.set");
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.max.set", "group." + name + ".ratio.max.set");
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.upload", "group." + name + ".ratio.upload");
CMD2_REDIRECT_MUTABLE("group2." + name + ".ratio.upload.set", "group." + name + ".ratio.upload.set");
}
return name; return name;
} }
@@ -171,12 +171,16 @@ cmd_file_append(const torrent::Object::list_type& args) {
FILE* output = fopen(args.front().as_string().c_str(), "a"); FILE* output = fopen(args.front().as_string().c_str(), "a");
if (output == NULL) if (output == nullptr)
throw torrent::input_error("Could not append to file '" + args.front().as_string() + "': " + rak::error_number::current().c_str()); throw torrent::input_error("Could not append to file '" + args.front().as_string() + "': " + std::strerror(errno));
file_print_list(++args.begin(), args.end(), output, file_print_delim_space); try {
file_print_list(++args.begin(), args.end(), output, file_print_delim_space);
fprintf(output, "\n"); fprintf(output, "\n");
} catch (...) {
fclose(output);
throw;
}
fclose(output); fclose(output);
return torrent::Object(); return torrent::Object();
} }
@@ -184,137 +188,211 @@ cmd_file_append(const torrent::Object::list_type& args) {
void void
initialize_command_local() { initialize_command_local() {
core::DownloadList* dList = control->core()->download_list(); core::DownloadList* dList = control->core()->download_list();
core::DownloadStore* dStore = control->core()->download_store();
torrent::ChunkManager* chunkManager = torrent::chunk_manager();
torrent::FileManager* fileManager = torrent::file_manager(); torrent::FileManager* fileManager = torrent::file_manager();
CMD2_ANY ("system.hostname", std::bind(&system_hostname)); CMD_ANY ("system.hostname", std::bind(&system_hostname));
CMD2_ANY ("system.pid", std::bind(&getpid)); CMD_ANY ("system.pid", std::bind(&getpid));
CMD2_VAR_C_STRING("system.api_version", (int64_t)API_VERSION); CMD_VAR_C_STRING("system.api_version", (int64_t)API_VERSION);
CMD2_VAR_C_STRING("system.client_version", PACKAGE_VERSION); CMD_VAR_C_STRING("system.client_version", PACKAGE_VERSION);
CMD2_VAR_C_STRING("system.library_version", torrent::version()); CMD_VAR_C_STRING("system.library_version", torrent::runtime::version());
CMD2_VAR_VALUE ("system.file.allocate", 0); CMD_VAR_VALUE ("system.file.allocate", 0);
CMD2_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30); CMD_VAR_VALUE ("system.file.max_size", (int64_t)512 << 30);
CMD2_VAR_VALUE ("system.file.split_size", -1); CMD_VAR_VALUE ("system.file.split_size", -1);
CMD2_VAR_STRING ("system.file.split_suffix", ".part"); CMD_VAR_STRING ("system.file.split_suffix", ".part");
CMD2_ANY ("system.file_status_cache.size", std::bind(&utils::FileStatusCache::size, CMD_ANY ("system.file_status_cache.size", std::bind(&utils::FileStatusCache::size,
(utils::FileStatusCache::base_type*)control->core()->file_status_cache())); (utils::FileStatusCache::base_type*)control->core()->file_status_cache()));
CMD2_ANY_V ("system.file_status_cache.prune", std::bind(&utils::FileStatusCache::prune, control->core()->file_status_cache())); CMD_ANY_V ("system.file_status_cache.prune", std::bind(&utils::FileStatusCache::prune, control->core()->file_status_cache()));
CMD2_VAR_BOOL ("file.prioritize_toc", 0); CMD_VAR_BOOL ("file.prioritize_toc", 0);
CMD2_VAR_LIST ("file.prioritize_toc.first"); CMD_VAR_LIST ("file.prioritize_toc.first");
CMD2_VAR_LIST ("file.prioritize_toc.last"); CMD_VAR_LIST ("file.prioritize_toc.last");
CMD2_ANY ("system.files.advise_random", std::bind(&FM_t::advise_random, fileManager)); CMD_ANY ("system.files.advise_random", std::bind(&FM_t::advise_random, fileManager));
CMD2_ANY_VALUE_V ("system.files.advise_random.set", std::bind(&FM_t::set_advise_random, fileManager, std::placeholders::_2)); CMD_ANY_VALUE_V ("system.files.advise_random.set", std::bind(&FM_t::set_advise_random, fileManager, std::placeholders::_2));
CMD2_ANY ("system.files.advise_random.hashing", std::bind(&FM_t::advise_random_hashing, fileManager)); CMD_ANY ("system.files.advise_random.hashing", std::bind(&FM_t::advise_random_hashing, fileManager));
CMD2_ANY_VALUE_V ("system.files.advise_random.hashing.set", std::bind(&FM_t::set_advise_random_hashing, fileManager, std::placeholders::_2)); CMD_ANY_VALUE_V ("system.files.advise_random.hashing.set", std::bind(&FM_t::set_advise_random_hashing, fileManager, std::placeholders::_2));
CMD2_VAR_BOOL ("system.files.session.fdatasync", true); 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); });
CMD2_ANY ("system.files.opened_counter", std::bind(&FM_t::files_opened_counter, fileManager)); CMD_ANY ("system.files.opened_counter", std::bind(&FM_t::files_opened_counter, fileManager));
CMD2_ANY ("system.files.closed_counter", std::bind(&FM_t::files_closed_counter, fileManager)); CMD_ANY ("system.files.closed_counter", std::bind(&FM_t::files_closed_counter, fileManager));
CMD2_ANY ("system.files.failed_counter", std::bind(&FM_t::files_failed_counter, fileManager)); CMD_ANY ("system.files.failed_counter", std::bind(&FM_t::files_failed_counter, fileManager));
CMD2_ANY_STRING ("system.env", std::bind(&system_env, std::placeholders::_2)); CMD_ANY_STRING ("system.env", [](auto, auto& str) { return system_env(str); });
CMD2_ANY ("system.time", []([[maybe_unused]] auto t, [[maybe_unused]] auto o) -> torrent::Object { CMD_ANY ("system.time", [](auto, auto) { return torrent::this_thread::cached_seconds().count(); });
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(); });
CMD2_ANY ("system.time_seconds", []([[maybe_unused]] auto t, [[maybe_unused]] auto o) -> torrent::Object {
return torrent::utils::cast_seconds(torrent::utils::time_since_epoch()).count();
});
CMD2_ANY ("system.time_usec", []([[maybe_unused]] auto t, [[maybe_unused]] auto o) -> torrent::Object {
return torrent::utils::time_since_epoch().count();
});
CMD2_ANY_VALUE_V ("system.umask.set", std::bind(&umask, std::placeholders::_2)); CMD_ANY_VALUE_V ("system.umask.set", [](auto, auto& value) { return ::umask(value); });
CMD2_VAR_BOOL ("system.daemon", false); CMD_VAR_BOOL ("system.daemon", false);
CMD2_ANY_V ("system.shutdown.normal", std::bind(&Control::receive_normal_shutdown, control)); CMD_ANY_V ("system.shutdown.normal", [](auto, auto) { control->receive_normal_shutdown(); });
CMD2_ANY_V ("system.shutdown.quick", std::bind(&Control::receive_quick_shutdown, control)); CMD_ANY_V ("system.shutdown.quick", [](auto, auto) { control->receive_quick_shutdown(); });
CMD2_REDIRECT_NO_EXPORT("system.shutdown", "system.shutdown.normal");
CMD2_ANY ("system.cwd", std::bind(&system_get_cwd)); CMD_REDIRECT_NO_EXPORT("system.shutdown", "system.shutdown.normal");
CMD2_ANY_STRING ("system.cwd.set", std::bind(&system_set_cwd, std::placeholders::_2));
CMD2_ANY ("pieces.sync.always_safe", std::bind(&CM_t::safe_sync, chunkManager)); CMD_ANY ("system.cwd", [](auto, auto) { return system_get_cwd(); });
CMD2_ANY_VALUE_V ("pieces.sync.always_safe.set", std::bind(&CM_t::set_safe_sync, chunkManager, std::placeholders::_2)); CMD_ANY_STRING ("system.cwd.set", [](auto, auto& str) { return system_set_cwd(str); });
CMD2_ANY ("pieces.sync.safe_free_diskspace", std::bind(&CM_t::safe_free_diskspace, chunkManager));
CMD2_ANY ("pieces.sync.timeout", std::bind(&CM_t::timeout_sync, chunkManager));
CMD2_ANY_VALUE_V ("pieces.sync.timeout.set", std::bind(&CM_t::set_timeout_sync, chunkManager, std::placeholders::_2));
CMD2_ANY ("pieces.sync.timeout_safe", std::bind(&CM_t::timeout_safe_sync, chunkManager));
CMD2_ANY_VALUE_V ("pieces.sync.timeout_safe.set", std::bind(&CM_t::set_timeout_safe_sync, chunkManager, std::placeholders::_2));
CMD2_ANY ("pieces.sync.queue_size", std::bind(&CM_t::sync_queue_size, chunkManager));
CMD2_ANY ("pieces.preload.type", std::bind(&CM_t::preload_type, chunkManager)); CMD_ANY ("system.sockets.size", [](auto, auto) { return torrent::runtime::socket_manager()->size(); });
CMD2_ANY_VALUE_V ("pieces.preload.type.set", std::bind(&CM_t::set_preload_type, chunkManager, std::placeholders::_2)); CMD_ANY ("system.sockets.max_size", [](auto, auto) { return torrent::runtime::socket_manager()->max_size(); });
CMD2_ANY ("pieces.preload.min_size", std::bind(&CM_t::preload_min_size, chunkManager)); CMD_ANY_VALUE_V ("system.sockets.max_size.set", [](auto, auto& value) { return torrent::runtime::socket_manager()->set_max_size_and_adjust(value); });
CMD2_ANY_VALUE_V ("pieces.preload.min_size.set", std::bind(&CM_t::set_preload_min_size, chunkManager, std::placeholders::_2)); CMD_ANY_V ("system.sockets.adjust_alloc", [](auto, auto) { torrent::runtime::socket_manager()->adjust_allocation(); });
CMD2_ANY ("pieces.preload.min_rate", std::bind(&CM_t::preload_required_rate, chunkManager)); CMD_ANY ("system.sockets.reserved_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->reserved_allocation(); });
CMD2_ANY_VALUE_V ("pieces.preload.min_rate.set", std::bind(&CM_t::set_preload_required_rate, chunkManager, std::placeholders::_2)); CMD_ANY ("system.sockets.available_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->available_allocation(); });
CMD2_ANY ("pieces.memory.current", std::bind(&CM_t::memory_usage, chunkManager)); for (uint32_t i = 0; i < torrent::runtime::SocketManager::category_count; ++i) {
CMD2_ANY ("pieces.memory.sync_queue", std::bind(&CM_t::sync_queue_memory_usage, chunkManager)); auto category = static_cast<torrent::runtime::socket_manager_category_t>(i);
CMD2_ANY ("pieces.memory.block_count", std::bind(&CM_t::memory_block_count, chunkManager)); auto category_name = "system.sockets." + torrent::option_to_str_or_throw(torrent::OPTION_SOCKET_CATEGORY, i);
CMD2_ANY ("pieces.memory.max", std::bind(&CM_t::max_memory_usage, chunkManager));
CMD2_ANY_VALUE_V ("pieces.memory.max.set", std::bind(&CM_t::set_max_memory_usage, chunkManager, std::placeholders::_2));
CMD2_ANY ("pieces.stats_preloaded", std::bind(&CM_t::stats_preloaded, chunkManager));
CMD2_ANY ("pieces.stats_not_preloaded", std::bind(&CM_t::stats_not_preloaded, chunkManager));
CMD2_ANY ("pieces.stats.total_size", std::bind(&apply_pieces_stats_total_size)); 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); });
CMD2_ANY ("pieces.hash.queue_size", std::bind(&torrent::main_thread::hash_queue_size)); if (i == 0) {
CMD2_VAR_BOOL ("pieces.hash.on_completion", true); CMD_ANY (category_name + ".min_alloc", [](auto, auto) { return torrent::runtime::socket_manager()->generic_min_allocation(); });
continue;
}
CMD2_VAR_STRING ("directory.default", "./"); 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, value); });
CMD_ANY_VALUE_V(category_name + ".max_alloc.set", [category](auto, auto& value) { torrent::runtime::socket_manager()->set_category_max_allocation(category, value); });
}
CMD2_VAR_STRING ("session.name", ""); CMD_ANY ("pieces.sync.always_safe", [](auto, auto) { return torrent::runtime::memory_manager()->safe_sync(); });
CMD2_VAR_BOOL ("session.use_lock", true); CMD_ANY_VALUE_V ("pieces.sync.always_safe.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_safe_sync(value); });
CMD2_VAR_BOOL ("session.on_completion", true); 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_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_VALUE_V ("pieces.sync.timeout_safe.set", [](auto, auto) { });
CMD_ANY ("pieces.sync.queue_size", [](auto, auto) { return torrent::runtime::memory_manager()->sync_queue_block_count(); });
CMD2_ANY ("session.path", std::bind(&core::DownloadStore::path, dStore)); CMD_ANY ("pieces.preload.type", [](auto, auto) { return torrent::runtime::memory_manager()->preload_type(); });
CMD2_ANY_STRING_V("session.path.set", std::bind(&core::DownloadStore::set_path, dStore, std::placeholders::_2)); CMD_ANY_VALUE_V ("pieces.preload.type.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_preload_type(value); });
CMD_ANY ("pieces.preload.min_size", [](auto, auto) { return torrent::runtime::memory_manager()->preload_min_size(); });
CMD_ANY_VALUE_V ("pieces.preload.min_size.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_preload_min_size(value); });
CMD_ANY ("pieces.preload.min_rate", [](auto, auto) { return torrent::runtime::memory_manager()->preload_required_rate(); });
CMD_ANY_VALUE_V ("pieces.preload.min_rate.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_preload_required_rate(value); });
CMD2_ANY_V ("session.save", std::bind(&core::DownloadList::session_save, dList)); CMD_ANY ("pieces.stats_preloaded", [](auto, auto) { return torrent::runtime::memory_manager()->stats_preloaded(); });
CMD_ANY ("pieces.stats_not_preloaded", [](auto, auto) { return torrent::runtime::memory_manager()->stats_not_preloaded(); });
CMD_ANY ("pieces.stats.total_size", std::bind(&apply_pieces_stats_total_size));
CMD_ANY ("pieces.memory.current", [](auto, auto) { return torrent::runtime::memory_manager()->memory_usage(); });
CMD_ANY ("pieces.memory.sync_queue", [](auto, auto) { return torrent::runtime::memory_manager()->sync_queue_memory_usage(); });
CMD_ANY ("pieces.memory.block_count", [](auto, auto) { return torrent::runtime::memory_manager()->memory_block_count(); });
CMD_ANY ("pieces.memory.max", [](auto, auto) { return torrent::runtime::memory_manager()->max_memory_usage(); });
CMD_ANY_VALUE_V ("pieces.memory.max.set", [](auto, auto& value) { return torrent::runtime::memory_manager()->set_max_memory_usage(value); });
CMD_ANY ("pieces.hash.queue_size", std::bind(&torrent::main_thread::hash_queue_size));
CMD_VAR_BOOL ("pieces.hash.on_completion", true);
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_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 ("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_VAR_BOOL ("session.on_completion", true);
CMD_ANY_V ("session.save", [dList](auto, auto) { return dList->session_save(); });
CMD_ANY ("magnet.path", [](auto, auto) { return control->core()->magnet_path(); });
CMD_ANY_STRING_V("magnet.path.set", [](auto, auto& str) { return control->core()->set_magnet_path(str); });
#ifdef HAVE_LUA #ifdef HAVE_LUA
rpc::LuaEngine* lua_engine = control->lua_engine(); rpc::LuaEngine* lua_engine = control->lua_engine();
CMD2_ANY ("lua.execute", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, 0)); CMD_ANY ("lua.execute", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, 0));
CMD2_ANY ("lua.execute.str", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, rpc::LuaEngine::flag_string)); CMD_ANY ("lua.execute.str", std::bind(&rpc::execute_lua, lua_engine, std::placeholders::_1, std::placeholders::_2, rpc::LuaEngine::flag_string));
#endif #endif
#define CMD2_EXECUTE(key, flags) \ #define CMD_EXECUTE(key, flags) \
CMD2_ANY(key, std::bind(&rpc::ExecFile::execute_object, &rpc::execFile, std::placeholders::_2, flags)); CMD_ANY(key, std::bind(&rpc::ExecFile::execute_object, &rpc::execFile, std::placeholders::_2, flags));
CMD2_EXECUTE ("execute", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw); CMD_EXECUTE ("execute", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw);
CMD2_EXECUTE ("execute2", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw); CMD_EXECUTE ("execute.throw", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw);
CMD2_EXECUTE ("execute.throw", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw); CMD_EXECUTE ("execute.throw.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background);
CMD2_EXECUTE ("execute.throw.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background); CMD_EXECUTE ("execute.nothrow", rpc::ExecFile::flag_expand_tilde);
CMD2_EXECUTE ("execute.nothrow", rpc::ExecFile::flag_expand_tilde); CMD_EXECUTE ("execute.nothrow.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_background);
CMD2_EXECUTE ("execute.nothrow.bg", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_background); CMD_EXECUTE ("execute.raw", rpc::ExecFile::flag_throw);
CMD2_EXECUTE ("execute.raw", rpc::ExecFile::flag_throw); CMD_EXECUTE ("execute.raw.bg", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background);
CMD2_EXECUTE ("execute.raw.bg", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_background); CMD_EXECUTE ("execute.raw_nothrow", 0);
CMD2_EXECUTE ("execute.raw_nothrow", 0); CMD_EXECUTE ("execute.raw_nothrow.bg", rpc::ExecFile::flag_background);
CMD2_EXECUTE ("execute.raw_nothrow.bg", rpc::ExecFile::flag_background); CMD_EXECUTE ("execute.capture", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture);
CMD2_EXECUTE ("execute.capture", rpc::ExecFile::flag_throw | rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture); CMD_EXECUTE ("execute.capture_nothrow", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture);
CMD2_EXECUTE ("execute.capture_nothrow", rpc::ExecFile::flag_expand_tilde | rpc::ExecFile::flag_capture);
CMD2_ANY_LIST ("file.append", std::bind(&cmd_file_append, std::placeholders::_2));
// TODO: Convert to new command types: // TODO: Convert to new command types:
*rpc::command_base::argument(0) = "placeholder.0"; *rpc::command_base::argument(0) = "placeholder.0";
*rpc::command_base::argument(1) = "placeholder.1"; *rpc::command_base::argument(1) = "placeholder.1";
*rpc::command_base::argument(2) = "placeholder.2"; *rpc::command_base::argument(2) = "placeholder.2";
*rpc::command_base::argument(3) = "placeholder.3"; *rpc::command_base::argument(3) = "placeholder.3";
CMD2_ANY_P("argument.0", std::bind(&rpc::command_base::argument_ref, 0)); CMD_ANY_P("argument.0", std::bind(&rpc::command_base::argument_ref, 0));
CMD2_ANY_P("argument.1", std::bind(&rpc::command_base::argument_ref, 1)); CMD_ANY_P("argument.1", std::bind(&rpc::command_base::argument_ref, 1));
CMD2_ANY_P("argument.2", std::bind(&rpc::command_base::argument_ref, 2)); CMD_ANY_P("argument.2", std::bind(&rpc::command_base::argument_ref, 2));
CMD2_ANY_P("argument.3", std::bind(&rpc::command_base::argument_ref, 3)); CMD_ANY_P("argument.3", std::bind(&rpc::command_base::argument_ref, 3));
CMD2_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");
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");
}
rpc::rpc.mark_safe("directory.default");
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.on_completion");
rpc::rpc.mark_safe("pieces.sync.always_safe");
rpc::rpc.mark_safe("pieces.sync.timeout");
rpc::rpc.mark_safe("pieces.sync.timeout_safe");
rpc::rpc.mark_safe("pieces.preload.type");
rpc::rpc.mark_safe("pieces.preload.min_size");
rpc::rpc.mark_safe("pieces.preload.min_rate");
rpc::rpc.mark_safe("pieces.memory.max");
rpc::rpc.mark_safe("pieces.hash.on_completion");
} }
+58 -54
View File
@@ -1,6 +1,7 @@
#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>
@@ -8,53 +9,24 @@
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h> #include <torrent/utils/option_strings.h>
#include "control.h"
#include "command_helpers.h"
#include "globals.h"
#include "setup.h"
#include "core/download.h" #include "core/download.h"
#include "core/download_list.h" #include "core/download_list.h"
#include "core/manager.h" #include "core/manager.h"
#include "rak/path.h" #include "rpc/parse.h"
#include "rpc/parse_commands.h" #include "rpc/parse_commands.h"
#include "globals.h"
#include "control.h"
#include "command_helpers.h"
static const int log_flag_use_gz = 0x1;
static const int log_flag_append_pid = 0x2;
static const int log_flag_append_file = 0x4;
void
log_add_group_output_str(const char* group_name, const char* output_id) {
int log_group = torrent::option_find_string(torrent::OPTION_LOG_GROUP, group_name);
torrent::log_add_group_output(log_group, output_id);
}
torrent::Object torrent::Object
apply_log_open(int output_flags, const torrent::Object::list_type& args) { apply_log_open(int output_flags, const torrent::Object::list_type& raw_args) {
if (args.size() < 2) std::vector<std::string> args;
throw torrent::input_error("Invalid number of arguments.");
torrent::Object::list_const_iterator itr = args.begin(); for (const auto& arg : raw_args)
args.push_back(arg.as_string());
std::string output_id = (itr++)->as_string();
std::string file_name = rak::path_expand((itr++)->as_string());
if ((output_flags & log_flag_append_pid)) {
char buffer[32];
snprintf(buffer, 32, ".%li", (long)getpid());
file_name += buffer;
}
bool append = (output_flags & log_flag_append_file);
if ((output_flags & log_flag_use_gz))
torrent::log_open_gz_file_output(output_id.c_str(), file_name.c_str(), append);
else
torrent::log_open_file_output(output_id.c_str(), file_name.c_str(), append);
while (itr != args.end())
log_add_group_output_str((itr++)->as_string().c_str(), output_id.c_str());
apply_log_open_str(output_flags, args);
return torrent::Object(); return torrent::Object();
} }
@@ -63,12 +35,37 @@ apply_log_add_output(const torrent::Object::list_type& args) {
if (args.size() != 2) if (args.size() != 2)
throw torrent::input_error("Invalid number of arguments."); throw torrent::input_error("Invalid number of arguments.");
log_add_group_output_str(args.front().as_string().c_str(), log_add_group_output_str(args.front().as_string(), args.back().as_string());
args.back().as_string().c_str());
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) {
@@ -86,7 +83,7 @@ apply_log(const torrent::Object::string_type& arg, int logType) {
} }
if (!arg.empty()) { if (!arg.empty()) {
int logFd = open(rak::path_expand(arg).c_str(), O_WRONLY | O_APPEND | O_CREAT, 0644); int logFd = open(expand_path(arg).c_str(), O_WRONLY | O_APPEND | O_CREAT, 0644);
if (logFd < 0) if (logFd < 0)
throw torrent::input_error("Could not open execute log file."); throw torrent::input_error("Could not open execute log file.");
@@ -118,6 +115,9 @@ log_vmmap_dump(const std::string& str) {
FILE* log_file = fopen(str.c_str(), "w"); FILE* log_file = fopen(str.c_str(), "w");
if (log_file == NULL)
throw torrent::input_error("Could not open log file: " + str);
for (auto& all_mapping : all_mappings) { for (auto& all_mapping : all_mappings) {
fprintf(log_file, "%8p-%8p [%5llxk]\n", all_mapping.ptr, (char*)all_mapping.ptr + all_mapping.length, (long long unsigned int)(all_mapping.length / 1024)); fprintf(log_file, "%8p-%8p [%5llxk]\n", all_mapping.ptr, (char*)all_mapping.ptr + all_mapping.length, (long long unsigned int)(all_mapping.length / 1024));
} }
@@ -128,19 +128,23 @@ log_vmmap_dump(const std::string& str) {
void void
initialize_command_logging() { initialize_command_logging() {
CMD2_ANY_LIST ("log.open_file", std::bind(&apply_log_open, 0, std::placeholders::_2)); CMD2_ANY_LIST ("log.open_file", std::bind(&apply_log_open, 0, std::placeholders::_2));
CMD2_ANY_LIST ("log.open_gz_file", std::bind(&apply_log_open, log_flag_use_gz, std::placeholders::_2)); CMD2_ANY_LIST ("log.open_file.flush", std::bind(&apply_log_open, log_flag_flush, std::placeholders::_2));
CMD2_ANY_LIST ("log.open_file_pid", std::bind(&apply_log_open, log_flag_append_pid, std::placeholders::_2)); CMD2_ANY_LIST ("log.open_gz_file", std::bind(&apply_log_open, 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.open_file_pid", std::bind(&apply_log_open, log_flag_append_pid, 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.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_gz_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_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));
CMD2_ANY_STRING_V("log.rpc", std::bind(&ThreadWorker::set_rpc_log, worker_thread, std::placeholders::_2)); CMD2_ANY_STRING_V("log.rpc", [](const auto&, const auto& str) { scgi_thread::set_rpc_log(str); });
CMD2_REDIRECT ("log.xmlrpc", "log.rpc"); // For backwards compatibility
CMD2_REDIRECT ("log.xmlrpc", "log.rpc"); // For backwards compatibility
} }
+305 -120
View File
@@ -3,20 +3,25 @@
#include <functional> #include <functional>
#include <cstdio> #include <cstdio>
#include <unistd.h> #include <unistd.h>
#include <rak/address_info.h>
#include <rak/path.h>
#include <torrent/torrent.h> #include <torrent/torrent.h>
#include <torrent/rate.h> #include <torrent/rate.h>
#include <torrent/data/file_manager.h> #include <torrent/data/file_manager.h>
#include <torrent/download/resource_manager.h> #include <torrent/download/resource_manager.h>
#include <torrent/net/http_stack.h> #include <torrent/net/http_stack.h>
#include <torrent/net/network_config.h>
#include <torrent/net/network_manager.h>
#include <torrent/net/socket_address.h> #include <torrent/net/socket_address.h>
#include <torrent/runtime/client_config.h>
#include <torrent/runtime/network_config.h>
#include <torrent/runtime/network_manager.h>
#include <torrent/runtime/proxy_manager.h>
#include <torrent/runtime/runtime.h>
#include <torrent/runtime/socket_manager.h>
#include <torrent/tracker/tracker.h> #include <torrent/tracker/tracker.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include <torrent/utils/option_strings.h> #include <torrent/utils/option_strings.h>
#include "globals.h"
#include "control.h"
#include "command_helpers.h"
#include "core/download.h" #include "core/download.h"
#include "core/manager.h" #include "core/manager.h"
#include "rpc/scgi.h" #include "rpc/scgi.h"
@@ -24,26 +29,121 @@
#include "rpc/parse.h" #include "rpc/parse.h"
#include "rpc/parse_commands.h" #include "rpc/parse_commands.h"
#include "globals.h" #ifdef HAVE_SYSTEMD
#include "control.h" #include <sys/socket.h>
#include "command_helpers.h" #include <systemd/sd-daemon.h>
#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 torrent::Object
apply_encryption(const torrent::Object::list_type& args) { apply_encryption(const torrent::Object::list_type& args) {
uint32_t options_mask = torrent::net::NetworkConfig::encryption_none; if (args.empty())
throw torrent::input_error("No encryption options specified.");
for (const auto& arg : args) { torrent::encryption_mode encryption_mode, handshake_mode, stream_mode;
uint32_t opt = torrent::option_find_string(torrent::OPTION_ENCRYPTION, arg.as_string().c_str());
if (opt == torrent::net::NetworkConfig::encryption_none) if (args.size() == 1) {
options_mask = torrent::net::NetworkConfig::encryption_none; try {
else encryption_mode = static_cast<torrent::encryption_mode>(torrent::option_find_string_str(torrent::OPTION_ENCRYPTION_MODE, args.front().as_string()));
options_mask |= opt;
} catch (torrent::input_error& e) {
return apply_obsolete_encryption(args);
}
torrent::runtime::network_config()->set_encryption_modes(encryption_mode, encryption_mode);
return {};
} }
torrent::config::network_config()->set_encryption_options(options_mask); if (args.size() != 2)
return apply_obsolete_encryption(args);
return torrent::Object(); 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
@@ -53,13 +153,11 @@ apply_tos(const torrent::Object::string_type& arg) {
if (!rpc::parse_whole_value_nothrow(arg.c_str(), &value, 16, 1)) if (!rpc::parse_whole_value_nothrow(arg.c_str(), &value, 16, 1))
value = torrent::option_find_string(torrent::OPTION_IP_TOS, arg.c_str()); value = torrent::option_find_string(torrent::OPTION_IP_TOS, arg.c_str());
torrent::config::network_config()->set_priority(value); torrent::runtime::network_config()->set_priority(value);
return torrent::Object(); return torrent::Object();
} }
torrent::Object apply_encoding_list(const std::string& arg) { torrent::encoding_list()->push_back(arg); return torrent::Object(); }
void void
initialize_rpc_handlers() { initialize_rpc_handlers() {
rpc::rpc.initialize_handlers(); rpc::rpc.initialize_handlers();
@@ -79,19 +177,18 @@ initialize_rpc_handlers() {
torrent::Object torrent::Object
apply_scgi(const std::string& arg, int type) { apply_scgi(const std::string& arg, int type) {
if (worker_thread->scgi() != NULL) if (scgi_thread::scgi() != nullptr)
throw torrent::input_error("SCGI already enabled."); throw torrent::input_error("SCGI already enabled.");
initialize_rpc_handlers(); initialize_rpc_handlers();
rpc::SCgi* scgi = new rpc::SCgi;
rak::address_info* ai = NULL;
torrent::sa_unique_ptr sa; torrent::sa_unique_ptr sa;
auto scgi = std::make_unique<rpc::SCgi>();
try { try {
int port, err; int port{};
char dummy; char dummy{};
char address[1024]; char address[1024];
std::string path; std::string path;
@@ -105,10 +202,11 @@ apply_scgi(const std::string& arg, int type) {
} else if (std::sscanf(arg.c_str(), "%1023[^:]:%i%c", address, &port, &dummy) == 2 || } else if (std::sscanf(arg.c_str(), "%1023[^:]:%i%c", address, &port, &dummy) == 2 ||
std::sscanf(arg.c_str(), "[%64[^]]]:%i%c", address, &port, &dummy) == 2) { // [xx::xx]:port format std::sscanf(arg.c_str(), "[%64[^]]]:%i%c", address, &port, &dummy) == 2) { // [xx::xx]:port format
if ((err = rak::address_info::get_address_info(address, PF_UNSPEC, SOCK_STREAM, &ai)) != 0) try {
throw torrent::input_error("Could not bind address: " + std::string(rak::address_info::strerror(err)) + "."); sa = torrent::sa_copy(torrent::sa_lookup_address(address, AF_UNSPEC).get());
} catch (torrent::input_error& e) {
sa = torrent::sa_copy(ai->c_addrinfo()->ai_addr); throw torrent::input_error("Could not bind address: " + std::string(e.what()));
}
lt_log_print(torrent::LOG_RPC_EVENTS, "SCGI socket is bound to an address and might be a security risk"); lt_log_print(torrent::LOG_RPC_EVENTS, "SCGI socket is bound to an address and might be a security risk");
@@ -126,26 +224,75 @@ apply_scgi(const std::string& arg, int type) {
case 2: case 2:
default: default:
path = rak::path_expand(arg); path = expand_path(arg);
unlink(path.c_str()); unlink(path.c_str());
scgi->open_named(path); scgi->open_named(path);
break; break;
} }
if (ai != NULL) rak::address_info::free_address_info(ai);
} catch (torrent::local_error& e) { } catch (torrent::local_error& e) {
if (ai != NULL) rak::address_info::free_address_info(ai);
delete scgi;
throw torrent::input_error(e.what()); throw torrent::input_error(e.what());
} }
worker_thread->set_scgi(scgi); scgi_thread::set_scgi(scgi.release());
return torrent::Object(); return torrent::Object();
} }
torrent::Object
apply_scgi_systemd() {
#ifdef HAVE_SYSTEMD
if (scgi_thread::scgi() != nullptr)
throw torrent::input_error("SCGI already enabled.");
int n = sd_listen_fds(0);
if (n < 1)
throw torrent::input_error("No systemd socket(s) provided (sd_listen_fds returned " +
std::to_string(n) + ").");
// Iterate over all provided fds. Use the first listening stream socket;
// close the rest. The systemd docs say unused fds should be closed.
int selected_fd = -1;
for (int i = 0; i < n; i++) {
int fd = SD_LISTEN_FDS_START + i;
if (selected_fd != -1) {
::close(fd);
continue;
}
auto err = sd_is_socket(fd, AF_UNSPEC, SOCK_STREAM, 1);
if (err < 0) {
// Safe to ignore errors here - we just skip it and move on.
::close(fd);
continue;
}
if (err == 0) {
// Not the socket we're looking for.
::close(fd);
continue;
}
selected_fd = fd;
}
if (selected_fd == -1)
throw torrent::input_error("No listening stream socket found among systemd-provided fds.");
initialize_rpc_handlers();
rpc::SCgi* scgi = new rpc::SCgi;
scgi->open_fd(selected_fd);
scgi_thread::set_scgi(scgi);
return torrent::Object();
#else
throw torrent::input_error("Systemd SCGI endpoint is not supported.");
#endif
}
torrent::Object torrent::Object
apply_xmlrpc_dialect(const std::string& arg) { apply_xmlrpc_dialect(const std::string& arg) {
int value; int value;
@@ -165,104 +312,142 @@ apply_xmlrpc_dialect(const std::string& arg) {
void void
initialize_command_network() { initialize_command_network() {
auto cm = torrent::connection_manager(); auto file_manager = torrent::file_manager();
auto file_manager = torrent::file_manager(); auto http_stack = torrent::net_thread::http_stack();
auto http_stack = torrent::net_thread::http_stack(); auto nw_config = torrent::runtime::network_config();
auto nw_config = torrent::config::network_config();
auto nw_manager = torrent::runtime::network_manager();
CMD2_ANY_STRING ("encoding.add", std::bind(&apply_encoding_list, std::placeholders::_2)); 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(value); });
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 ("network.listen.port.range", [](auto, auto) { return listen_port_range(); });
CMD_ANY_STRING_V("network.listen.port.range.set", [](auto, auto& value) { return set_listen_port_range(value); });
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); });
// Isn't port_open used? CMD_ANY ("protocol.pex", [](auto, auto) { return torrent::runtime::client_config()->is_pex_enabled(); });
CMD2_VAR_BOOL ("network.port_open", true); CMD_ANY_VALUE_V ("protocol.pex.set", [](auto, auto& value) { return torrent::runtime::client_config()->set_pex_enabled(value); });
CMD2_VAR_BOOL ("network.port_random", true);
CMD2_VAR_STRING ("network.port_range", "6881-6999");
CMD2_ANY ("network.listen.port", [nw_manager](auto, auto) { return nw_manager->listen_port(); }); CMD_ANY_LIST ("protocol.encryption", [](auto, auto) { return get_encryption(); });
CMD2_ANY ("network.listen.backlog", [nw_config](auto, auto) { return nw_config->listen_backlog(); }); CMD_ANY_LIST ("protocol.encryption.set", [](auto, auto& args) { return apply_encryption(args); });
CMD2_ANY_VALUE_V ("network.listen.backlog.set", [nw_config](auto, auto& value) { return nw_config->set_listen_backlog(value); }); CMD_ANY_LIST ("protocol.encryption.handshake", [](auto, auto) { return get_handshake_encryption(); });
CMD_ANY_LIST ("protocol.encryption.stream", [](auto, auto) { return get_stream_encryption(); });
CMD2_VAR_BOOL ("protocol.pex", true); CMD_VAR_STRING ("protocol.connection.leech", "leech");
CMD2_ANY_LIST ("protocol.encryption.set", [](auto, auto& args) { return apply_encryption(args); }); CMD_VAR_STRING ("protocol.connection.seed", "seed");
CMD2_VAR_STRING ("protocol.connection.leech", "leech"); CMD_VAR_STRING ("protocol.choke_heuristics.up.leech", "upload_leech");
CMD2_VAR_STRING ("protocol.connection.seed", "seed"); CMD_VAR_STRING ("protocol.choke_heuristics.up.seed", "upload_leech");
CMD_VAR_STRING ("protocol.choke_heuristics.down.leech", "download_leech");
CMD_VAR_STRING ("protocol.choke_heuristics.down.seed", "download_leech");
CMD2_VAR_STRING ("protocol.choke_heuristics.up.leech", "upload_leech"); CMD_ANY ("network.http.cacert", [http_stack](auto, auto) { return http_stack->http_cacert(); });
CMD2_VAR_STRING ("protocol.choke_heuristics.up.seed", "upload_leech"); CMD_ANY_STRING_V("network.http.cacert.set", [http_stack](auto, auto& str) { return http_stack->set_http_cacert(str); });
CMD2_VAR_STRING ("protocol.choke_heuristics.down.leech", "download_leech"); CMD_ANY ("network.http.capath", [http_stack](auto, auto) { return http_stack->http_capath(); });
CMD2_VAR_STRING ("protocol.choke_heuristics.down.seed", "download_leech"); CMD_ANY_STRING_V("network.http.capath.set", [http_stack](auto, auto& str) { return http_stack->set_http_capath(str); });
CMD_ANY ("network.http.dns_cache_timeout", [http_stack](auto, auto) { return http_stack->dns_timeout(); });
CMD_ANY_VALUE_V ("network.http.dns_cache_timeout.set", [http_stack](auto, auto& value) { return http_stack->set_dns_timeout(value); });
CMD_ANY ("network.http.current_open", [http_stack](auto, auto) { return http_stack->size(); });
CMD_ANY ("network.http.max_cache_connections", [http_stack](auto, auto) { return http_stack->max_cache_connections(); });
CMD_ANY_VALUE_V ("network.http.max_cache_connections.set", [http_stack](auto, auto& value) { return http_stack->set_max_cache_connections(value); });
CMD_ANY ("network.http.max_host_connections", [http_stack](auto, auto) { return http_stack->max_host_connections(); });
CMD_ANY_VALUE_V ("network.http.max_host_connections.set", [http_stack](auto, auto& value) { return http_stack->set_max_host_connections(value); });
CMD_ANY ("network.http.max_total_connections", [http_stack](auto, auto) { return http_stack->max_total_connections(); });
CMD2_ANY ("network.http.cacert", [http_stack](auto, auto) { return http_stack->http_cacert(); }); CMD_ANY ("network.http.ssl_verify_host", [http_stack](auto, auto) { return http_stack->ssl_verify_host(); });
CMD2_ANY_STRING_V("network.http.cacert.set", [http_stack](auto, auto& str) { return http_stack->set_http_cacert(str); }); CMD_ANY_VALUE_V ("network.http.ssl_verify_host.set", [http_stack](auto, auto& value) { return http_stack->set_ssl_verify_host(value); });
CMD2_ANY ("network.http.capath", [http_stack](auto, auto) { return http_stack->http_capath(); }); CMD_ANY ("network.http.ssl_verify_peer", [http_stack](auto, auto) { return http_stack->ssl_verify_peer(); });
CMD2_ANY_STRING_V("network.http.capath.set", [http_stack](auto, auto& str) { return http_stack->set_http_capath(str); }); CMD_ANY_VALUE_V ("network.http.ssl_verify_peer.set", [http_stack](auto, auto& value) { return http_stack->set_ssl_verify_peer(value); });
CMD2_ANY ("network.http.dns_cache_timeout", [http_stack](auto, auto) { return http_stack->dns_timeout(); });
CMD2_ANY_VALUE_V ("network.http.dns_cache_timeout.set", [http_stack](auto, auto& value) { return http_stack->set_dns_timeout(value); });
CMD2_ANY ("network.http.current_open", [http_stack](auto, auto) { return http_stack->size(); });
CMD2_ANY ("network.http.max_cache_connections", [http_stack](auto, auto) { return http_stack->max_cache_connections(); });
CMD2_ANY_VALUE_V ("network.http.max_cache_connections.set", [http_stack](auto, auto& value) { return http_stack->set_max_cache_connections(value); });
CMD2_ANY ("network.http.max_host_connections", [http_stack](auto, auto) { return http_stack->max_host_connections(); });
CMD2_ANY_VALUE_V ("network.http.max_host_connections.set", [http_stack](auto, auto& value) { return http_stack->set_max_host_connections(value); });
CMD2_ANY ("network.http.max_total_connections", [http_stack](auto, auto) { return http_stack->max_total_connections(); });
CMD2_ANY_VALUE_V ("network.http.max_total_connections.set", [http_stack](auto, auto& value) { return http_stack->set_max_total_connections(value); });
CMD2_ANY ("network.http.proxy_address", [http_stack](auto, auto) { return http_stack->http_proxy(); });
CMD2_ANY_STRING_V("network.http.proxy_address.set", [http_stack](auto, auto& str) { return http_stack->set_http_proxy(str); });
CMD2_ANY ("network.http.ssl_verify_host", [http_stack](auto, auto) { return http_stack->ssl_verify_host(); });
CMD2_ANY_VALUE_V ("network.http.ssl_verify_host.set", [http_stack](auto, auto& value) { return http_stack->set_ssl_verify_host(value); });
CMD2_ANY ("network.http.ssl_verify_peer", [http_stack](auto, auto) { return http_stack->ssl_verify_peer(); });
CMD2_ANY_VALUE_V ("network.http.ssl_verify_peer.set", [http_stack](auto, auto& value) { return http_stack->set_ssl_verify_peer(value); });
CMD2_ANY ("network.send_buffer.size", [nw_config](auto, auto) { return nw_config->send_buffer_size(); }); CMD_ANY ("network.send_buffer.size", [nw_config](auto, auto) { return nw_config->send_buffer_size(); });
CMD2_ANY_VALUE_V ("network.send_buffer.size.set", [nw_config](auto, auto& value) { return nw_config->set_send_buffer_size(value); }); CMD_ANY_VALUE_V ("network.send_buffer.size.set", [nw_config](auto, auto& value) { return nw_config->set_send_buffer_size(value); });
CMD2_ANY ("network.receive_buffer.size", [nw_config](auto, auto) { return nw_config->receive_buffer_size(); }); CMD_ANY ("network.receive_buffer.size", [nw_config](auto, auto) { return nw_config->receive_buffer_size(); });
CMD2_ANY_VALUE_V ("network.receive_buffer.size.set", [nw_config](auto, auto& value) { return nw_config->set_receive_buffer_size(value); }); CMD_ANY_VALUE_V ("network.receive_buffer.size.set", [nw_config](auto, auto& value) { return nw_config->set_receive_buffer_size(value); });
CMD2_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); });
CMD2_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(); });
CMD2_ANY_STRING_V("network.bind_address.set", [nw_config](auto, auto& str) { return nw_config->set_bind_address_str(str); }); CMD_ANY_STRING_V("network.bind_address.set", [nw_config](auto, auto& str) { return nw_config->set_bind_address_str(str); });
CMD2_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(); });
CMD2_ANY_STRING_V("network.bind_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet_address_str(str); }); CMD_ANY_STRING_V("network.bind_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet_address_str(str); });
CMD2_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(); });
CMD2_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_address.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_bind_inet6_address_str(str); });
CMD2_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(); });
CMD2_ANY_STRING_V("network.local_address.set", [nw_config](auto, auto& str) { return nw_config->set_local_address_str(str); }); CMD_ANY_STRING_V("network.local_address.set", [nw_config](auto, auto& str) { return nw_config->set_local_address_str(str); });
CMD2_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(); });
CMD2_ANY_STRING_V("network.local_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet_address_str(str); }); CMD_ANY_STRING_V("network.local_address.ipv4.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet_address_str(str); });
CMD2_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(); });
CMD2_ANY_STRING_V("network.local_address.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet6_address_str(str); }); CMD_ANY_STRING_V("network.local_address.ipv6.set", [nw_config](auto, auto& str) { return nw_config->set_local_inet6_address_str(str); });
CMD2_ANY ("network.proxy_address", [nw_config](auto, auto) { return nw_config->proxy_address_str(); }); CMD_ANY ("network.proxy.global", [](auto, auto) { return torrent::runtime::proxy_manager()->proxy_url(); });
CMD2_ANY_STRING_V("network.proxy_address.set", [](auto, auto& str) { return control->core()->set_proxy_address(str); }); CMD_ANY_STRING_V("network.proxy.global.set", [](auto, auto& str) { return torrent::runtime::proxy_manager()->set_proxy_url(str); });
CMD_ANY ("network.proxy.http", [](auto, auto) { return torrent::runtime::proxy_manager()->http_proxy_url(); });
CMD_ANY_STRING_V("network.proxy.http.set", [](auto, auto& str) { return torrent::runtime::proxy_manager()->set_http_proxy_url(str); });
CMD2_ANY ("network.open_files", [file_manager](auto, auto) { return file_manager->open_files(); }); CMD_ANY ("network.open_files", [file_manager](auto, auto) { return file_manager->open_files(); });
CMD2_ANY ("network.max_open_files", [file_manager](auto, auto) { return file_manager->max_open_files(); }); CMD_ANY ("network.max_open_files", [file_manager](auto, auto) { return file_manager->max_open_files(); });
CMD2_ANY_VALUE_V ("network.max_open_files.set", [file_manager](auto, auto& value) { return file_manager->set_max_open_files(value); }); CMD_ANY ("network.total_handshakes", [](auto, auto) { return torrent::runtime::total_handshakes(); });
CMD2_ANY ("network.total_handshakes", [](auto, auto) { return torrent::total_handshakes(); });
CMD2_ANY ("network.open_sockets", [cm](auto, auto) { return cm->size(); });
CMD2_ANY ("network.max_open_sockets", [cm](auto, auto) { return cm->max_size(); });
CMD2_ANY_VALUE_V ("network.max_open_sockets.set", [cm](auto, auto& value) { return cm->set_max_size(value); });
CMD2_ANY_STRING ("network.scgi.open_port", std::bind(&apply_scgi, std::placeholders::_2, 1)); CMD_ANY_STRING ("network.scgi.open_port", [](auto, auto& arg) { return apply_scgi(arg, 1); });
CMD2_ANY_STRING ("network.scgi.open_local", std::bind(&apply_scgi, std::placeholders::_2, 2)); CMD_ANY_STRING ("network.scgi.open_local", [](auto, auto& arg) { return apply_scgi(arg, 2); });
CMD2_VAR_BOOL ("network.scgi.dont_route", false); CMD_VAR_BOOL ("network.scgi.dont_route", false);
CMD_ANY ("network.scgi.open_systemd", [](auto, auto) { return apply_scgi_systemd(); });
CMD2_ANY_STRING ("network.xmlrpc.dialect.set", [](const auto&, const auto& arg) { return apply_xmlrpc_dialect(arg); }) CMD_ANY ("network.scgi.use_gzip", [](auto, auto) { return rpc::rpc.scgi_allow_compression(); });
CMD2_ANY ("network.xmlrpc.size_limit", [](const auto&, const auto&) { return rpc::rpc.size_limit(); }); CMD_ANY_VALUE_V ("network.scgi.use_gzip.set", [](auto, auto& arg) { return rpc::rpc.set_scgi_allow_compression(arg); });
CMD2_ANY_VALUE_V ("network.xmlrpc.size_limit.set", [](const auto&, const auto& arg) { return rpc::rpc.set_size_limit(arg); }); CMD_ANY ("network.scgi.gzip.min_size", [](auto, auto) { return rpc::rpc.scgi_min_compress_size(); });
CMD_ANY_VALUE_V ("network.scgi.gzip.min_size.set", [](auto, auto& arg) { return rpc::rpc.set_scgi_min_compress_size(arg); });
CMD2_VAR_BOOL ("network.rpc.use_xmlrpc", true); CMD_ANY_STRING ("network.xmlrpc.dialect.set", [](auto, auto& arg) { return apply_xmlrpc_dialect(arg); })
CMD2_VAR_BOOL ("network.rpc.use_jsonrpc", true); 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); });
CMD2_ANY ("network.block.ipv4", [nw_config](auto, auto) { return nw_config->is_block_ipv4(); }); CMD_ANY ("network.rpc.use_xmlrpc", [](auto, auto) { return rpc::rpc.use_xmlrpc(); });
CMD2_ANY_VALUE_V ("network.block.ipv4.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv4(value); }); CMD_ANY_VALUE_V ("network.rpc.use_xmlrpc.set", [](auto, auto& arg) { return rpc::rpc.set_use_xmlrpc(arg); });
CMD2_ANY ("network.block.ipv6", [nw_config](auto, auto) { return nw_config->is_block_ipv6(); }); CMD_ANY ("network.rpc.use_jsonrpc", [](auto, auto) { return rpc::rpc.use_jsonrpc(); });
CMD2_ANY_VALUE_V ("network.block.ipv6.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv6(value); }); CMD_ANY_VALUE_V ("network.rpc.use_jsonrpc.set", [](auto, auto& arg) { return rpc::rpc.set_use_jsonrpc(arg); });
CMD2_ANY ("network.block.ipv4in6", [nw_config](auto, auto) { return nw_config->is_block_ipv4in6(); });
CMD2_ANY_VALUE_V ("network.block.ipv4in6.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv4in6(value); }); CMD_ANY ("network.block.ipv4", [nw_config](auto, auto) { return nw_config->is_block_ipv4(); });
CMD2_ANY ("network.block.outgoing", [nw_config](auto, auto) { return nw_config->is_block_outgoing(); }); CMD_ANY_VALUE_V ("network.block.ipv4.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv4(value); });
CMD2_ANY_VALUE_V ("network.block.outgoing.set", [nw_config](auto, auto& value) { return nw_config->set_block_outgoing(value); }); CMD_ANY ("network.block.ipv6", [nw_config](auto, auto) { return nw_config->is_block_ipv6(); });
CMD2_ANY ("network.prefer.ipv6", [nw_config](auto, auto) { return nw_config->is_prefer_ipv6(); }); CMD_ANY_VALUE_V ("network.block.ipv6.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv6(value); });
CMD2_ANY_VALUE_V ("network.prefer.ipv6.set", [nw_config](auto, auto& value) { return nw_config->set_prefer_ipv6(value); }); CMD_ANY ("network.block.ipv4in6", [nw_config](auto, auto) { return nw_config->is_block_ipv4in6(); });
CMD_ANY_VALUE_V ("network.block.ipv4in6.set", [nw_config](auto, auto& value) { return nw_config->set_block_ipv4in6(value); });
CMD_ANY ("network.block.outgoing", [nw_config](auto, auto) { return nw_config->is_block_outgoing(); });
CMD_ANY_VALUE_V ("network.block.outgoing.set", [nw_config](auto, auto& value) { return nw_config->set_block_outgoing(value); });
CMD_ANY ("network.prefer.ipv6", [nw_config](auto, auto) { return nw_config->is_prefer_ipv6(); });
CMD_ANY_VALUE_V ("network.prefer.ipv6.set", [nw_config](auto, auto& value) { return nw_config->set_prefer_ipv6(value); });
rpc::rpc.mark_safe("network.port_open");
rpc::rpc.mark_safe("network.port_random");
rpc::rpc.mark_safe("network.port_range");
rpc::rpc.mark_safe("network.listen.port");
rpc::rpc.mark_safe("network.listen.backlog");
rpc::rpc.mark_safe("network.http.current_open");
rpc::rpc.mark_safe("network.http.max_cache_connections");
rpc::rpc.mark_safe("network.http.max_host_connections");
rpc::rpc.mark_safe("network.http.max_total_connections");
rpc::rpc.mark_safe("network.total_handshakes");
rpc::rpc.mark_safe("network.open_files");
rpc::rpc.mark_safe("network.max_open_files");
rpc::rpc.mark_safe("network.send_buffer.size");
rpc::rpc.mark_safe("network.receive_buffer.size");
rpc::rpc.mark_safe("network.bind_address");
rpc::rpc.mark_safe("network.local_address");
rpc::rpc.mark_safe("network.xmlrpc.size_limit");
rpc::rpc.mark_safe("network.open_sockets");
rpc::rpc.mark_safe("network.http.cacert");
rpc::rpc.mark_safe("network.http.capath");
rpc::rpc.mark_safe("network.proxy.global");
rpc::rpc.mark_safe("network.proxy.http");
rpc::rpc.mark_safe("network.scgi.dont_route");
rpc::rpc.mark_safe("protocol.pex");
rpc::rpc.mark_safe("network.rpc.use_xmlrpc");
rpc::rpc.mark_safe("network.rpc.use_jsonrpc");
} }
+36 -28
View File
@@ -1,33 +1,18 @@
#include "config.h" #include "config.h"
#include <rak/string_manip.h>
#include <torrent/bitfield.h> #include <torrent/bitfield.h>
#include <torrent/rate.h> #include <torrent/rate.h>
#include <torrent/net/socket_address.h> #include <torrent/net/socket_address.h>
#include <torrent/peer/connection_list.h> #include <torrent/peer/connection_list.h>
#include <torrent/peer/peer.h> #include <torrent/peer/peer.h>
#include <torrent/peer/peer_info.h> #include <torrent/peer/peer_info.h>
#include <torrent/utils/string_manip.h>
#include "core/manager.h"
#include "display/utils.h"
#include "globals.h" #include "globals.h"
#include "control.h"
#include "command_helpers.h" #include "command_helpers.h"
#include "control.h"
torrent::Object #include "core/manager.h"
retrieve_p_id(torrent::Peer* peer) { #include "display/utils.h"
const torrent::HashString* hashString = &peer->id();
return rak::transform_hex(hashString->begin(), hashString->end());
}
torrent::Object
retrieve_p_id_html(torrent::Peer* peer) {
const torrent::HashString* hashString = &peer->id();
return rak::copy_escape_html(hashString->begin(), hashString->end());
}
torrent::Object torrent::Object
retrieve_p_address(torrent::Peer* peer) { retrieve_p_address(torrent::Peer* peer) {
@@ -40,11 +25,6 @@ retrieve_p_address(torrent::Peer* peer) {
return addr_str; return addr_str;
} }
torrent::Object
retrieve_p_port(torrent::Peer* peer) {
return torrent::sa_port(peer->peer_info()->socket_address());
}
torrent::Object torrent::Object
retrieve_p_client_version(torrent::Peer* peer) { retrieve_p_client_version(torrent::Peer* peer) {
char buf[128]; char buf[128];
@@ -55,18 +35,20 @@ retrieve_p_client_version(torrent::Peer* peer) {
torrent::Object torrent::Object
retrieve_p_options_str(torrent::Peer* peer) { retrieve_p_options_str(torrent::Peer* peer) {
return rak::transform_hex(peer->peer_info()->options(), peer->peer_info()->options() + 8); return torrent::utils::transform_to_hex_str(peer->peer_info()->options(), peer->peer_info()->options() + 8);
} }
torrent::Object torrent::Object
retrieve_p_completed_percent(torrent::Peer* peer) { retrieve_p_completed_percent(torrent::Peer* peer) {
if (peer->bitfield()->size_bits() == 0)
return int64_t(0);
return (100 * peer->bitfield()->size_set()) / peer->bitfield()->size_bits(); return (100 * peer->bitfield()->size_set()) / peer->bitfield()->size_bits();
} }
void void
initialize_command_peer() { initialize_command_peer() {
CMD2_PEER("p.id", std::bind(&retrieve_p_id, std::placeholders::_1)); CMD2_PEER("p.id", [](auto* peer, auto) { return torrent::utils::transform_to_hex_str(peer->id()); });
CMD2_PEER("p.id_html", std::bind(&retrieve_p_id_html, std::placeholders::_1)); CMD2_PEER("p.id_html", [](auto* peer, auto) { return torrent::utils::copy_escape_html_str(peer->id()); });
CMD2_PEER("p.client_version", std::bind(&retrieve_p_client_version, std::placeholders::_1)); CMD2_PEER("p.client_version", std::bind(&retrieve_p_client_version, std::placeholders::_1));
CMD2_PEER("p.options_str", std::bind(&retrieve_p_options_str, std::placeholders::_1)); CMD2_PEER("p.options_str", std::bind(&retrieve_p_options_str, std::placeholders::_1));
@@ -80,7 +62,7 @@ initialize_command_peer() {
CMD2_PEER("p.is_preferred", std::bind(&torrent::PeerInfo::is_preferred, std::bind(&torrent::Peer::peer_info, std::placeholders::_1))); CMD2_PEER("p.is_preferred", std::bind(&torrent::PeerInfo::is_preferred, std::bind(&torrent::Peer::peer_info, std::placeholders::_1)));
CMD2_PEER("p.address", std::bind(&retrieve_p_address, std::placeholders::_1)); CMD2_PEER("p.address", std::bind(&retrieve_p_address, std::placeholders::_1));
CMD2_PEER("p.port", std::bind(&retrieve_p_port, std::placeholders::_1)); CMD2_PEER("p.port", [](auto* peer, auto) { return torrent::sa_port(peer->peer_info()->socket_address()); });
CMD2_PEER("p.completed_percent", std::bind(&retrieve_p_completed_percent, std::placeholders::_1)); CMD2_PEER("p.completed_percent", std::bind(&retrieve_p_completed_percent, std::placeholders::_1));
@@ -98,4 +80,30 @@ initialize_command_peer() {
CMD2_PEER_V("p.disconnect", std::bind(&torrent::Peer::disconnect, std::placeholders::_1, 0)); CMD2_PEER_V("p.disconnect", std::bind(&torrent::Peer::disconnect, std::placeholders::_1, 0));
CMD2_PEER_V("p.disconnect_delayed", std::bind(&torrent::Peer::disconnect, std::placeholders::_1, torrent::ConnectionList::disconnect_delayed)); CMD2_PEER_V("p.disconnect_delayed", std::bind(&torrent::Peer::disconnect, std::placeholders::_1, torrent::ConnectionList::disconnect_delayed));
rpc::rpc.mark_safe("p.address");
rpc::rpc.mark_safe("p.port");
rpc::rpc.mark_safe("p.client_version");
rpc::rpc.mark_safe("p.options_str");
rpc::rpc.mark_safe("p.id");
rpc::rpc.mark_safe("p.id_html");
rpc::rpc.mark_safe("p.up_rate");
rpc::rpc.mark_safe("p.up_total");
rpc::rpc.mark_safe("p.down_rate");
rpc::rpc.mark_safe("p.down_total");
rpc::rpc.mark_safe("p.peer_rate");
rpc::rpc.mark_safe("p.peer_total");
rpc::rpc.mark_safe("p.is_encrypted");
rpc::rpc.mark_safe("p.is_incoming");
rpc::rpc.mark_safe("p.is_obfuscated");
rpc::rpc.mark_safe("p.is_snubbed");
rpc::rpc.mark_safe("p.is_unwanted");
rpc::rpc.mark_safe("p.is_preferred");
rpc::rpc.mark_safe("p.snubbed");
rpc::rpc.mark_safe("p.snubbed.set");
rpc::rpc.mark_safe("p.banned");
rpc::rpc.mark_safe("p.banned.set");
rpc::rpc.mark_safe("p.completed_percent");
rpc::rpc.mark_safe("p.disconnect");
rpc::rpc.mark_safe("p.disconnect_delayed");
} }
+359
View File
@@ -0,0 +1,359 @@
#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 {
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;
auto padding_offsets = utf8_offsets(padding);
auto padding_length = static_cast<int64_t>(padding_offsets.size() - 1);
std::string result;
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);
}
+90 -42
View File
@@ -8,7 +8,6 @@
#include "core/manager.h" #include "core/manager.h"
#include "ui/root.h" #include "ui/root.h"
#include "rak/address_info.h"
#include "rpc/parse.h" #include "rpc/parse.h"
#include "rpc/parse_commands.h" #include "rpc/parse_commands.h"
@@ -21,21 +20,26 @@ parse_address_range(const torrent::Object::list_type& args, torrent::Object::lis
unsigned int prefixWidth, ret; unsigned int prefixWidth, ret;
char dummy; char dummy;
char host[1024]; char host[1024];
rak::address_info* ai; torrent::sa_unique_ptr sa;
ret = std::sscanf(itr->as_string().c_str(), "%1023[^/]/%d%c", host, &prefixWidth, &dummy); ret = std::sscanf(itr->as_string().c_str(), "%1023[^/]/%d%c", host, &prefixWidth, &dummy);
if (ret < 1 || rak::address_info::get_address_info(host, PF_INET, SOCK_STREAM, &ai) != 0) if (ret < 1)
throw torrent::input_error("Could not resolve host."); 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; uint32_t begin, end;
auto sa = torrent::sa_copy(ai->c_addrinfo()->ai_addr);
auto sa_addr = htonl(reinterpret_cast<sockaddr_in*>(sa.get())->sin_addr.s_addr); auto sa_addr = htonl(reinterpret_cast<sockaddr_in*>(sa.get())->sin_addr.s_addr);
begin = end = sa_addr; begin = end = sa_addr;
rak::address_info::free_address_info(ai);
if (ret == 2) { if (ret == 2) {
if (++itr != args.end()) if (++itr != args.end())
throw torrent::input_error("Cannot specify both network and range end."); throw torrent::input_error("Cannot specify both network and range end.");
@@ -48,13 +52,14 @@ parse_address_range(const torrent::Object::list_type& args, torrent::Object::lis
end = sa_addr | ~netmask; end = sa_addr | ~netmask;
} else if (++itr != args.end()) { } else if (++itr != args.end()) {
if (rak::address_info::get_address_info(itr->as_string().c_str(), PF_INET, SOCK_STREAM, &ai) != 0) try {
throw torrent::input_error("Could not resolve host."); sa = torrent::sa_copy(torrent::sa_lookup_address(itr->as_string(), AF_INET).get());
} catch (torrent::input_error& e) {
throw torrent::input_error("Could not resolve host: " + std::string(e.what()));
}
sa = torrent::sa_copy(ai->c_addrinfo()->ai_addr);
sa_addr = htonl(reinterpret_cast<sockaddr_in*>(sa.get())->sin_addr.s_addr); sa_addr = htonl(reinterpret_cast<sockaddr_in*>(sa.get())->sin_addr.s_addr);
rak::address_info::free_address_info(ai);
end = sa_addr; end = sa_addr;
} }
@@ -65,33 +70,35 @@ parse_address_range(const torrent::Object::list_type& args, torrent::Object::lis
torrent::Object torrent::Object
apply_throttle(const torrent::Object::list_type& args, bool up) { apply_throttle(const torrent::Object::list_type& args, bool up) {
torrent::Object::list_const_iterator argItr = args.begin(); auto arg_itr = args.begin();
if (argItr == args.end()) if (arg_itr == args.end())
throw torrent::input_error("Missing throttle name."); throw torrent::input_error("Missing throttle name.");
const std::string& name = argItr->as_string(); const std::string& name = arg_itr->as_string();
if (name.empty() || name == "NULL") if (name.empty() || name == "NULL")
throw torrent::input_error("Invalid throttle name '" + name + "'."); throw torrent::input_error("Invalid throttle name '" + name + "'.");
if (++argItr == args.end() || argItr->as_string().empty()) if (++arg_itr == args.end() || arg_itr->as_string().empty())
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(argItr->as_string().c_str(), &rate); rpc::parse_whole_value_nothrow(arg_itr->as_string().c_str(), &rate);
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.");
core::ThrottleMap::iterator itr = control->core()->throttles().find(name); auto itr = control->core()->throttles().find(name);
if (itr == control->core()->throttles().end())
itr = control->core()->throttles().insert(std::make_pair(name, torrent::ThrottlePair(NULL, NULL))).first;
torrent::Throttle*& throttle = up ? itr->second.first : itr->second.second; if (itr == control->core()->throttles().end())
if (rate != 0 && throttle == NULL) itr = control->core()->throttles().insert(std::make_pair(name, core::ThrottlePair(nullptr, nullptr))).first;
auto*& throttle = up ? itr->second.first : itr->second.second;
if (rate != 0 && throttle == nullptr)
throttle = (up ? torrent::up_throttle_global() : torrent::down_throttle_global())->create_slave(); throttle = (up ? torrent::up_throttle_global() : torrent::down_throttle_global())->create_slave();
if (throttle != NULL) if (throttle != nullptr)
throttle->set_max_rate(rate * 1024); throttle->set_max_rate(rate * 1024);
return torrent::Object(); return torrent::Object();
@@ -104,10 +111,10 @@ static const int throttle_info_rate = (1 << 3);
torrent::Object torrent::Object
retrieve_throttle_info(const torrent::Object::string_type& name, int flags) { retrieve_throttle_info(const torrent::Object::string_type& name, int flags) {
core::ThrottleMap::iterator itr = control->core()->throttles().find(name); auto itr = control->core()->throttles().find(name);
torrent::ThrottlePair throttles = itr == control->core()->throttles().end() ? torrent::ThrottlePair(NULL, NULL) : itr->second; auto throttles = (itr == control->core()->throttles().end()) ? core::ThrottlePair(nullptr, nullptr) : itr->second;
torrent::Throttle* throttle = flags & throttle_info_down ? throttles.second : throttles.first; auto* throttle = flags & throttle_info_down ? throttles.second : throttles.first;
torrent::Throttle* global = flags & throttle_info_down ? torrent::down_throttle_global() : torrent::up_throttle_global(); auto* global = flags & throttle_info_down ? torrent::down_throttle_global() : torrent::up_throttle_global();
if (throttle == NULL && name.empty()) if (throttle == NULL && name.empty())
throttle = global; throttle = global;
@@ -122,20 +129,6 @@ retrieve_throttle_info(const torrent::Object::string_type& name, int flags) {
return (int64_t)throttle->max_rate(); return (int64_t)throttle->max_rate();
} }
torrent::Object
apply_address_throttle(const torrent::Object::list_type& args) {
if (args.size() < 2 || args.size() > 3)
throw torrent::input_error("Incorrect number of arguments.");
std::pair<uint32_t, uint32_t> range = parse_address_range(args, ++args.begin());
core::ThrottleMap::iterator throttleItr = control->core()->throttles().find(args.begin()->as_string().c_str());
if (throttleItr == control->core()->throttles().end())
throw torrent::input_error("Throttle not found.");
control->core()->set_address_throttle(range.first, range.second, throttleItr->second);
return torrent::Object();
}
torrent::Object torrent::Object
throttle_update(const char* variable, int64_t value) { throttle_update(const char* variable, int64_t value) {
rpc::commands.call_command(variable, value); rpc::commands.call_command(variable, value);
@@ -193,10 +186,65 @@ initialize_command_throttle() {
// than kB. // than kB.
CMD2_ANY_LIST ("throttle.up", std::bind(&apply_throttle, std::placeholders::_2, true)); CMD2_ANY_LIST ("throttle.up", std::bind(&apply_throttle, std::placeholders::_2, true));
CMD2_ANY_LIST ("throttle.down", std::bind(&apply_throttle, std::placeholders::_2, false)); CMD2_ANY_LIST ("throttle.down", std::bind(&apply_throttle, std::placeholders::_2, false));
CMD2_ANY_LIST ("throttle.ip", std::bind(&apply_address_throttle, std::placeholders::_2));
CMD2_ANY_STRING ("throttle.up.max", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_up | throttle_info_max)); CMD2_ANY_STRING ("throttle.up.max", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_up | throttle_info_max));
CMD2_ANY_STRING ("throttle.up.rate", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_up | throttle_info_rate)); CMD2_ANY_STRING ("throttle.up.rate", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_up | throttle_info_rate));
CMD2_ANY_STRING ("throttle.down.max", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_down | throttle_info_max)); CMD2_ANY_STRING ("throttle.down.max", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_down | throttle_info_max));
CMD2_ANY_STRING ("throttle.down.rate", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_down | throttle_info_rate)); CMD2_ANY_STRING ("throttle.down.rate", std::bind(&retrieve_throttle_info, std::placeholders::_2, throttle_info_down | throttle_info_rate));
rpc::rpc.mark_safe("throttle.unchoked_uploads");
rpc::rpc.mark_safe("throttle.max_unchoked_uploads");
rpc::rpc.mark_safe("throttle.unchoked_downloads");
rpc::rpc.mark_safe("throttle.max_unchoked_downloads");
rpc::rpc.mark_safe("throttle.min_peers.normal");
rpc::rpc.mark_safe("throttle.min_peers.normal.set");
rpc::rpc.mark_safe("throttle.max_peers.normal");
rpc::rpc.mark_safe("throttle.max_peers.normal.set");
rpc::rpc.mark_safe("throttle.min_peers.seed");
rpc::rpc.mark_safe("throttle.min_peers.seed.set");
rpc::rpc.mark_safe("throttle.max_peers.seed");
rpc::rpc.mark_safe("throttle.max_peers.seed.set");
rpc::rpc.mark_safe("throttle.min_uploads");
rpc::rpc.mark_safe("throttle.min_uploads.set");
rpc::rpc.mark_safe("throttle.max_uploads");
rpc::rpc.mark_safe("throttle.max_uploads.set");
rpc::rpc.mark_safe("throttle.min_downloads");
rpc::rpc.mark_safe("throttle.min_downloads.set");
rpc::rpc.mark_safe("throttle.max_downloads");
rpc::rpc.mark_safe("throttle.max_downloads.set");
rpc::rpc.mark_safe("throttle.max_uploads.div");
rpc::rpc.mark_safe("throttle.max_uploads.div.set");
rpc::rpc.mark_safe("throttle.max_uploads.div._val");
rpc::rpc.mark_safe("throttle.max_uploads.div._val.set");
rpc::rpc.mark_safe("throttle.max_uploads.global");
rpc::rpc.mark_safe("throttle.max_uploads.global.set");
rpc::rpc.mark_safe("throttle.max_uploads.global._val");
rpc::rpc.mark_safe("throttle.max_uploads.global._val.set");
rpc::rpc.mark_safe("throttle.max_downloads.div");
rpc::rpc.mark_safe("throttle.max_downloads.div.set");
rpc::rpc.mark_safe("throttle.max_downloads.div._val");
rpc::rpc.mark_safe("throttle.max_downloads.div._val.set");
rpc::rpc.mark_safe("throttle.max_downloads.global");
rpc::rpc.mark_safe("throttle.max_downloads.global.set");
rpc::rpc.mark_safe("throttle.max_downloads.global._val");
rpc::rpc.mark_safe("throttle.max_downloads.global._val.set");
rpc::rpc.mark_safe("throttle.global_up.rate");
rpc::rpc.mark_safe("throttle.global_up.total");
rpc::rpc.mark_safe("throttle.global_up.max_rate");
rpc::rpc.mark_safe("throttle.global_up.max_rate.set");
rpc::rpc.mark_safe("throttle.global_up.max_rate.set_kb");
rpc::rpc.mark_safe("throttle.global_down.rate");
rpc::rpc.mark_safe("throttle.global_down.total");
rpc::rpc.mark_safe("throttle.global_down.max_rate");
rpc::rpc.mark_safe("throttle.global_down.max_rate.set");
rpc::rpc.mark_safe("throttle.global_down.max_rate.set_kb");
rpc::rpc.mark_safe("throttle.up.max");
rpc::rpc.mark_safe("throttle.up.rate");
rpc::rpc.mark_safe("throttle.down.max");
rpc::rpc.mark_safe("throttle.down.rate");
} }
+84 -62
View File
@@ -3,8 +3,10 @@
#include <cassert> #include <cassert>
#include <cstdio> #include <cstdio>
#include <netdb.h> #include <netdb.h>
#include <torrent/net/network_config.h>
#include <torrent/net/resolver.h> #include <torrent/net/resolver.h>
#include <torrent/runtime/network_config.h>
#include <torrent/runtime/network_manager.h>
#include <torrent/system/callbacks.h>
#include <torrent/tracker/dht_controller.h> #include <torrent/tracker/dht_controller.h>
#include <torrent/tracker/tracker.h> #include <torrent/tracker/tracker.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
@@ -27,9 +29,6 @@ tracker_set_enabled(torrent::tracker::Tracker* tracker, bool state) {
torrent::Object torrent::Object
apply_dht_add_node(const std::string& arg) { apply_dht_add_node(const std::string& arg) {
if (!torrent::dht_controller()->is_valid())
throw torrent::input_error("DHT not enabled.");
int port; int port;
char dummy; char dummy;
char host[1024]; char host[1024];
@@ -44,21 +43,23 @@ apply_dht_add_node(const std::string& arg) {
if (port < 1 || port > 65535) if (port < 1 || port > 65535)
throw torrent::input_error("Invalid port number."); throw torrent::input_error("Invalid port number.");
assert(std::this_thread::get_id() == torrent::main_thread::thread()->thread_id()); assert(std::this_thread::get_id() == torrent::main_thread::thread_id());
auto host_str = std::string(host); auto host_str = std::string(host);
// TODO: Move this lookup to DhtController. // TODO: Move this lookup to DhtController.
auto callback_id = torrent::system::make_callback_id();
// Currently discarding SOCK_STREAM. // Currently discarding SOCK_STREAM.
torrent::this_thread::resolver()->resolve_specific(nullptr, host_str, PF_INET, [host_str, port](torrent::c_sa_shared_ptr sa, int err) { torrent::this_thread::resolver()->resolve_specific(callback_id, host_str, PF_INET, [host_str, port](torrent::c_sa_shared_ptr sa, int err) {
if (sa == nullptr) { if (sa == nullptr) {
lt_log_print(torrent::LOG_DHT_ERROR, "dht.add_node : could not resolve host : %s (%s)", gai_strerror(err), host_str.c_str()); lt_log_print(torrent::LOG_DHT_ERROR, "dht.add_node : could not resolve host : %s (%s)", gai_strerror(err), host_str.c_str());
return; return;
} }
lt_log_print(torrent::LOG_DHT_CONTROLLER, "dht.add_node : %s", host_str.c_str()); lt_log_print(torrent::LOG_DHT_CONTROLLER, "dht.add_node : %s", host_str.c_str());
torrent::dht_controller()->add_node(sa.get(), port); torrent::runtime::network_manager()->dht_add_bootstrap_node(host_str.c_str(), port);
}); });
return torrent::Object(); return torrent::Object();
@@ -70,16 +71,6 @@ apply_enable_trackers(int64_t arg) {
for (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 if (rpc::call_command_value("trackers.use_udp") == 0) {
for (auto download : *control->core()->download_list()) {
download->tracker_controller().for_each([](auto& tracker) {
if (tracker.type() == torrent::TRACKER_UDP)
tracker.disable();
else
tracker.enable();
});
}
} else { } else {
for (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(); });
@@ -90,69 +81,100 @@ apply_enable_trackers(int64_t arg) {
void void
initialize_command_tracker() { initialize_command_tracker() {
CMD2_TRACKER ("t.is_busy", std::bind(&torrent::tracker::Tracker::is_busy, std::placeholders::_1)); CMD2_TRACKER ("t.is_busy", [](auto* tracker, auto) { return tracker->is_requesting(); });
CMD2_TRACKER ("t.is_enabled", std::bind(&torrent::tracker::Tracker::is_enabled, std::placeholders::_1)); CMD2_TRACKER ("t.is_enabled", [](auto* tracker, auto) { return tracker->is_enabled(); });
CMD2_TRACKER ("t.is_extra_tracker", std::bind(&torrent::tracker::Tracker::is_extra_tracker, std::placeholders::_1)); CMD2_TRACKER ("t.is_extra_tracker", [](auto* tracker, auto) { return tracker->is_extra_tracker(); });
CMD2_TRACKER ("t.is_open", std::bind(&torrent::tracker::Tracker::is_busy, std::placeholders::_1)); CMD2_TRACKER ("t.is_open", [](auto* tracker, auto) { return tracker->is_requesting(); });
CMD2_TRACKER ("t.is_scrapable", std::bind(&torrent::tracker::Tracker::is_scrapable, std::placeholders::_1)); CMD2_TRACKER ("t.is_scrapable", [](auto* tracker, auto) { return tracker->is_scrapable(); });
CMD2_TRACKER ("t.is_usable", std::bind(&torrent::tracker::Tracker::is_usable, std::placeholders::_1)); CMD2_TRACKER ("t.is_usable", [](auto* tracker, auto) { return tracker->is_usable(); });
// TODO: Deprecate. // TODO: Deprecate.
CMD2_TRACKER ("t.can_scrape", std::bind(&torrent::tracker::Tracker::is_scrapable, std::placeholders::_1)); CMD2_TRACKER ("t.can_scrape", [](auto* tracker, auto) { return tracker->is_scrapable(); });
CMD2_TRACKER_V ("t.enable", std::bind(&torrent::tracker::Tracker::enable, std::placeholders::_1)); CMD2_TRACKER_V ("t.enable", [](auto* tracker, auto) { tracker->enable(); });
CMD2_TRACKER_V ("t.disable", std::bind(&torrent::tracker::Tracker::disable, std::placeholders::_1)); CMD2_TRACKER_V ("t.disable", [](auto* tracker, auto) { tracker->disable(); });
CMD2_TRACKER_VALUE_V("t.is_enabled.set", std::bind(&tracker_set_enabled, std::placeholders::_1, std::placeholders::_2)); CMD2_TRACKER_VALUE_V("t.is_enabled.set", [](auto* tracker, auto& value) { return tracker_set_enabled(tracker, value); });
CMD2_TRACKER ("t.url", std::bind(&torrent::tracker::Tracker::url, std::placeholders::_1)); CMD2_TRACKER ("t.url", [](auto* tracker, auto) { return tracker->url(); });
CMD2_TRACKER ("t.group", std::bind(&torrent::tracker::Tracker::group, std::placeholders::_1)); CMD2_TRACKER ("t.group", [](auto* tracker, auto) { return tracker->group(); });
CMD2_TRACKER ("t.type", std::bind(&torrent::tracker::Tracker::type, std::placeholders::_1)); CMD2_TRACKER ("t.type", [](auto* tracker, auto) { return tracker->type(); });
CMD2_TRACKER ("t.id", std::bind(&torrent::tracker::Tracker::tracker_id, std::placeholders::_1)); CMD2_TRACKER ("t.id", [](auto* tracker, auto) { return tracker->tracker_id(); });
CMD2_TRACKER ("t.latest_event", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().latest_event(); }); CMD2_TRACKER ("t.latest_event", [](auto* tracker, auto) { return tracker->state().latest_event(); });
CMD2_TRACKER ("t.latest_new_peers", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().latest_new_peers(); }); CMD2_TRACKER ("t.latest_new_peers", [](auto* tracker, auto) { return tracker->state().latest_new_peers(); });
CMD2_TRACKER ("t.latest_sum_peers", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().latest_sum_peers(); }); CMD2_TRACKER ("t.latest_sum_peers", [](auto* tracker, auto) { return tracker->state().latest_sum_peers(); });
CMD2_TRACKER ("t.normal_interval", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().normal_interval(); }); CMD2_TRACKER ("t.normal_interval", [](auto* tracker, auto) { return tracker->state().normal_interval().count(); });
CMD2_TRACKER ("t.min_interval", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().min_interval(); }); CMD2_TRACKER ("t.min_interval", [](auto* tracker, auto) { return tracker->state().min_interval().count(); });
CMD2_TRACKER ("t.activity_time_next", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().activity_time_next(); }); CMD2_TRACKER ("t.activity_time_next", [](auto* tracker, auto) { return tracker->state().activity_time_next().count(); });
CMD2_TRACKER ("t.activity_time_last", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().activity_time_last(); }); CMD2_TRACKER ("t.activity_time_last", [](auto* tracker, auto) { return tracker->state().activity_time_last().count(); });
CMD2_TRACKER ("t.success_time_next", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().success_time_next(); }); CMD2_TRACKER ("t.success_time_next", [](auto* tracker, auto) { return tracker->state().success_time_next().count(); });
CMD2_TRACKER ("t.success_time_last", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().success_time_last(); }); CMD2_TRACKER ("t.success_time_last", [](auto* tracker, auto) { return tracker->state().success_time_last().count(); });
CMD2_TRACKER ("t.success_counter", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().success_counter(); }); CMD2_TRACKER ("t.success_counter", [](auto* tracker, auto) { return tracker->state().success_counter(); });
CMD2_TRACKER ("t.failed_time_next", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().failed_time_next(); }); CMD2_TRACKER ("t.failed_time_next", [](auto* tracker, auto) { return tracker->state().failed_time_next().count(); });
CMD2_TRACKER ("t.failed_time_last", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().failed_time_last(); }); CMD2_TRACKER ("t.failed_time_last", [](auto* tracker, auto) { return tracker->state().failed_time_last().count(); });
CMD2_TRACKER ("t.failed_counter", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().failed_counter(); }); CMD2_TRACKER ("t.failed_counter", [](auto* tracker, auto) { return tracker->state().failed_counter(); });
CMD2_TRACKER ("t.scrape_time_last", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_time_last(); }); CMD2_TRACKER ("t.scrape_time_last", [](auto* tracker, auto) { return tracker->state().scrape_time_last().count(); });
CMD2_TRACKER ("t.scrape_counter", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_counter(); }); CMD2_TRACKER ("t.scrape_counter", [](auto* tracker, auto) { return tracker->state().scrape_counter(); });
CMD2_TRACKER ("t.scrape_complete", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_complete(); }); CMD2_TRACKER ("t.scrape_complete", [](auto* tracker, auto) { return tracker->state().scrape_complete(); });
CMD2_TRACKER ("t.scrape_incomplete", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_incomplete(); }); CMD2_TRACKER ("t.scrape_incomplete", [](auto* tracker, auto) { return tracker->state().scrape_incomplete(); });
CMD2_TRACKER ("t.scrape_downloaded", [](torrent::tracker::Tracker* tracker, [[maybe_unused]] auto o) -> auto { return tracker->state().scrape_downloaded(); }); CMD2_TRACKER ("t.scrape_downloaded", [](auto* tracker, auto) { return tracker->state().scrape_downloaded(); });
CMD2_ANY_VALUE ("trackers.enable", std::bind(&apply_enable_trackers, int64_t(1))); CMD2_ANY_VALUE ("trackers.enable", [](auto, auto) { return apply_enable_trackers(1); });
CMD2_ANY_VALUE ("trackers.disable", std::bind(&apply_enable_trackers, int64_t(0))); CMD2_ANY_VALUE ("trackers.disable", [](auto, auto) { return apply_enable_trackers(0); });
CMD2_VAR_BOOL ("trackers.delay_scrape", false); CMD2_VAR_BOOL ("trackers.delay_scrape", false);
CMD2_VAR_VALUE ("trackers.numwant", -1); CMD2_VAR_VALUE ("trackers.numwant", -1);
CMD2_VAR_BOOL ("trackers.use_udp", true);
auto dht_manager = control->dht_manager(); CMD2_ANY ("trackers.use_udp", [](auto, auto) { return true; });
CMD2_ANY_VALUE_V ("trackers.use_udp.set", [](auto, auto) {
lt_log_print(torrent::LOG_ERROR, "trackers.use_udp.set is no longer supported", 0);
})
CMD2_ANY_STRING_V ("dht.mode.set", std::bind(&core::DhtManager::set_mode, control->dht_manager(), std::placeholders::_2)); CMD2_ANY_STRING_V ("dht.mode.set", [](auto, auto& str) { return control->dht_manager()->set_mode_by_user(str); });
CMD2_ANY ("dht.port", std::bind(&torrent::tracker::DhtController::port, torrent::dht_controller())); CMD2_ANY ("dht.port", [](auto, auto) { return torrent::runtime::network_manager()->dht_controller()->port(); });
CMD2_ANY_VALUE_V ("dht.port.set", [](auto, auto) { CMD2_ANY_VALUE_V ("dht.port.set", [](auto, auto) {
lt_log_print(torrent::LOG_DHT_ERROR, "dht.port.set is no longer supported, use dht.override_port.set", 0); lt_log_print(torrent::LOG_DHT_ERROR, "dht.port.set is no longer supported, use dht.override_port.set", 0);
}); });
CMD2_ANY ("dht.override_port", std::bind(&torrent::net::NetworkConfig::override_dht_port, torrent::config::network_config())); CMD2_ANY ("dht.override_port", [](auto, auto) { return torrent::runtime::network_config()->override_dht_port(); });
CMD2_ANY_VALUE_V ("dht.override_port.set", std::bind(&torrent::net::NetworkConfig::set_override_dht_port, torrent::config::network_config(), std::placeholders::_2)); CMD2_ANY_VALUE_V ("dht.override_port.set", [](auto, auto& value) { return torrent::runtime::network_manager()->set_dht_port(value); });
CMD2_ANY_STRING ("dht.add_node", std::bind(&apply_dht_add_node, std::placeholders::_2)); CMD2_ANY_STRING ("dht.add_node", [](auto, auto& str) { return apply_dht_add_node(str); });
CMD2_ANY ("dht.statistics", std::bind(&core::DhtManager::dht_statistics, dht_manager)); CMD2_ANY ("dht.statistics", [](auto, auto) { return control->dht_manager()->dht_statistics(); });
CMD2_ANY ("dht.throttle.name", std::bind(&core::DhtManager::throttle_name, dht_manager));
CMD2_ANY_STRING_V ("dht.throttle.name.set", std::bind(&core::DhtManager::set_throttle_name, dht_manager, std::placeholders::_2)); rpc::rpc.mark_safe("t.url");
rpc::rpc.mark_safe("t.group");
rpc::rpc.mark_safe("t.id");
rpc::rpc.mark_safe("t.type");
rpc::rpc.mark_safe("t.is_usable");
rpc::rpc.mark_safe("t.is_busy");
rpc::rpc.mark_safe("t.is_enabled");
rpc::rpc.mark_safe("t.is_enabled.set");
rpc::rpc.mark_safe("t.is_extra_tracker");
rpc::rpc.mark_safe("t.is_open");
rpc::rpc.mark_safe("t.normal_interval");
rpc::rpc.mark_safe("t.scrape_time_last");
rpc::rpc.mark_safe("t.scrape_counter");
rpc::rpc.mark_safe("t.success_time_last");
rpc::rpc.mark_safe("t.success_counter");
rpc::rpc.mark_safe("t.failed_time_last");
rpc::rpc.mark_safe("t.failed_counter");
rpc::rpc.mark_safe("t.activity_time_last");
rpc::rpc.mark_safe("t.activity_time_next");
rpc::rpc.mark_safe("t.scrape_complete");
rpc::rpc.mark_safe("t.scrape_incomplete");
rpc::rpc.mark_safe("t.scrape_downloaded");
rpc::rpc.mark_safe("dht.mode.set");
rpc::rpc.mark_safe("dht.port");
rpc::rpc.mark_safe("dht.override_port");
rpc::rpc.mark_safe("dht.add_node");
rpc::rpc.mark_safe("dht.statistics");
rpc::rpc.mark_safe("trackers.numwant");
rpc::rpc.mark_safe("trackers.use_udp");
} }
+109 -60
View File
@@ -4,7 +4,6 @@
#include <ctime> #include <ctime>
#include <regex> #include <regex>
#include <rak/algorithm.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include "core/manager.h" #include "core/manager.h"
@@ -473,11 +472,14 @@ apply_to_throttle(const torrent::Object& rawArgs) {
// if (cond1) { branch1 } else if (cond2) { branch2 } else { branch3 } // if (cond1) { branch1 } else if (cond2) { branch2 } else { branch3 }
// <cond1>,<branch1>,<cond2>,<branch2>,<branch3> // <cond1>,<branch1>,<cond2>,<branch2>,<branch3>
torrent::Object torrent::Object
apply_if(rpc::target_type target, const torrent::Object& rawArgs, int flags) { apply_if(rpc::target_type target, const torrent::Object& raw_args, int flags) {
const torrent::Object::list_type& args = rawArgs.as_list(); auto& args = raw_args.as_list();
torrent::Object::list_const_iterator itr = args.begin(); auto itr = args.begin();
while (itr != args.end() && itr != --args.end()) { if (args.empty())
throw torrent::input_error("Empty argument list to " + std::string((flags & 0x1) ? "branch" : "if") + ".");
{
torrent::Object tmp; torrent::Object tmp;
const torrent::Object* conditional; const torrent::Object* conditional;
@@ -501,40 +503,44 @@ apply_if(rpc::target_type target, const torrent::Object& rawArgs, int flags) {
result = false; result = false;
break; break;
default: default:
throw torrent::input_error("Type not supported by 'if'."); throw torrent::input_error("Type not supported by " + std::string((flags & 0x1) ? "branch" : "if") + ".");
}; };
itr++; itr++;
if (result) if (!result && itr != args.end())
break; itr++;
itr++;
} }
if (itr == args.end()) if (itr == args.end())
return torrent::Object(); return torrent::Object();
if (flags & 0x1 && itr->is_string()) { if (flags & 0x1) {
return rpc::parse_command(target, itr->as_string().c_str(), itr->as_string().c_str() + itr->as_string().size()).first; if (itr->is_string())
return rpc::parse_command(target, itr->as_string().c_str(), itr->as_string().c_str() + itr->as_string().size()).first;
} else if (flags & 0x1 && itr->is_dict_key()) { if (itr->is_dict_key())
return rpc::commands.call_command(itr->as_dict_key().c_str(), itr->as_dict_obj(), target); return rpc::commands.call_command(itr->as_dict_key().c_str(), itr->as_dict_obj(), target);
} else if (flags & 0x1 && itr->is_list()) { if (itr->is_list()) {
// Move this into a special function or something. Also, might be for (const auto& cmd_itr : itr->as_list()) {
// nice to have a parse_command function that takes list if (cmd_itr.is_string())
// iterator... rpc::parse_command(target, cmd_itr.as_string().c_str(), cmd_itr.as_string().c_str() + cmd_itr.as_string().size());
for (const auto& cmdItr : itr->as_list()) else if (cmd_itr.is_dict_key())
if (cmdItr.is_string()) rpc::commands.call_command(cmd_itr.as_dict_key().c_str(), cmd_itr.as_dict_obj(), target);
rpc::parse_command(target, cmdItr.as_string().c_str(), cmdItr.as_string().c_str() + cmdItr.as_string().size());
return torrent::Object(); else
throw torrent::input_error("Invalid command type in branch list.");
}
} else { return torrent::Object();
return *itr; }
throw torrent::input_error("Invalid command type in branch.");
} }
return *itr;
} }
torrent::Object torrent::Object
@@ -730,6 +736,26 @@ apply_arith_count(const torrent::Object::list_type& args) {
return val; return val;
} }
// Get the median of an unordered set of numbers of arbitrary
// type by modifing the underlying dataset
template <typename T = double, typename _InputIter>
T median(_InputIter __first, _InputIter __last) {
T __med;
unsigned int __size = __last - __first;
unsigned int __middle = __size / 2;
_InputIter __target1 = __first + __middle;
std::nth_element(__first, __target1, __last);
__med = *__target1;
if (__size % 2 == 0) {
_InputIter __target2 = std::max_element(__first, __target1);
__med = (__med + *__target2) / 2.0;
}
return __med;
}
int64_t int64_t
apply_arith_other(const char* op, const torrent::Object::list_type& args) { apply_arith_other(const char* op, const torrent::Object::list_type& args) {
if (args.size() == 0) if (args.size() == 0)
@@ -739,7 +765,8 @@ apply_arith_other(const char* op, const torrent::Object::list_type& args) {
return (int64_t)(apply_math_basic(op, std::plus<int64_t>(), args) / apply_arith_count(args)); return (int64_t)(apply_math_basic(op, std::plus<int64_t>(), args) / apply_arith_count(args));
} else if (strcmp(op, "median") == 0) { } else if (strcmp(op, "median") == 0) {
std::vector<int64_t> result = as_vector(args); std::vector<int64_t> result = as_vector(args);
return (int64_t)rak::median(result.begin(), result.end());
return (int64_t)median(result.begin(), result.end());
} else { } else {
throw torrent::input_error("Wrong operation supplied to apply_arith_other."); throw torrent::input_error("Wrong operation supplied to apply_arith_other.");
} }
@@ -775,61 +802,59 @@ apply_set_color(int color_id, const torrent::Object::string_type& color_str) {
void void
initialize_command_ui() { initialize_command_ui() {
CMD2_VAR_STRING("keys.layout", "qwerty"); CMD2_VAR_STRING ("keys.layout", "qwerty");
CMD2_ANY_STRING("view.add", object_convert_void(std::bind(&core::ViewManager::insert_throw, control->view_manager(), std::placeholders::_2))); CMD2_ANY_STRING ("view.add", object_convert_void(std::bind(&core::ViewManager::insert_throw, control->view_manager(), std::placeholders::_2)));
CMD2_ANY_L ("view.list", std::bind(&apply_view_list)); CMD2_ANY_L ("view.list", std::bind(&apply_view_list));
CMD2_ANY_LIST("view.set", std::bind(&apply_view_set, std::placeholders::_2)); CMD2_ANY_LIST ("view.set", std::bind(&apply_view_set, std::placeholders::_2));
CMD2_ANY_LIST ("view.filter", std::bind(&apply_view_event, &core::ViewManager::set_filter, std::placeholders::_2)); CMD2_ANY_LIST ("view.filter", std::bind(&apply_view_event, &core::ViewManager::set_filter, std::placeholders::_2));
CMD2_ANY_LIST ("view.filter_on", std::bind(&apply_view_filter_on, std::placeholders::_2)); CMD2_ANY_LIST ("view.filter_on", std::bind(&apply_view_filter_on, std::placeholders::_2));
CMD2_ANY_LIST ("view.filter.temp", std::bind(&apply_view_event, &core::ViewManager::set_filter_temp, std::placeholders::_2)); CMD2_ANY_LIST ("view.filter.temp", std::bind(&apply_view_event, &core::ViewManager::set_filter_temp, std::placeholders::_2));
CMD2_VAR_STRING("view.filter.temp.excluded", "default,started,stopped"); CMD2_VAR_STRING ("view.filter.temp.excluded", "default,started,stopped");
CMD2_VAR_BOOL ("view.filter.temp.log", 0); CMD2_VAR_BOOL ("view.filter.temp.log", 0);
CMD2_ANY_LIST("view.sort", std::bind(&apply_view_sort, std::placeholders::_2)); CMD2_ANY_LIST ("view.sort", std::bind(&apply_view_sort, std::placeholders::_2));
CMD2_ANY_LIST("view.sort_new", std::bind(&apply_view_event, &core::ViewManager::set_sort_new, std::placeholders::_2)); CMD2_ANY_LIST ("view.sort_new", std::bind(&apply_view_event, &core::ViewManager::set_sort_new, std::placeholders::_2));
CMD2_ANY_LIST("view.sort_current", std::bind(&apply_view_event, &core::ViewManager::set_sort_current, std::placeholders::_2)); CMD2_ANY_LIST ("view.sort_current", std::bind(&apply_view_event, &core::ViewManager::set_sort_current, std::placeholders::_2));
CMD2_ANY_LIST("view.event_added", std::bind(&apply_view_event, &core::ViewManager::set_event_added, std::placeholders::_2)); CMD2_ANY_LIST ("view.event_added", std::bind(&apply_view_event, &core::ViewManager::set_event_added, std::placeholders::_2));
CMD2_ANY_LIST("view.event_removed", std::bind(&apply_view_event, &core::ViewManager::set_event_removed, std::placeholders::_2)); CMD2_ANY_LIST ("view.event_removed", std::bind(&apply_view_event, &core::ViewManager::set_event_removed, std::placeholders::_2));
// Cleanup and add . to view. CMD2_ANY_STRING ("view.size", std::bind(&cmd_view_size, std::placeholders::_2));
CMD2_ANY_STRING ("view.size_not_visible", std::bind(&cmd_view_size_not_visible, std::placeholders::_2));
CMD2_ANY_STRING("view.size", std::bind(&cmd_view_size, std::placeholders::_2)); CMD2_ANY_STRING ("view.persistent", std::bind(&cmd_view_persistent, std::placeholders::_2));
CMD2_ANY_STRING("view.size_not_visible", std::bind(&cmd_view_size_not_visible, std::placeholders::_2));
CMD2_ANY_STRING("view.persistent", std::bind(&cmd_view_persistent, std::placeholders::_2));
CMD2_ANY_STRING_V("view.filter_all", std::bind(&core::View::filter, std::bind(&core::ViewManager::find_ptr_throw, control->view_manager(), std::placeholders::_2))); CMD2_ANY_STRING_V("view.filter_all", std::bind(&core::View::filter, std::bind(&core::ViewManager::find_ptr_throw, control->view_manager(), std::placeholders::_2)));
CMD2_DL_STRING ("view.filter_download", std::bind(&cmd_view_filter_download, std::placeholders::_1, std::placeholders::_2)); CMD2_DL_STRING ("view.filter_download", std::bind(&cmd_view_filter_download, std::placeholders::_1, std::placeholders::_2));
CMD2_DL_STRING ("view.set_visible", std::bind(&cmd_view_set_visible, std::placeholders::_1, std::placeholders::_2)); CMD2_DL_STRING ("view.set_visible", std::bind(&cmd_view_set_visible, std::placeholders::_1, std::placeholders::_2));
CMD2_DL_STRING ("view.set_not_visible", std::bind(&cmd_view_set_not_visible, std::placeholders::_1, std::placeholders::_2)); CMD2_DL_STRING ("view.set_not_visible", std::bind(&cmd_view_set_not_visible, std::placeholders::_1, std::placeholders::_2));
// Commands that affect the default rtorrent UI. // Commands that affect the default rtorrent UI.
CMD2_DL ("ui.unfocus_download", std::bind(&cmd_ui_unfocus_download, std::placeholders::_1)); CMD2_DL ("ui.unfocus_download", std::bind(&cmd_ui_unfocus_download, std::placeholders::_1));
CMD2_ANY ("ui.current_view", std::bind(&cmd_ui_current_view)); CMD2_ANY ("ui.current_view", std::bind(&cmd_ui_current_view));
CMD2_ANY_STRING("ui.current_view.set", std::bind(&cmd_ui_set_view, std::placeholders::_2)); CMD2_ANY_STRING ("ui.current_view.set", std::bind(&cmd_ui_set_view, std::placeholders::_2));
CMD2_ANY ("ui.input.history.size", std::bind(&ui::Root::get_input_history_size, control->ui())); CMD2_ANY ("ui.input.history.size", std::bind(&ui::Root::get_input_history_size, control->ui()));
CMD2_ANY_VALUE_V("ui.input.history.size.set", std::bind(&ui::Root::set_input_history_size, control->ui(), std::placeholders::_2)); CMD2_ANY_VALUE_V ("ui.input.history.size.set", std::bind(&ui::Root::set_input_history_size, control->ui(), std::placeholders::_2));
CMD2_ANY_V ("ui.input.history.clear", std::bind(&ui::Root::clear_input_history, control->ui())); CMD2_ANY_V ("ui.input.history.clear", std::bind(&ui::Root::clear_input_history, control->ui()));
CMD2_VAR_VALUE ("ui.throttle.global.step.small", 5); CMD2_VAR_VALUE ("ui.throttle.global.step.small", 5);
CMD2_VAR_VALUE ("ui.throttle.global.step.medium", 50); CMD2_VAR_VALUE ("ui.throttle.global.step.medium", 50);
CMD2_VAR_VALUE ("ui.throttle.global.step.large", 500); CMD2_VAR_VALUE ("ui.throttle.global.step.large", 500);
CMD2_VAR_VALUE ("ui.focus.page_size", 0); CMD2_VAR_VALUE ("ui.focus.page_size", 0);
CMD2_ANY_LIST ("ui.status.throttle.up.set", std::bind(&cmd_status_throttle_names, true, std::placeholders::_2)); CMD2_ANY_LIST ("ui.status.throttle.up.set", std::bind(&cmd_status_throttle_names, true, std::placeholders::_2));
CMD2_ANY_LIST ("ui.status.throttle.down.set", std::bind(&cmd_status_throttle_names, false, std::placeholders::_2)); CMD2_ANY_LIST ("ui.status.throttle.down.set", std::bind(&cmd_status_throttle_names, false, std::placeholders::_2));
CMD2_ANY ("ui.keymap.style", std::bind(&ui::Root::keymap_style, control->ui())); CMD2_ANY ("ui.keymap.style", std::bind(&ui::Root::keymap_style, control->ui()));
CMD2_ANY_STRING_V("ui.keymap.style.set", std::bind(&ui::Root::set_keymap_style, control->ui(), std::placeholders::_2)); CMD2_ANY_STRING_V("ui.keymap.style.set", std::bind(&ui::Root::set_keymap_style, control->ui(), std::placeholders::_2));
// TODO: Add 'option_string' for rtorrent-specific options. // TODO: Add 'option_string' for rtorrent-specific options.
CMD2_VAR_STRING("ui.torrent_list.layout", "full"); CMD2_VAR_STRING ("ui.torrent_list.layout", "full");
// Move. // Move.
CMD2_ANY("print", &apply_print); CMD2_ANY("print", &apply_print);
@@ -888,4 +913,28 @@ initialize_command_ui() {
return control->object_storage()->get_str(display::color_vars[color_id]); return control->object_storage()->get_str(display::color_vars[color_id]);
}); });
} }
rpc::rpc.mark_safe("view.set_visible");
rpc::rpc.mark_safe("view.set_not_visible");
rpc::rpc.mark_safe("cat");
rpc::rpc.mark_safe("if");
rpc::rpc.mark_safe("branch");
rpc::rpc.mark_safe("and");
rpc::rpc.mark_safe("or");
rpc::rpc.mark_safe("not");
rpc::rpc.mark_safe("value");
rpc::rpc.mark_safe("compare");
rpc::rpc.mark_safe("elapsed.less");
rpc::rpc.mark_safe("elapsed.greater");
rpc::rpc.mark_safe("convert.gm_time");
rpc::rpc.mark_safe("convert.gm_date");
rpc::rpc.mark_safe("convert.time");
rpc::rpc.mark_safe("convert.date");
rpc::rpc.mark_safe("convert.elapsed_time");
rpc::rpc.mark_safe("convert.kb");
rpc::rpc.mark_safe("convert.mb");
rpc::rpc.mark_safe("convert.xb");
rpc::rpc.mark_safe("convert.throttle");
} }
+47 -23
View File
@@ -5,11 +5,11 @@
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <torrent/net/http_stack.h> #include <torrent/net/http_stack.h>
#include <torrent/net/network_manager.h> #include <torrent/runtime/network_manager.h>
#include <torrent/runtime/runtime.h>
#include <torrent/utils/directory_events.h> #include <torrent/utils/directory_events.h>
#include "core/dht_manager.h" #include "core/dht_manager.h"
#include "core/download_store.h"
#include "core/http_queue.h" #include "core/http_queue.h"
#include "core/manager.h" #include "core/manager.h"
#include "core/view_manager.h" #include "core/view_manager.h"
@@ -26,7 +26,9 @@
#include "rpc/lua.h" #include "rpc/lua.h"
#include "rpc/parse_commands.h" #include "rpc/parse_commands.h"
#include "rpc/object_storage.h" #include "rpc/object_storage.h"
#include "session/session_manager.h"
#include "ui/root.h" #include "ui/root.h"
#include "utils/watch_ready_queue.h"
Control::Control() Control::Control()
: m_ui(new ui::Root()), : m_ui(new ui::Root()),
@@ -36,7 +38,8 @@ Control::Control()
m_commandScheduler(new rpc::CommandScheduler()), m_commandScheduler(new rpc::CommandScheduler()),
m_objectStorage(new rpc::object_storage()), m_objectStorage(new rpc::object_storage()),
m_lua_engine(new rpc::LuaEngine()), m_lua_engine(new rpc::LuaEngine()),
m_directory_events(new torrent::directory_events()) { m_directory_events(new torrent::directory_events()),
m_watch_ready_queue(new utils::WatchReadyQueue()) {
m_core = std::make_unique<core::Manager>(); m_core = std::make_unique<core::Manager>();
m_view_manager = std::make_unique<core::ViewManager>(); m_view_manager = std::make_unique<core::ViewManager>();
@@ -44,7 +47,8 @@ Control::Control()
m_inputStdin->slot_pressed(std::bind(&input::Manager::pressed, m_input.get(), std::placeholders::_1)); m_inputStdin->slot_pressed(std::bind(&input::Manager::pressed, m_input.get(), std::placeholders::_1));
m_task_shutdown.slot() = std::bind(&Control::handle_shutdown, this); m_task_shutdown.slot() = [this] { handle_shutdown(); };
m_task_shutdown_clear_requests.slot() = [this] { handle_shutdown_clear_requests(); };
m_commandScheduler->set_slot_error_message([this](const std::string& msg) { m_core->push_log_std(msg); }); m_commandScheduler->set_slot_error_message([this](const std::string& msg) { m_core->push_log_std(msg); });
} }
@@ -58,21 +62,20 @@ Control::~Control() {
void void
Control::initialize() { Control::initialize() {
session_thread::thread()->start_thread();
scgi_thread::thread()->start_thread();
display::Canvas::initialize(); display::Canvas::initialize();
display::Window::slot_schedule([this](display::Window* w, std::chrono::microseconds t) { m_display->schedule(w, t); }); display::Window::slot_schedule([this](display::Window* w, std::chrono::microseconds t) { m_display->schedule(w, t); });
display::Window::slot_unschedule([this](display::Window* w) { m_display->unschedule(w); }); display::Window::slot_unschedule([this](display::Window* w) { m_display->unschedule(w); });
display::Window::slot_adjust([this]() { m_display->adjust_layout(); }); display::Window::slot_adjust([this]() { m_display->adjust_layout(); });
torrent::net_thread::http_stack()->set_user_agent(USER_AGENT);
m_core->listen_open();
m_core->download_store()->enable(rpc::call_command_value("session.use_lock"));
m_core->set_hashing_view(*m_view_manager->find_throw("hashing")); m_core->set_hashing_view(*m_view_manager->find_throw("hashing"));
m_ui->init(this); m_ui->init(this);
if(!display::Canvas::daemon()) if(!display::Canvas::daemon())
m_inputStdin->insert(torrent::this_thread::poll()); m_inputStdin->insert();
} }
void void
@@ -80,12 +83,19 @@ Control::cleanup() {
rpc::rpc.cleanup(); rpc::rpc.cleanup();
torrent::this_thread::scheduler()->erase(&m_task_shutdown); torrent::this_thread::scheduler()->erase(&m_task_shutdown);
torrent::this_thread::scheduler()->erase(&m_task_shutdown_clear_requests);
if(!display::Canvas::daemon()) { if(!display::Canvas::daemon())
m_inputStdin->remove(torrent::this_thread::poll()); m_inputStdin->remove();
}
m_core->download_store()->disable(); m_directory_events->close();
if (scgi_thread::thread()->is_active())
scgi_thread::thread()->stop_thread_wait();
// Wait for all session files to be written.
session_thread::manager()->flush_all_pending_builds();
session_thread::thread()->stop_thread_wait();
m_ui->cleanup(); m_ui->cleanup();
m_core->cleanup(); m_core->cleanup();
@@ -103,7 +113,7 @@ Control::cleanup_exception() {
bool bool
Control::is_shutdown_completed() { Control::is_shutdown_completed() {
if (!m_shutdownQuick || worker_thread->is_active()) if (!m_shutdown_quick)
return false; return false;
// Tracker requests can be disowned, so wait for these to // Tracker requests can be disowned, so wait for these to
@@ -115,18 +125,21 @@ Control::is_shutdown_completed() {
if (torrent::net_thread::http_stack()->size() != 0 || !core()->http_queue()->empty()) if (torrent::net_thread::http_stack()->size() != 0 || !core()->http_queue()->empty())
return false; return false;
return torrent::is_inactive(); return core()->is_download_shutdown_completed();
} }
void void
Control::handle_shutdown() { Control::handle_shutdown() {
m_watch_ready_queue->shutdown();
rpc::commands.call_catch("event.system.shutdown", rpc::make_target(), "shutdown", "System shutdown event action failed: "); rpc::commands.call_catch("event.system.shutdown", rpc::make_target(), "shutdown", "System shutdown event action failed: ");
if (!m_shutdownQuick) { if (scgi_thread::thread()->is_active())
if (worker_thread->is_active()) scgi_thread::thread()->stop_thread_wait();
worker_thread->stop_thread_wait();
if (!m_shutdown_quick) {
torrent::runtime::network_manager()->listen_close(); torrent::runtime::network_manager()->listen_close();
torrent::runtime::shutdown();
m_directory_events->close(); m_directory_events->close();
m_core->shutdown(false); m_core->shutdown(false);
@@ -135,13 +148,24 @@ Control::handle_shutdown() {
torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown, 5s); torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown, 5s);
} else { } else {
if (worker_thread->is_active()) torrent::runtime::quick_shutdown();
worker_thread->stop_thread_wait();
m_core->shutdown(true); m_core->shutdown(true);
} }
m_shutdownQuick = true; if (!m_task_shutdown_clear_requests.is_scheduled())
m_shutdownReceived = false; torrent::this_thread::scheduler()->wait_for_ceil_seconds(&m_task_shutdown_clear_requests, 10s);
m_shutdown_quick = true;
m_shutdown_received = false;
} }
void
Control::handle_shutdown_clear_requests() {
torrent::net_thread::http_stack()->clear_requests();
// Use 5s for the initial wait to ensure trackers get a chance to finish both IPv4 and IPv6 requests.
if (m_clear_requests_count++ == 0)
torrent::this_thread::scheduler()->wait_for(&m_task_shutdown_clear_requests, 5s);
else
torrent::this_thread::scheduler()->wait_for(&m_task_shutdown_clear_requests, 1s);
}
+20 -8
View File
@@ -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/utils/scheduler.h> #include <torrent/system/scheduler.h>
namespace ui { namespace ui {
class Root; class Root;
@@ -38,23 +38,28 @@ namespace torrent {
class directory_events; class directory_events;
} }
namespace utils {
class WatchReadyQueue;
}
class Control { class Control {
public: public:
Control(); Control();
~Control(); ~Control();
bool is_shutdown_completed(); bool is_shutdown_completed();
bool is_shutdown_received() { return m_shutdownReceived; } bool is_shutdown_received() { return m_shutdown_received; }
bool is_shutdown_started() { return m_shutdownQuick; } bool is_shutdown_started() { return m_shutdown_quick; }
void initialize(); void initialize();
void cleanup(); void cleanup();
void cleanup_exception(); void cleanup_exception();
void handle_shutdown(); void handle_shutdown();
void handle_shutdown_clear_requests();
void receive_normal_shutdown() { m_shutdownReceived = true; } void receive_normal_shutdown() { m_shutdown_received = true; }
void receive_quick_shutdown() { m_shutdownReceived = true; m_shutdownQuick = true; } void receive_quick_shutdown() { m_shutdown_received = true; m_shutdown_quick = true; }
core::Manager* core() { return m_core.get(); } core::Manager* core() { return m_core.get(); }
core::ViewManager* view_manager() { return m_view_manager.get(); } core::ViewManager* view_manager() { return m_view_manager.get(); }
@@ -70,6 +75,7 @@ public:
rpc::LuaEngine* lua_engine() { return m_lua_engine.get(); } rpc::LuaEngine* lua_engine() { return m_lua_engine.get(); }
torrent::directory_events* directory_events() { return m_directory_events.get(); } torrent::directory_events* directory_events() { return m_directory_events.get(); }
utils::WatchReadyQueue* watch_ready_queue() { return m_watch_ready_queue.get(); }
uint64_t tick() const { return m_tick; } uint64_t tick() const { return m_tick; }
void inc_tick() { m_tick++; } void inc_tick() { m_tick++; }
@@ -94,16 +100,22 @@ private:
std::unique_ptr<rpc::object_storage> m_objectStorage; std::unique_ptr<rpc::object_storage> m_objectStorage;
std::unique_ptr<rpc::LuaEngine> m_lua_engine; std::unique_ptr<rpc::LuaEngine> m_lua_engine;
std::unique_ptr<torrent::directory_events> m_directory_events; std::unique_ptr<torrent::directory_events> m_directory_events;
std::unique_ptr<utils::WatchReadyQueue> m_watch_ready_queue;
uint64_t m_tick{}; uint64_t m_tick{};
mode_t m_umask; mode_t m_umask;
std::string m_workingDirectory; std::string m_workingDirectory;
torrent::utils::SchedulerEntry m_task_shutdown; torrent::system::SchedulerEntry m_task_shutdown;
torrent::system::SchedulerEntry m_task_shutdown_clear_requests;
std::atomic<bool> m_shutdownReceived{}; int m_clear_requests_count{};
std::atomic<bool> m_shutdownQuick{};
align_cacheline
std::atomic<bool> m_shutdown_received{};
std::atomic<bool> m_shutdown_quick{};
}; };
#endif #endif
+78 -58
View File
@@ -1,22 +1,23 @@
#include "config.h" #include "config.h"
#include "dht_manager.h"
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#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>
#include <torrent/runtime/network_manager.h>
#include <torrent/runtime/runtime.h>
#include <torrent/tracker/dht_controller.h> #include <torrent/tracker/dht_controller.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include "rpc/parse_commands.h"
#include "globals.h"
#include "control.h" #include "control.h"
#include "dht_manager.h"
#include "download.h" #include "download.h"
#include "download_store.h" #include "globals.h"
#include "manager.h" #include "manager.h"
#include "rpc/parse_commands.h"
#include "session/session_manager.h"
#define LT_LOG(log_fmt, ...) \ #define LT_LOG(log_fmt, ...) \
lt_log_print_subsystem(torrent::LOG_DHT_CONTROLLER, "dht_manager", log_fmt, __VA_ARGS__); lt_log_print_subsystem(torrent::LOG_DHT_CONTROLLER, "dht_manager", log_fmt, __VA_ARGS__);
@@ -35,13 +36,13 @@ DhtManager::~DhtManager() {
void void
DhtManager::load_dht_cache() { DhtManager::load_dht_cache() {
if (m_start == dht_disable || !control->core()->download_store()->is_enabled()) { if (m_start == dht_disable || !session_thread::manager()->is_used()) {
LT_LOG("ignoring cache file", 0); LT_LOG("ignoring cache file", 0);
return; return;
} }
std::string cache_filename = control->core()->download_store()->path() + "rtorrent.dht_cache"; auto cache_filename = session_thread::manager()->path() + "rtorrent.dht_cache";
std::fstream cache_stream(cache_filename.c_str(), std::ios::in | std::ios::binary); auto cache_stream = std::fstream(cache_filename.c_str(), std::ios::in | std::ios::binary);
torrent::Object cache = torrent::Object::create_map(); torrent::Object cache = torrent::Object::create_map();
@@ -61,7 +62,7 @@ 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());
} }
torrent::dht_controller()->initialize(cache); torrent::runtime::network_manager()->dht_controller()->initialize(cache);
if (m_start == dht_on) if (m_start == dht_on)
start_dht(); start_dht();
@@ -71,26 +72,22 @@ void
DhtManager::start_dht() { DhtManager::start_dht() {
torrent::this_thread::scheduler()->erase(&m_stop_timeout); torrent::this_thread::scheduler()->erase(&m_stop_timeout);
if (!torrent::dht_controller()->is_valid()) { if (!torrent::runtime::network_manager()->is_dht_valid()) {
LT_LOG("server start skipped, manager is uninitialized", 0); LT_LOG("server start skipped, manager is uninitialized", 0);
return; return;
} }
if (torrent::dht_controller()->is_active()) { if (torrent::runtime::network_manager()->is_dht_active()) {
LT_LOG("server start skipped, already active", 0); LT_LOG("server start skipped, already active", 0);
return; return;
} }
torrent::ThrottlePair throttles = control->core()->get_throttle(m_throttleName); if (!torrent::runtime::network_manager()->dht_controller()->start()) {
torrent::dht_controller()->set_upload_throttle(throttles.first);
torrent::dht_controller()->set_download_throttle(throttles.second);
if (!torrent::dht_controller()->start()) {
m_start = dht_off; m_start = dht_off;
return; return;
} }
torrent::dht_controller()->reset_statistics(); torrent::runtime::network_manager()->dht_controller()->reset_statistics();
m_update_timeout.slot() = std::bind(&DhtManager::update, this); m_update_timeout.slot() = std::bind(&DhtManager::update, this);
@@ -109,28 +106,31 @@ DhtManager::stop_dht() {
torrent::this_thread::scheduler()->erase(&m_update_timeout); torrent::this_thread::scheduler()->erase(&m_update_timeout);
torrent::this_thread::scheduler()->erase(&m_stop_timeout); torrent::this_thread::scheduler()->erase(&m_stop_timeout);
if (torrent::dht_controller()->is_active()) { if (torrent::runtime::network_manager()->is_dht_active()) {
LT_LOG("stopping server", 0); LT_LOG("stopping server", 0);
log_statistics(true); log_statistics(true);
torrent::dht_controller()->stop(); torrent::runtime::network_manager()->dht_controller()->stop();
} }
} }
void void
DhtManager::save_dht_cache() { DhtManager::save_dht_cache() {
if (!control->core()->download_store()->is_enabled() || !torrent::dht_controller()->is_valid()) if (!session_thread::manager()->is_used())
return; return;
std::string filename = control->core()->download_store()->path() + "rtorrent.dht_cache"; if (!torrent::runtime::network_manager()->is_dht_valid())
std::string filename_tmp = filename + ".new"; return;
std::fstream cache_file(filename_tmp.c_str(), std::ios::out | std::ios::trunc);
auto filename = session_thread::manager()->path() + "rtorrent.dht_cache";
auto filename_tmp = filename + ".new";
auto cache_file = std::fstream(filename_tmp.c_str(), std::ios::out | std::ios::trunc);
if (!cache_file.is_open()) if (!cache_file.is_open())
return; return;
torrent::Object cache = torrent::Object::create_map(); torrent::Object cache = torrent::Object::create_map();
cache_file << *torrent::dht_controller()->store_cache(&cache); cache_file << *torrent::runtime::network_manager()->dht_controller()->store_cache(&cache);
if (!cache_file.good()) if (!cache_file.good())
return; return;
@@ -141,17 +141,32 @@ DhtManager::save_dht_cache() {
} }
void void
DhtManager::set_mode(const std::string& arg) { DhtManager::set_mode_by_user(const std::string& arg) {
int i; unsigned int mode = [arg]() {
for (i = 0; i < dht_settings_num; i++) { for (int i = 0; i < dht_settings_num; i++) {
if (arg == dht_settings[i]) { if (arg == dht_settings[i])
m_start = i; return i;
break; }
}
throw torrent::input_error("Invalid dht mode: " + arg);
}();
m_set_by_user = true;
if (!torrent::runtime::is_network_initialized()) {
m_start = mode;
return;
} }
if (i == dht_settings_num) set_mode_directly(mode);
throw torrent::input_error("Invalid argument."); }
void
DhtManager::set_mode_directly(unsigned int mode) {
if (mode >= dht_settings_num)
throw torrent::input_error("Invalid dht mode.");
m_start = mode;
if (m_start == dht_off) if (m_start == dht_off)
stop_dht(); stop_dht();
@@ -159,9 +174,26 @@ DhtManager::set_mode(const std::string& arg) {
start_dht(); start_dht();
} }
void
DhtManager::set_auto_if_untouched_and_has_session() {
if (m_set_by_user) {
set_mode_directly(m_start);
return;
}
if (rpc::call_command_string("session.path").empty()) {
LT_LOG("DHT auto-start disabled, session path not set.", 0);
return;
}
set_mode_directly(dht_auto);
LT_LOG("DHT auto-start enabled.", 0);
}
void void
DhtManager::update() { DhtManager::update() {
if (!torrent::dht_controller()->is_active()) if (!torrent::runtime::network_manager()->is_dht_active())
throw torrent::internal_error("DhtManager::update called with DHT inactive."); throw torrent::internal_error("DhtManager::update called with DHT inactive.");
if (m_start == dht_auto && !m_stop_timeout.is_scheduled()) { if (m_start == dht_auto && !m_stop_timeout.is_scheduled()) {
@@ -186,17 +218,17 @@ DhtManager::update() {
bool bool
DhtManager::log_statistics(bool force) { DhtManager::log_statistics(bool force) {
auto stats = torrent::dht_controller()->get_statistics(); auto stats = torrent::runtime::network_manager()->dht_controller()->get_statistics();
// Check for firewall problems. // Check for firewall problems.
if (stats.cycle > 2 && stats.queries_sent - m_dhtPrevQueriesSent > 100 && stats.queries_received == m_dhtPrevQueriesReceived) { if (stats.cycle > 2 && stats.queries_sent - m_dhtPrevQueriesSent > 100 && stats.queries_received == m_dhtPrevQueriesReceived) {
// We should have had clients ping us at least but have received // We should have had clients ping us at least but have received
// nothing, that means the UDP port is probably unreachable. // nothing, that means the UDP port is probably unreachable.
if (torrent::dht_controller()->is_receiving_requests()) if (torrent::runtime::network_manager()->is_dht_active_and_receiving_requests())
LT_LOG("listening port appears to be unreachable, no queries received", 0); LT_LOG("listening port appears to be unreachable, no queries received", 0);
torrent::dht_controller()->set_receive_requests(false); torrent::runtime::network_manager()->dht_controller()->set_receive_requests(false);
} }
if (stats.queries_sent - m_dhtPrevQueriesSent > stats.num_nodes * 2 + 20 && stats.replies_received == m_dhtPrevRepliesReceived) { if (stats.queries_sent - m_dhtPrevQueriesSent > stats.num_nodes * 2 + 20 && stats.replies_received == m_dhtPrevRepliesReceived) {
@@ -211,7 +243,7 @@ DhtManager::log_statistics(bool force) {
m_warned = false; m_warned = false;
if (stats.queries_received > m_dhtPrevQueriesReceived) if (stats.queries_received > m_dhtPrevQueriesReceived)
torrent::dht_controller()->set_receive_requests(true); torrent::runtime::network_manager()->dht_controller()->set_receive_requests(true);
// Nothing to log while bootstrapping, but check again every minute. // Nothing to log while bootstrapping, but check again every minute.
if (stats.cycle <= 1) { if (stats.cycle <= 1) {
@@ -233,13 +265,11 @@ DhtManager::log_statistics(bool force) {
if ((force && stats.cycle != m_dhtPrevCycle) || stats.cycle == 3 || stats.cycle > m_dhtPrevCycle + 7) { if ((force && stats.cycle != m_dhtPrevCycle) || stats.cycle == 3 || stats.cycle > m_dhtPrevCycle + 7) {
char buffer[256]; char buffer[256];
snprintf(buffer, sizeof(buffer), snprintf(buffer, sizeof(buffer),
"DHT statistics: %d queries in, %d queries out, %d replies received, %lld bytes read, %lld bytes sent, " "DHT statistics: %d queries in, %d queries out, %d replies received, "
"%d known nodes in %d buckets, %d peers (highest: %d) tracked in %d torrents.", "%d known nodes in %d buckets, %d peers (highest: %d) tracked in %d torrents.",
stats.queries_received - m_dhtPrevQueriesReceived, stats.queries_received - m_dhtPrevQueriesReceived,
stats.queries_sent - m_dhtPrevQueriesSent, stats.queries_sent - m_dhtPrevQueriesSent,
stats.replies_received - m_dhtPrevRepliesReceived, stats.replies_received - m_dhtPrevRepliesReceived,
(long long unsigned int)(stats.down_rate.total() - m_dhtPrevBytesDown),
(long long unsigned int)(stats.up_rate.total() - m_dhtPrevBytesUp),
stats.num_nodes, stats.num_nodes,
stats.num_buckets, stats.num_buckets,
stats.num_peers, stats.num_peers,
@@ -252,8 +282,6 @@ DhtManager::log_statistics(bool force) {
m_dhtPrevQueriesSent = stats.queries_sent; m_dhtPrevQueriesSent = stats.queries_sent;
m_dhtPrevRepliesReceived = stats.replies_received; m_dhtPrevRepliesReceived = stats.replies_received;
m_dhtPrevQueriesReceived = stats.queries_received; m_dhtPrevQueriesReceived = stats.queries_received;
m_dhtPrevBytesUp = stats.up_rate.total();
m_dhtPrevBytesDown = stats.down_rate.total();
} }
return false; return false;
@@ -264,11 +292,11 @@ DhtManager::dht_statistics() {
torrent::Object dhtStats = torrent::Object::create_map(); torrent::Object dhtStats = torrent::Object::create_map();
dhtStats.insert_key("dht", dht_settings[m_start]); dhtStats.insert_key("dht", dht_settings[m_start]);
dhtStats.insert_key("active", torrent::dht_controller()->is_active()); dhtStats.insert_key("active", torrent::runtime::network_manager()->is_dht_active());
dhtStats.insert_key("throttle", m_throttleName); dhtStats.insert_key("throttle", "");
if (torrent::dht_controller()->is_active()) { if (torrent::runtime::network_manager()->is_dht_active()) {
auto stats = torrent::dht_controller()->get_statistics(); auto stats = torrent::runtime::network_manager()->dht_controller()->get_statistics();
dhtStats.insert_key("cycle", stats.cycle); dhtStats.insert_key("cycle", stats.cycle);
dhtStats.insert_key("queries_received", stats.queries_received); dhtStats.insert_key("queries_received", stats.queries_received);
@@ -276,8 +304,8 @@ DhtManager::dht_statistics() {
dhtStats.insert_key("replies_received", stats.replies_received); dhtStats.insert_key("replies_received", stats.replies_received);
dhtStats.insert_key("errors_received", stats.errors_received); dhtStats.insert_key("errors_received", stats.errors_received);
dhtStats.insert_key("errors_caught", stats.errors_caught); dhtStats.insert_key("errors_caught", stats.errors_caught);
dhtStats.insert_key("bytes_read", stats.down_rate.total()); dhtStats.insert_key("bytes_read", int64_t());
dhtStats.insert_key("bytes_written", stats.up_rate.total()); dhtStats.insert_key("bytes_written", int64_t());
dhtStats.insert_key("nodes", stats.num_nodes); dhtStats.insert_key("nodes", stats.num_nodes);
dhtStats.insert_key("buckets", stats.num_buckets); dhtStats.insert_key("buckets", stats.num_buckets);
dhtStats.insert_key("peers", stats.num_peers); dhtStats.insert_key("peers", stats.num_peers);
@@ -288,12 +316,4 @@ DhtManager::dht_statistics() {
return dhtStats; return dhtStats;
} }
void
DhtManager::set_throttle_name(const std::string& throttleName) {
if (torrent::dht_controller()->is_active())
throw torrent::input_error("Cannot set DHT throttle while active.");
m_throttleName = throttleName;
}
} }
+14 -15
View File
@@ -2,12 +2,17 @@
#define RTORRENT_CORE_DHT_MANAGER_H #define RTORRENT_CORE_DHT_MANAGER_H
#include <torrent/object.h> #include <torrent/object.h>
#include <torrent/utils/scheduler.h> #include <torrent/system/scheduler.h>
namespace core { namespace core {
class DhtManager { class DhtManager {
public: public:
static constexpr int dht_disable = 0;
static constexpr int dht_off = 1;
static constexpr int dht_auto = 2;
static constexpr int dht_on = 3;
~DhtManager(); ~DhtManager();
void load_dht_cache(); void load_dht_cache();
@@ -18,19 +23,14 @@ public:
void stop_dht(); void stop_dht();
void auto_start() { if (m_start == dht_auto) start_dht(); } void auto_start() { if (m_start == dht_auto) start_dht(); }
void set_mode(const std::string& arg); void set_mode_by_user(const std::string& arg);
void set_mode_directly(unsigned int mode);
void set_throttle_name(const std::string& throttleName); void set_auto_if_untouched_and_has_session();
const std::string& throttle_name() const { return m_throttleName; }
private: private:
static const int dht_disable = 0; static constexpr int dht_settings_num = 4;
static const int dht_off = 1; static const char* dht_settings[dht_settings_num];
static const int dht_auto = 2;
static const int dht_on = 3;
static const int dht_settings_num = 4;
static const char* dht_settings[dht_settings_num];
void update(); void update();
bool log_statistics(bool force); bool log_statistics(bool force);
@@ -42,13 +42,12 @@ private:
uint64_t m_dhtPrevBytesUp; uint64_t m_dhtPrevBytesUp;
uint64_t m_dhtPrevBytesDown; uint64_t m_dhtPrevBytesDown;
torrent::utils::SchedulerEntry m_update_timeout; torrent::system::SchedulerEntry m_update_timeout;
torrent::utils::SchedulerEntry m_stop_timeout; torrent::system::SchedulerEntry m_stop_timeout;
bool m_warned{}; bool m_warned{};
bool m_set_by_user{};
int m_start{dht_off}; int m_start{dht_off};
std::string m_throttleName;
}; };
} }
+16 -29
View File
@@ -1,25 +1,25 @@
#include "config.h" #include "config.h"
#include "core/download.h"
#include <list> #include <list>
#include <rak/file_stat.h>
#include <rak/path.h>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include <torrent/rate.h> #include <torrent/rate.h>
#include <torrent/torrent.h> #include <torrent/torrent.h>
#include <torrent/tracker/tracker.h> #include <torrent/tracker/tracker.h>
#include <torrent/data/file.h> #include <torrent/data/file.h>
#include <torrent/data/file_list.h> #include <torrent/data/file_list.h>
#include <torrent/utils/file_stat.h>
#include "rpc/parse_commands.h" #include "rpc/parse_commands.h"
#include "control.h" #include "control.h"
#include "download.h" #include "core/manager.h"
#include "manager.h"
namespace core { namespace core {
Download::Download(download_type d) : Download::Download(download_type d)
m_download(d) { : m_download(d) {
m_download.info()->signal_tracker_success().push_back(std::bind(&Download::receive_tracker_msg, this, "")); m_download.info()->signal_tracker_success().push_back(std::bind(&Download::receive_tracker_msg, this, ""));
m_download.info()->signal_tracker_failed().push_back(std::bind(&Download::receive_tracker_msg, this, std::placeholders::_1)); m_download.info()->signal_tracker_failed().push_back(std::bind(&Download::receive_tracker_msg, this, std::placeholders::_1));
@@ -32,21 +32,6 @@ Download::~Download() {
m_download = download_type(); m_download = download_type();
} }
void
Download::enable_udp_trackers(bool state) {
for (int idx = 0, end = m_download.tracker_controller().size(); idx < end; ++idx) {
auto tracker = m_download.tracker_controller().at(idx);
if (tracker.type() != torrent::TRACKER_UDP)
continue;
if (state)
tracker.enable();
else
tracker.disable();
}
}
uint32_t uint32_t
Download::priority() { Download::priority() {
return bencode()->get_key("rtorrent").get_key_value("priority"); return bencode()->get_key("rtorrent").get_key_value("priority");
@@ -103,15 +88,16 @@ Download::distributed_copies() const {
} }
void void
Download::set_throttle_name(const std::string& throttleName) { Download::set_throttle_name(const std::string& name) {
if (m_download.info()->is_active()) if (m_download.info()->is_active())
throw torrent::input_error("Cannot set throttle on active download."); throw torrent::input_error("Cannot set throttle on active download.");
torrent::ThrottlePair throttles = control->core()->get_throttle(throttleName); auto throttles = control->core()->get_throttle(name);
m_download.set_upload_throttle(throttles.first); m_download.set_upload_throttle(throttles.first);
m_download.set_download_throttle(throttles.second); m_download.set_download_throttle(throttles.second);
m_download.bencode()->get_key("rtorrent").insert_key("throttle_name", throttleName); m_download.bencode()->get_key("rtorrent").insert_key("throttle_name", name);
} }
void void
@@ -123,24 +109,25 @@ Download::set_root_directory(const std::string& path) {
// directory 'after' moving files. In cases where the user wants to // directory 'after' moving files. In cases where the user wants to
// override this behavior the download must first be closed or // override this behavior the download must first be closed or
// 'd.directory_base.set' may be used. // 'd.directory_base.set' may be used.
rak::file_stat file_stat;
torrent::FileList* file_list = m_download.file_list(); torrent::utils::FileStat file_stat;
torrent::FileList* file_list = m_download.file_list();
if (is_hash_checked() && file_list->completed_chunks() != 0 && if (is_hash_checked() && file_list->completed_chunks() != 0 &&
(file_list->is_multi_file() ? (file_list->is_multi_file() ?
!file_list->is_root_dir_created() : !file_list->is_root_dir_created() :
!file_stat.update(file_list->front()->frozen_path()))) { !file_stat.update(file_list->front()->frozen_path().str()))) {
set_message("Cannot change the directory of an open download after the files have been moved."); 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)); 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 atter the files have been moved."); throw torrent::input_error("Cannot change the directory of an open download after the files have been moved.");
} }
control->core()->download_list()->close_directly(this); control->core()->download_list()->close_directly(this);
file_list->set_root_dir(rak::path_expand(path)); file_list->set_root_dir(expand_path(path));
bencode()->get_key("rtorrent").insert_key("directory", path); bencode()->get_key("rtorrent").insert_key("directory", path);
} }
+4 -4
View File
@@ -22,6 +22,8 @@ public:
typedef torrent::ConnectionList connection_list_type; typedef torrent::ConnectionList connection_list_type;
typedef download_type::ConnectionType connection_type; typedef download_type::ConnectionType connection_type;
static constexpr uint32_t default_resume_flags = ~uint32_t{} & ~torrent::Download::open_enable_fallocate;
static const int variable_hashing_stopped = 0; static const int variable_hashing_stopped = 0;
static const int variable_hashing_initial = 1; static const int variable_hashing_initial = 1;
static const int variable_hashing_last = 2; static const int variable_hashing_last = 2;
@@ -70,8 +72,6 @@ public:
const std::string& message() const { return m_message; } const std::string& message() const { return m_message; }
void set_message(const std::string& msg) { m_message = msg; } void set_message(const std::string& msg) { m_message = msg; }
void enable_udp_trackers(bool state);
uint32_t priority(); uint32_t priority();
void set_priority(uint32_t p); void set_priority(uint32_t p);
@@ -80,7 +80,7 @@ public:
void set_root_directory(const std::string& path); void set_root_directory(const std::string& path);
void set_throttle_name(const std::string& throttleName); void set_throttle_name(const std::string& name);
bool operator == (const std::string& str) const; bool operator == (const std::string& str) const;
@@ -102,7 +102,7 @@ private:
download_type m_download; download_type m_download;
bool m_hashFailed{}; bool m_hashFailed{};
std::string m_message; std::string m_message;
uint32_t m_resumeFlags{~uint32_t{}}; uint32_t m_resumeFlags{default_resume_flags};
unsigned int m_group{}; unsigned int m_group{};
}; };
+76 -37
View File
@@ -7,7 +7,6 @@
#include <functional> #include <functional>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <rak/path.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,11 +15,12 @@
#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 "control.h" #include "control.h"
#include "globals.h" #include "globals.h"
#include "core/download.h" #include "core/download.h"
#include "core/download_store.h"
#include "core/http_queue.h" #include "core/http_queue.h"
#include "core/manager.h" #include "core/manager.h"
#include "rpc/parse_commands.h" #include "rpc/parse_commands.h"
@@ -35,8 +35,10 @@ 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) { download_factory_load_stream(const char* filename, bool* is_invalid) {
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())
@@ -45,8 +47,10 @@ download_factory_load_stream(const char* filename) {
auto obj = std::make_unique<torrent::Object>(); auto obj = std::make_unique<torrent::Object>();
stream >> *obj; stream >> *obj;
if (!stream.good()) if (!stream.good() || !obj->is_map()) {
*is_invalid = true;
return std::unique_ptr<torrent::Object>(); return std::unique_ptr<torrent::Object>();
}
return obj; return obj;
} }
@@ -105,40 +109,42 @@ DownloadFactory::receive_load() {
throw torrent::internal_error("DownloadFactory::load*() called on an object with m_stream != NULL"); throw torrent::internal_error("DownloadFactory::load*() called on an object with m_stream != NULL");
if (is_network_uri(m_uri)) { if (is_network_uri(m_uri)) {
// Http handling here.
m_stream.reset(new std::stringstream); m_stream.reset(new std::stringstream);
HttpQueue::iterator itr = m_manager->http_queue()->insert(m_uri, m_stream); auto done_fn = [this]() { receive_loaded(); };
auto failed_fn = [this](const std::string& error) { receive_failed(error); };
itr->add_done_slot([this]() { receive_loaded(); }); m_manager->http_queue()->insert(m_uri, m_stream, done_fn, failed_fn);
itr->add_failed_slot([this](const std::string& error) { receive_failed(error); });
m_variables["tied_to_file"] = (int64_t)false; m_variables["tied_to_file"] = (int64_t)false;
return;
}
} else if (is_magnet_uri(m_uri)) { if (is_magnet_uri(m_uri)) {
// DEBUG: Use m_object. // DEBUG: Use m_object.
m_stream.reset(new std::stringstream()); m_stream.reset(new std::stringstream());
*m_stream << "d10:magnet-uri" << m_uri.length() << ":" << m_uri << "e"; *m_stream << "d10:magnet-uri" << m_uri.length() << ":" << m_uri << "e";
m_variables["tied_to_file"] = (int64_t)false; m_variables["tied_to_file"] = (int64_t)false;
receive_loaded();
} else {
std::fstream stream(rak::path_expand(m_uri).c_str(), std::ios::in | std::ios::binary);
if (!stream.is_open())
return receive_failed("Could not open file");
m_object = new torrent::Object;
stream >> *m_object;
if (!stream.good())
return receive_failed("Reading torrent file failed");
m_isFile = true;
receive_loaded(); receive_loaded();
return;
} }
std::fstream stream(expand_path(m_uri).c_str(), std::ios::in | std::ios::binary);
if (!stream.is_open())
return receive_failed("Could not open file");
m_object = new torrent::Object;
stream >> *m_object;
if (!stream.good())
return receive_failed("Reading torrent file failed");
m_isFile = true;
receive_loaded();
} }
void void
@@ -159,8 +165,13 @@ DownloadFactory::receive_commit() {
void void
DownloadFactory::receive_success() { DownloadFactory::receive_success() {
auto rtorrent_object = download_factory_load_stream((rak::path_expand(m_uri) + ".rtorrent").c_str()); bool session_invalid = false;
auto libtorrent_resume_object = download_factory_load_stream((rak::path_expand(m_uri) + ".libtorrent_resume").c_str());
auto rtorrent_object = download_factory_load_stream((expand_path(m_uri) + ".rtorrent").c_str(), &session_invalid);
auto 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;
@@ -182,6 +193,14 @@ 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()) {
@@ -239,9 +258,6 @@ DownloadFactory::receive_success() {
rpc::call_command("d.peers_max.set", rpc::call_command("throttle.max_peers.seed"), rpc::make_target(download)); rpc::call_command("d.peers_max.set", rpc::call_command("throttle.max_peers.seed"), rpc::make_target(download));
} }
if (!rpc::call_command_value("trackers.use_udp"))
download->enable_udp_trackers(false);
// Skip forcing trackers to scrape when rtorrent starts // Skip forcing trackers to scrape when rtorrent starts
if (m_initLoad && rpc::call_command_value("trackers.delay_scrape")) if (m_initLoad && rpc::call_command_value("trackers.delay_scrape"))
download->set_resume_flags(torrent::Download::start_skip_tracker); download->set_resume_flags(torrent::Download::start_skip_tracker);
@@ -255,19 +271,42 @@ DownloadFactory::receive_success() {
rpc::call_command_value("system.file.split_size"), rpc::call_command_value("system.file.split_size"),
rpc::call_command_string("system.file.split_suffix")); rpc::call_command_string("system.file.split_suffix"));
if (!rtorrent->has_key_string("directory")) if (rtorrent->has_key_string("directory")) {
rpc::call_command("d.directory.set", m_variables["directory"], rpc::make_target(download));
else
rpc::call_command("d.directory_base.set", rtorrent->get_key("directory"), rpc::make_target(download)); rpc::call_command("d.directory_base.set", rtorrent->get_key("directory"), rpc::make_target(download));
} else if (download->download()->info()->is_meta_download()) {
auto& metadata_path = control->core()->magnet_path();
if (!metadata_path.empty())
rpc::call_command("d.directory.set", metadata_path, rpc::make_target(download));
else
rpc::call_command("d.directory.set", session_thread::session_path(), rpc::make_target(download));
} else {
rpc::call_command("d.directory.set", m_variables["directory"], rpc::make_target(download));
}
if (!m_session && m_variables["tied_to_file"].as_value()) 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", rpc::call_command_value("protocol.pex"), rpc::make_target(download)); rpc::call_command("d.peer_exchange.set", torrent::runtime::client_config()->is_pex_enabled(), rpc::make_target(download));
torrent::resume_load_addresses(*download->download(), resumeObject); try {
torrent::resume_load_file_priorities(*download->download(), resumeObject); torrent::resume_load_addresses(*download->download(), resumeObject);
torrent::resume_load_tracker_settings(*download->download(), resumeObject); torrent::resume_load_file_priorities(*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.
@@ -319,7 +358,7 @@ void
DownloadFactory::log_created(Download* download, torrent::Object* rtorrent) { DownloadFactory::log_created(Download* download, torrent::Object* rtorrent) {
std::stringstream dump; std::stringstream dump;
dump << "info_hash = " << torrent::hash_string_to_hex_str(download->info()->hash()) << std::endl; dump << "info_hash = " << torrent::utils::transform_to_hex_str(download->info()->hash()) << std::endl;
dump << "session = " << (m_session ? "true" : "false") << std::endl; dump << "session = " << (m_session ? "true" : "false") << std::endl;
if (download->download()->info()->is_meta_download()) if (download->download()->info()->is_meta_download())
+4 -4
View File
@@ -9,7 +9,7 @@
#include <iosfwd> #include <iosfwd>
#include <torrent/object.h> #include <torrent/object.h>
#include <torrent/utils/scheduler.h> #include <torrent/system/scheduler.h>
#include "http_queue.h" #include "http_queue.h"
@@ -79,9 +79,9 @@ private:
command_list_type m_commands; command_list_type m_commands;
torrent::Object::map_type m_variables; torrent::Object::map_type m_variables;
slot_void m_slot_finished; slot_void m_slot_finished;
torrent::utils::SchedulerEntry m_task_load; torrent::system::SchedulerEntry m_task_load;
torrent::utils::SchedulerEntry m_task_commit; torrent::system::SchedulerEntry m_task_commit;
}; };
bool is_network_uri(const std::string& uri); bool is_network_uri(const std::string& uri);
+33 -24
View File
@@ -1,9 +1,9 @@
#include "config.h" #include "config.h"
#include <algorithm> #include <algorithm>
#include <cstring>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <rak/string_manip.h>
#include <torrent/data/file.h> #include <torrent/data/file.h>
#include <torrent/utils/resume.h> #include <torrent/utils/resume.h>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
@@ -13,6 +13,7 @@
#include <torrent/object_stream.h> #include <torrent/object_stream.h>
#include <torrent/torrent.h> #include <torrent/torrent.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include <torrent/utils/string_manip.h>
#include "rpc/parse_commands.h" #include "rpc/parse_commands.h"
@@ -22,10 +23,10 @@
#include "view.h" #include "view.h"
#include "view_manager.h" #include "view_manager.h"
#include "dht_manager.h" #include "core/dht_manager.h"
#include "download.h" #include "core/download.h"
#include "download_list.h" #include "core/download_list.h"
#include "download_store.h" #include "session/session_manager.h"
#include "ui/root.h" #include "ui/root.h"
#define DL_TRIGGER_EVENT(download, event_name) \ #define DL_TRIGGER_EVENT(download, event_name) \
@@ -68,10 +69,8 @@ DownloadList::clear() {
void void
DownloadList::session_save() { DownloadList::session_save() {
unsigned int c = std::count_if(begin(), end(), [&](Download* d) { return control->core()->download_store()->save_resume(d); }); for (auto& download : *this)
session_thread::manager()->save_resume_download(download);
if (c != size())
lt_log_print(torrent::LOG_ERROR, "Failed to save session torrents.");
control->dht_manager()->save_dht_cache(); control->dht_manager()->save_dht_cache();
control->ui()->save_input_history(); control->ui()->save_input_history();
@@ -84,10 +83,13 @@ DownloadList::find(const torrent::HashString& hash) {
DownloadList::iterator DownloadList::iterator
DownloadList::find_hex(const char* hash) { DownloadList::find_hex(const char* hash) {
if (strlen(hash) < 40)
return end();
torrent::HashString key; torrent::HashString key;
for (torrent::HashString::iterator itr = key.begin(), last = key.end(); itr != last; itr++, hash += 2) if (torrent::utils::transform_from_hex(hash, hash + 40, key) != key.end())
*itr = (rak::hexchar_to_value(*hash) << 4) + rak::hexchar_to_value(*(hash + 1)); return end();
return std::find_if(begin(), end(), [key](Download* d) { return key == d->info()->hash(); }); return std::find_if(begin(), end(), [key](Download* d) { return key == d->info()->hash(); });
} }
@@ -199,10 +201,10 @@ DownloadList::erase(iterator itr) {
(*itr)->set_hash_failed(true); (*itr)->set_hash_failed(true);
close(*itr); close(*itr);
session_thread::manager()->remove_download(*itr);
control->core()->download_store()->remove(*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); v->erase(*itr);
@@ -329,7 +331,7 @@ DownloadList::resume(Download* download, int flags) {
// We need to make sure the flags aren't reset if someone decideds // We need to make sure the flags aren't reset if someone decideds
// to call resume() while it is hashing, etc. // to call resume() while it is hashing, etc.
if (download->resume_flags() == ~uint32_t()) if (download->resume_flags() == Download::default_resume_flags)
download->set_resume_flags(flags); download->set_resume_flags(flags);
// Manual or end-of-download rehashing clears the resume data so // Manual or end-of-download rehashing clears the resume data so
@@ -402,7 +404,7 @@ DownloadList::resume(Download* download, int flags) {
download->set_priority(download->priority()); download->set_priority(download->priority());
download->download()->start(download->resume_flags()); download->download()->start(download->resume_flags());
download->set_resume_flags(~uint32_t()); download->set_resume_flags(Download::default_resume_flags);
DL_TRIGGER_EVENT(download, "event.download.resumed"); DL_TRIGGER_EVENT(download, "event.download.resumed");
@@ -419,7 +421,7 @@ DownloadList::pause(Download* download, int flags) {
try { try {
download->set_resume_flags(~uint32_t()); download->set_resume_flags(Download::default_resume_flags);
rpc::parse_command_single(rpc::make_target(download), "view.set_not_visible=active"); rpc::parse_command_single(rpc::make_target(download), "view.set_not_visible=active");
@@ -491,6 +493,10 @@ DownloadList::hash_done(Download* download) {
if (!download->is_hash_checked()) { if (!download->is_hash_checked()) {
download->set_hash_failed(true); download->set_hash_failed(true);
auto& msg = download->download()->hash_error_message();
if (!msg.empty())
download->set_message(msg);
DL_TRIGGER_EVENT(download, "event.download.hash_failed"); DL_TRIGGER_EVENT(download, "event.download.hash_failed");
return; return;
} }
@@ -639,10 +645,8 @@ DownloadList::confirm_finished(Download* download) {
// the download. // the download.
// //
// Obsolete. // Obsolete.
if (!download->is_active() && rpc::call_command_value("session.on_completion") != 0) { if (!download->is_active() && rpc::call_command_value("session.on_completion") != 0)
// torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume")); session_thread::manager()->save_resume_download(download);
control->core()->download_store()->save_resume(download);
}
// Send the completed request before resuming so we don't reset the // Send the completed request before resuming so we don't reset the
// up/downloaded baseline. // up/downloaded baseline.
@@ -656,8 +660,8 @@ DownloadList::confirm_finished(Download* download) {
if (find(infohash) == end()) if (find(infohash) == end())
return; return;
// if (download->resume_flags() != ~uint32_t()) // if (download->resume_flags() != Download::default_resume_flags)
// throw torrent::internal_error("DownloadList::confirm_finished(...) download->resume_flags() != ~uint32_t()."); // throw torrent::internal_error("DownloadList::confirm_finished(...) download->resume_flags() != Download::default_resume_flags.");
// See #1292. // See #1292.
// //
@@ -667,7 +671,7 @@ DownloadList::confirm_finished(Download* download) {
// //
// TODO: Add a check when setting the flags to see if the torrent is // TODO: Add a check when setting the flags to see if the torrent is
// being hashed. // being hashed.
download->set_resume_flags(~uint32_t()); download->set_resume_flags(Download::default_resume_flags);
if (!download->is_active() && rpc::call_command_value("d.state", rpc::make_target(download)) == 1) if (!download->is_active() && rpc::call_command_value("d.state", rpc::make_target(download)) == 1)
resume(download, resume(download,
@@ -683,8 +687,10 @@ DownloadList::process_meta_download(Download* download) {
rpc::call_command("d.stop", torrent::Object(), rpc::make_target(download)); rpc::call_command("d.stop", torrent::Object(), rpc::make_target(download));
rpc::call_command("d.close", torrent::Object(), rpc::make_target(download)); rpc::call_command("d.close", torrent::Object(), rpc::make_target(download));
std::string metafile = (*download->file_list()->begin())->frozen_path(); std::string metafile = (*download->file_list()->begin())->frozen_path().str();
std::fstream file(metafile.c_str(), std::ios::in | std::ios::binary); std::fstream file(metafile.c_str(), std::ios::in | std::ios::binary);
if (!file.is_open()) { if (!file.is_open()) {
lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not read download metadata."); lt_log_print(torrent::LOG_TORRENT_ERROR, "Could not read download metadata.");
return; return;
@@ -692,11 +698,13 @@ DownloadList::process_meta_download(Download* download) {
torrent::Object* bencode = new 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; 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;
} }
file.close(); file.close();
// Steal the keys we still need. The old download has no use for them. // Steal the keys we still need. The old download has no use for them.
@@ -707,6 +715,7 @@ DownloadList::process_meta_download(Download* download) {
bencode->insert_key("announce-list", torrent::Object()).swap(download->bencode()->get_key("announce-list")); bencode->insert_key("announce-list", torrent::Object()).swap(download->bencode()->get_key("announce-list"));
erase_ptr(download); erase_ptr(download);
control->core()->try_create_download_from_meta_download(bencode, metafile); control->core()->try_create_download_from_meta_download(bencode, metafile);
} }
-205
View File
@@ -1,205 +0,0 @@
// DownloadStore handles the saving and listing of session torrents.
#include "config.h"
#include <fstream>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <rak/error_number.h>
#include <rak/path.h>
#include <rak/string_manip.h>
#include <torrent/utils/resume.h>
#include <torrent/object.h>
#include <torrent/exceptions.h>
#include <torrent/torrent.h>
#include <torrent/rate.h>
#include <torrent/object_stream.h>
#include "utils/directory.h"
#include "download.h"
#include "download_store.h"
#include "rpc/parse_commands.h"
namespace core {
void
DownloadStore::enable(bool lock) {
if (is_enabled())
throw torrent::input_error("Session directory already enabled.");
if (m_path.empty())
return;
if (lock)
m_lockfile.set_path(m_path + "rtorrent.lock");
else
m_lockfile.set_path(std::string());
if (!m_lockfile.try_lock()) {
if (rak::error_number::current().is_bad_path())
throw torrent::input_error("Could not lock session directory: \"" + m_path + "\", " + rak::error_number::current().c_str());
else
throw torrent::input_error("Could not lock session directory: \"" + m_path + "\", held by \"" + m_lockfile.locked_by_as_string() + "\".");
}
}
void
DownloadStore::disable() {
if (!is_enabled())
return;
m_lockfile.unlock();
}
void
DownloadStore::set_path(const std::string& path) {
if (is_enabled())
throw torrent::input_error("Tried to change session directory while it is enabled.");
if (!path.empty() && *path.rbegin() != '/')
m_path = rak::path_expand(path + '/');
else
m_path = rak::path_expand(path);
}
bool
DownloadStore::write_bencode(const std::string& filename, const torrent::Object& obj, uint32_t skip_mask) {
int fd;
torrent::Object tmp;
std::fstream output(filename.c_str(), std::ios::out | std::ios::trunc);
if (!output.is_open())
goto download_store_save_error;
torrent::object_write_bencode(&output, &obj, skip_mask);
if (!output.good())
goto download_store_save_error;
output.close();
// Test the new file, to ensure it is a valid bencode string.
output.open(filename.c_str(), std::ios::in);
output >> tmp;
if (!output.good())
goto download_store_save_error;
output.close();
// Ensure that the new file is actually written to the disk
fd = ::open(filename.c_str(), O_WRONLY);
if (fd < 0)
goto download_store_save_error;
if (rpc::call_command_value("system.files.session.fdatasync")) {
#ifdef __APPLE__
fsync(fd);
#else
fdatasync(fd);
#endif
}
::close(fd);
return true;
download_store_save_error:
output.close();
return false;
}
bool
DownloadStore::save(Download* d, int flags) {
if (!is_enabled())
return true;
torrent::Object* resume_base = &d->download()->bencode()->get_key("libtorrent_resume");
torrent::Object* rtorrent_base = &d->download()->bencode()->get_key("rtorrent");
// Move this somewhere else?
rtorrent_base->insert_key("chunks_done", d->download()->file_list()->completed_chunks());
rtorrent_base->insert_key("chunks_wanted", d->download()->data()->wanted_chunks());
rtorrent_base->insert_key("total_uploaded", d->info()->up_rate()->total());
rtorrent_base->insert_key("total_downloaded", d->info()->down_rate()->total());
// Don't save for completed torrents when we've cleared the uncertain_pieces.
torrent::resume_save_progress(*d->download(), *resume_base);
torrent::resume_save_uncertain_pieces(*d->download(), *resume_base);
torrent::resume_save_addresses(*d->download(), *resume_base);
torrent::resume_save_file_priorities(*d->download(), *resume_base);
torrent::resume_save_tracker_settings(*d->download(), *resume_base);
// Temp fixing of all flags, move to a better place:
resume_base->set_flags(torrent::Object::flag_session_data);
rtorrent_base->set_flags(torrent::Object::flag_session_data);
std::string base_filename = create_filename(d);
if (!write_bencode(base_filename + ".libtorrent_resume.new", *resume_base, 0) ||
!write_bencode(base_filename + ".rtorrent.new", *rtorrent_base, 0))
return false;
::rename((base_filename + ".libtorrent_resume.new").c_str(), (base_filename + ".libtorrent_resume").c_str());
::rename((base_filename + ".rtorrent.new").c_str(), (base_filename + ".rtorrent").c_str());
if (!(flags & flag_skip_static) &&
write_bencode(base_filename + ".new", *d->bencode(), torrent::Object::flag_session_data))
::rename((base_filename + ".new").c_str(), base_filename.c_str());
return true;
}
void
DownloadStore::remove(Download* d) {
if (!is_enabled())
return;
::unlink((create_filename(d) + ".libtorrent_resume").c_str());
::unlink((create_filename(d) + ".rtorrent").c_str());
::unlink(create_filename(d).c_str());
}
// This also needs to check that it isn't a directory.
bool
not_correct_format(const utils::directory_entry& entry) {
return !DownloadStore::is_correct_format(entry.s_name);
}
utils::Directory
DownloadStore::get_formated_entries() {
if (!is_enabled())
return utils::Directory();
utils::Directory d(m_path);
if (!d.update(utils::Directory::update_hide_dot))
throw torrent::storage_error("core::DownloadStore::update() could not open directory \"" + m_path + "\"");
d.erase(std::remove_if(d.begin(), d.end(), [&](const utils::directory_entry& entry) { return not_correct_format(entry); }), d.end());
return d;
}
bool
DownloadStore::is_correct_format(const std::string& f) {
if (f.size() != 48 || f.substr(40) != ".torrent")
return false;
for (std::string::const_iterator itr = f.begin(); itr != f.end() - 8; ++itr)
if (!(*itr >= '0' && *itr <= '9') &&
!(*itr >= 'A' && *itr <= 'F'))
return false;
return true;
}
std::string
DownloadStore::create_filename(Download* d) {
return m_path + rak::transform_hex(d->info()->hash().begin(), d->info()->hash().end()) + ".torrent";
}
}
-49
View File
@@ -1,49 +0,0 @@
#ifndef RTORRENT_CORE_DOWNLOAD_STORE_H
#define RTORRENT_CORE_DOWNLOAD_STORE_H
#include <string>
#include "utils/lockfile.h"
namespace utils {
class Directory;
}
namespace core {
class Download;
class DownloadStore {
public:
static const int flag_skip_static = 0x1;
bool is_enabled() { return m_lockfile.is_locked(); }
void enable(bool lock);
void disable();
const std::string& path() const { return m_path; }
void set_path(const std::string& path);
bool save(Download* d, int flags);
bool save_full(Download* d) { return save(d, 0); }
bool save_resume(Download* d) { return save(d, flag_skip_static); }
void remove(Download* d);
// Currently shows all entries in the correct format.
utils::Directory get_formated_entries();
static bool is_correct_format(const std::string& f);
private:
std::string create_filename(Download* d);
bool write_bencode(const std::string& filename, const torrent::Object& obj, uint32_t skip_mask);
std::string m_path;
utils::Lockfile m_lockfile;
};
}
#endif
+9 -5
View File
@@ -9,17 +9,21 @@
namespace core { namespace core {
HttpQueue::iterator HttpQueue::iterator
HttpQueue::insert(const std::string& url, std::shared_ptr<std::ostream> stream) { HttpQueue::insert(const std::string& url, std::shared_ptr<std::ostream> stream,
std::function<void()> done_fn, std::function<void(const std::string&)> failed_fn) {
auto itr = base_type::insert(end(), torrent::net::HttpGet(url, stream)); auto itr = base_type::insert(end(), torrent::net::HttpGet(url, stream));
itr->set_max_file_size(15 << 20);
itr->set_redirect_only_http_https();
for (auto& slot : m_signal_insert) for (auto& slot : m_signal_insert)
slot(*itr); slot(*itr);
itr->add_done_slot([this, itr]() { erase(itr); }); itr->add_done_slot(torrent::this_thread::thread(), std::move(done_fn));
itr->add_failed_slot([this, itr](auto) { erase(itr); }); itr->add_done_slot(torrent::this_thread::thread(), [this, itr]() { erase(itr); });
// TODO: Downloading http torrents doesn't seem to work. itr->add_failed_slot(torrent::this_thread::thread(), std::move(failed_fn));
// TODO: Quitting no longer works. itr->add_failed_slot(torrent::this_thread::thread(), [this, itr](auto) { erase(itr); });
torrent::net_thread::http_stack()->start_get(*itr); torrent::net_thread::http_stack()->start_get(*itr);
+2 -1
View File
@@ -39,7 +39,8 @@ public:
// //
// Consider adding a flag to indicate whetever HttpQueue should // Consider adding a flag to indicate whetever HttpQueue should
// delete the stream. // delete the stream.
iterator insert(const std::string& url, std::shared_ptr<std::ostream> stream); iterator insert(const std::string& url, std::shared_ptr<std::ostream> stream,
std::function<void()> done_fn, std::function<void(const std::string&)> failed_fn);
void erase(iterator itr); void erase(iterator itr);
void clear(); void clear();
+107 -108
View File
@@ -1,27 +1,25 @@
#include "config.h" #include "config.h"
#include "core/manager.h"
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include <unistd.h> #include <unistd.h>
#include <sys/select.h> #include <sys/select.h>
#include <rak/address_info.h> #include <fnmatch.h>
#include <rak/regex.h>
#include <rak/path.h>
#include <rak/string_manip.h>
#include <torrent/utils/resume.h> #include <torrent/utils/resume.h>
#include <torrent/object.h> #include <torrent/object.h>
#include <torrent/connection_manager.h>
#include <torrent/error.h>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include <torrent/object_stream.h> #include <torrent/object_stream.h>
#include <torrent/throttle.h> #include <torrent/throttle.h>
#include <torrent/net/http_stack.h> #include <torrent/net/http_stack.h>
#include <torrent/net/network_config.h>
#include <torrent/net/network_manager.h>
#include <torrent/net/socket_address.h> #include <torrent/net/socket_address.h>
#include <torrent/runtime/network_config.h>
#include <torrent/runtime/network_manager.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include <torrent/utils/string_manip.h>
#include "rpc/parse_commands.h" #include "rpc/parse_commands.h"
#include "utils/directory.h" #include "utils/directory.h"
@@ -32,11 +30,11 @@
#include "control.h" #include "control.h"
#include "core/download.h" #include "core/download.h"
#include "core/download_factory.h" #include "core/download_factory.h"
#include "core/download_store.h"
#include "core/http_queue.h" #include "core/http_queue.h"
#include "core/manager.h"
#include "core/view.h" #include "core/view.h"
#include <torrent/runtime/client_config.h>
namespace core { namespace core {
const int Manager::create_start; const int Manager::create_start;
@@ -50,11 +48,10 @@ Manager::push_log(const char* msg) {
m_log_complete->lock_and_push_log(msg, strlen(msg), 0); m_log_complete->lock_and_push_log(msg, strlen(msg), 0);
} }
Manager::Manager() : Manager::Manager()
m_log_important(torrent::log_open_log_buffer("important")), : m_log_important(torrent::log_open_log_buffer("important")),
m_log_complete(torrent::log_open_log_buffer("complete")) { m_log_complete(torrent::log_open_log_buffer("complete")) {
m_download_store = std::make_unique<DownloadStore>();
m_download_list = std::make_unique<DownloadList>(); m_download_list = std::make_unique<DownloadList>();
m_file_status_cache = std::make_unique<FileStatusCache>(); m_file_status_cache = std::make_unique<FileStatusCache>();
m_http_queue = std::make_unique<HttpQueue>(); m_http_queue = std::make_unique<HttpQueue>();
@@ -68,6 +65,20 @@ Manager::~Manager() {
torrent::Throttle::destroy_throttle(m_throttles["NULL"].first); torrent::Throttle::destroy_throttle(m_throttles["NULL"].first);
} }
bool
Manager::is_download_shutdown_completed() {
for (const auto& download : *download_list()) {
// TODO: Why is this being checked?
// if (download->tracker_controller().is_active())
// return false;
if (download->tracker_controller().has_active_trackers_not_dht_scrape_disownable())
return false;
}
return true;
}
void void
Manager::set_hashing_view(View* v) { Manager::set_hashing_view(View* v) {
if (v == nullptr || m_hashingView != nullptr) if (v == nullptr || m_hashingView != nullptr)
@@ -77,10 +88,10 @@ Manager::set_hashing_view(View* v) {
m_hashingView->signal_changed().push_back(std::bind(&Manager::receive_hashing_changed, this)); m_hashingView->signal_changed().push_back(std::bind(&Manager::receive_hashing_changed, this));
} }
torrent::ThrottlePair ThrottlePair
Manager::get_throttle(const std::string& name) { Manager::get_throttle(const std::string& name) {
ThrottleMap::const_iterator itr = m_throttles.find(name); auto itr = m_throttles.find(name);
torrent::ThrottlePair throttles = (itr == m_throttles.end() ? torrent::ThrottlePair(nullptr, nullptr) : itr->second); auto throttles = (itr == m_throttles.end() ? ThrottlePair(nullptr, nullptr) : itr->second);
if (throttles.first == nullptr) if (throttles.first == nullptr)
throttles.first = torrent::up_throttle_global(); throttles.first = torrent::up_throttle_global();
@@ -91,20 +102,6 @@ Manager::get_throttle(const std::string& name) {
return throttles; return throttles;
} }
void
Manager::set_address_throttle(uint32_t begin, uint32_t end, torrent::ThrottlePair throttles) {
m_addressThrottles.set_merge(begin, end, throttles);
torrent::connection_manager()->address_throttle() = std::bind(&core::Manager::get_address_throttle, control->core(), std::placeholders::_1);
}
torrent::ThrottlePair
Manager::get_address_throttle(const sockaddr* addr) {
if (addr->sa_family != AF_INET)
return torrent::ThrottlePair(nullptr, nullptr);
return m_addressThrottles.get(ntohl(reinterpret_cast<const sockaddr_in*>(addr)->sin_addr.s_addr), torrent::ThrottlePair(nullptr, nullptr));
}
int64_t int64_t
Manager::retrieve_throttle_value(const torrent::Object::string_type& name, bool rate, bool up) { Manager::retrieve_throttle_value(const torrent::Object::string_type& name, bool rate, bool up) {
ThrottleMap::iterator itr = throttles().find(name); ThrottleMap::iterator itr = throttles().find(name);
@@ -134,6 +131,16 @@ Manager::retrieve_throttle_value(const torrent::Object::string_type& name, bool
} }
} }
void
Manager::set_magnet_path(const std::string& path) {
if (path.empty())
m_magnet_path.clear();
else if (path.back() == '/')
m_magnet_path = path;
else
m_magnet_path = path + '/';
}
void void
Manager::cleanup() { Manager::cleanup() {
// Need to disconnect log signals? Not really since we won't receive // Need to disconnect log signals? Not really since we won't receive
@@ -146,78 +153,12 @@ Manager::cleanup() {
void void
Manager::shutdown(bool force) { Manager::shutdown(bool force) {
if (!force) if (!force) {
for (auto d : *m_download_list) for (auto d : *m_download_list)
m_download_list->pause_default(d); m_download_list->pause_default(d);
else } else {
for (auto d : *m_download_list) for (auto d : *m_download_list)
m_download_list->close_quick(d); m_download_list->close_quick(d);
}
void
Manager::listen_open() {
// This stuff really should be moved outside of manager, make it
// part of the init script.
if (!rpc::call_command_value("network.port_open"))
return;
int portFirst, portLast;
torrent::Object portRange = rpc::call_command("network.port_range");
if (!portRange.is_string())
throw torrent::input_error("Invalid port_range argument type.");
if (std::sscanf(portRange.as_string().c_str(), "%i-%i", &portFirst, &portLast) != 2)
throw torrent::input_error("Invalid port_range argument.");
if (portFirst > portLast || portLast >= (1 << 16))
throw torrent::input_error("Invalid port range.");
if (rpc::call_command_value("network.port_random")) {
int boundary = portFirst + random() % (portLast - portFirst + 1);
if (torrent::runtime::network_manager()->listen_open(boundary, portLast) ||
torrent::runtime::network_manager()->listen_open(portFirst, boundary))
return;
} else {
if (torrent::runtime::network_manager()->listen_open(portFirst, portLast))
return;
}
throw torrent::input_error("Could not open/bind port for listening: " + std::string(std::strerror(errno)));
}
void
Manager::set_proxy_address(const std::string& addr) {
int port;
rak::address_info* ai;
std::string buf(addr.length() + 1, '\0');
int err = std::sscanf(addr.c_str(), "%[^:]:%i", buf.data(), &port);
if (err <= 0)
throw torrent::input_error("Could not parse proxy address.");
if (err == 1)
port = 80;
if ((err = rak::address_info::get_address_info(buf.data(), PF_INET, SOCK_STREAM, &ai)) != 0)
throw torrent::input_error("Could not set proxy address: " + std::string(rak::address_info::strerror(err)) + ".");
try {
auto sa = torrent::sa_copy(ai->c_addrinfo()->ai_addr);
torrent::sap_set_port(sa, port);
torrent::config::network_config()->set_proxy_address(sa.get());
rak::address_info::free_address_info(ai);
} catch (torrent::input_error& e) {
rak::address_info::free_address_info(ai);
throw e;
} }
} }
@@ -233,9 +174,10 @@ is_data_uri(const std::string& uri) {
std::string std::string
decode_data_uri(const std::string& uri) { decode_data_uri(const std::string& uri) {
const auto start = uri.find("base64,", 5) + 7; const auto pos = uri.find("base64,", 5);
if (start == std::string::npos) if (pos == std::string::npos)
throw torrent::input_error("Invalid data uri: not base64 encoded."); throw torrent::input_error("Invalid data uri: not base64 encoded.");
const auto start = pos + 7;
if (start >= uri.size()) if (start >= uri.size())
throw torrent::input_error("Empty base64."); throw torrent::input_error("Empty base64.");
return utils::decode_base64(uri.substr(start)); return utils::decode_base64(uri.substr(start));
@@ -249,7 +191,7 @@ Manager::try_create_download(const std::string& uri, int flags, const command_li
!is_network_uri(uri) && !is_network_uri(uri) &&
!is_magnet_uri(uri) && !is_magnet_uri(uri) &&
!is_data_uri(uri) && !is_data_uri(uri) &&
!file_status_cache()->insert(uri, 0)) !file_status_cache()->insert(uri))
return; return;
// Adding download. // Adding download.
@@ -306,14 +248,70 @@ path_expand_transform(std::string path, const utils::directory_entry& entry) {
return path + entry.s_name; return path + entry.s_name;
} }
namespace {
// Consider rewritting such that m_seq is replaced by first/last.
template <typename Sequence>
class split_iterator_t {
public:
typedef typename Sequence::const_iterator const_iterator;
typedef typename Sequence::value_type value_type;
split_iterator_t() {}
split_iterator_t(const Sequence& seq, value_type delim) :
m_seq(&seq),
m_delim(delim),
m_pos(seq.begin()),
m_next(std::find(seq.begin(), seq.end(), delim)) {
}
Sequence operator * () { return Sequence(m_pos, m_next); }
split_iterator_t& operator ++ () {
m_pos = m_next;
if (m_pos == m_seq->end())
return *this;
m_pos++;
m_next = std::find(m_pos, m_seq->end(), m_delim);
return *this;
}
bool operator == (const split_iterator_t&) const { return m_pos == m_seq->end(); }
bool operator != (const split_iterator_t&) const { return m_pos != m_seq->end(); }
private:
const Sequence* m_seq;
value_type m_delim;
const_iterator m_pos;
const_iterator m_next;
};
template <typename Sequence>
inline split_iterator_t<Sequence>
split_iterator(const Sequence& seq, typename Sequence::value_type delim) {
return split_iterator_t<Sequence>(seq, delim);
}
template <typename Sequence>
inline split_iterator_t<Sequence>
split_iterator(const Sequence&) {
return split_iterator_t<Sequence>();
}
}
// Move this somewhere better. // Move this somewhere better.
void void
path_expand(std::vector<std::string>* paths, const std::string& pattern) { path_expand(std::vector<std::string>* paths, const std::string& pattern) {
std::vector<utils::Directory> currentCache; std::vector<utils::Directory> currentCache;
std::vector<utils::Directory> nextCache; std::vector<utils::Directory> nextCache;
rak::split_iterator_t<std::string> first = rak::split_iterator(pattern, '/'); auto first = split_iterator(pattern, '/');
rak::split_iterator_t<std::string> last = rak::split_iterator(pattern); auto last = split_iterator(pattern);
if (first == last) if (first == last)
return; return;
@@ -322,7 +320,7 @@ path_expand(std::vector<std::string>* paths, const std::string& pattern) {
if ((*first).empty()) { if ((*first).empty()) {
currentCache.push_back(utils::Directory("/")); currentCache.push_back(utils::Directory("/"));
++first; ++first;
} else if (rak::trim(*first) == "~") { } else if (torrent::utils::trim_spaces_str(*first) == "~") {
currentCache.push_back(utils::Directory("~")); currentCache.push_back(utils::Directory("~"));
++first; ++first;
} else { } else {
@@ -332,9 +330,9 @@ path_expand(std::vector<std::string>* paths, const std::string& pattern) {
// Might be an idea to use depth-first search instead. // Might be an idea to use depth-first search instead.
for (; first != last; ++first) { for (; first != last; ++first) {
rak::regex r(*first); const std::string& pattern = *first;
if (r.pattern().empty()) if (pattern.empty())
continue; continue;
// Special case for ".."? // Special case for ".."?
@@ -342,8 +340,8 @@ path_expand(std::vector<std::string>* paths, const std::string& pattern) {
for (auto& itr : currentCache) { for (auto& itr : currentCache) {
// Only include filenames starting with '.' if the pattern // Only include filenames starting with '.' if the pattern
// starts with the same. // starts with the same.
itr.update((r.pattern()[0] != '.') ? utils::Directory::update_hide_dot : 0); itr.update((pattern[0] != '.') ? utils::Directory::update_hide_dot : 0);
itr.erase(std::remove_if(itr.begin(), itr.end(), [r](const utils::directory_entry& entry) { return !r(entry.s_name); }), itr.end()); itr.erase(std::remove_if(itr.begin(), itr.end(), [&pattern](const utils::directory_entry& entry) { return fnmatch(pattern.c_str(), entry.s_name.c_str(), 0) != 0; }), itr.end());
for (const auto& cache : itr) for (const auto& cache : itr)
nextCache.push_back(path_expand_transform(itr.path() + (itr.path() == "/" ? "" : "/"), cache)); nextCache.push_back(path_expand_transform(itr.path() + (itr.path() == "/" ? "" : "/"), cache));
@@ -438,6 +436,7 @@ Manager::receive_hashing_changed() {
} else { } else {
(*itr)->set_hash_failed(true); (*itr)->set_hash_failed(true);
(*itr)->set_message("Hashing failed: " + std::string(e.what()));
lt_log_print(torrent::LOG_TORRENT_ERROR, "Hashing failed: %s", e.what()); lt_log_print(torrent::LOG_TORRENT_ERROR, "Hashing failed: %s", e.what());
} }
} }
+18 -23
View File
@@ -5,7 +5,6 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <torrent/utils/log_buffer.h> #include <torrent/utils/log_buffer.h>
#include <torrent/connection_manager.h>
#include <torrent/object.h> #include <torrent/object.h>
#include "download_list.h" #include "download_list.h"
@@ -21,10 +20,10 @@ class FileStatusCache;
namespace core { namespace core {
class DownloadStore;
class HttpQueue; class HttpQueue;
typedef std::map<std::string, torrent::ThrottlePair> ThrottleMap; using ThrottlePair = std::pair<torrent::Throttle*, torrent::Throttle*>;
using ThrottleMap = std::map<std::string, ThrottlePair>;
class View; class View;
@@ -36,32 +35,28 @@ public:
Manager(); Manager();
~Manager(); ~Manager();
DownloadList* download_list() { return m_download_list.get(); } bool is_download_shutdown_completed();
DownloadStore* download_store() { return m_download_store.get(); }
FileStatusCache* file_status_cache() { return m_file_status_cache.get(); }
HttpQueue* http_queue() { return m_http_queue.get(); } DownloadList* download_list() { return m_download_list.get(); }
FileStatusCache* file_status_cache() { return m_file_status_cache.get(); }
View* hashing_view() { return m_hashingView; } HttpQueue* http_queue() { return m_http_queue.get(); }
View* hashing_view() { return m_hashingView; }
void set_hashing_view(View* v); void set_hashing_view(View* v);
torrent::log_buffer* log_important() { return m_log_important.get(); } auto* log_important() { return m_log_important.get(); }
torrent::log_buffer* log_complete() { return m_log_complete.get(); } auto* log_complete() { return m_log_complete.get(); }
ThrottleMap& throttles() { return m_throttles; } ThrottleMap& throttles() { return m_throttles; }
torrent::ThrottlePair get_throttle(const std::string& name); ThrottlePair get_throttle(const std::string& name);
int64_t retrieve_throttle_value(const torrent::Object::string_type& name, bool rate, bool up); int64_t retrieve_throttle_value(const torrent::Object::string_type& name, bool rate, bool up);
// Use custom throttle for the given range of IP addresses.
void set_address_throttle(uint32_t begin, uint32_t end, torrent::ThrottlePair throttles);
torrent::ThrottlePair get_address_throttle(const sockaddr* addr);
void cleanup(); void cleanup();
void listen_open(); const std::string& magnet_path();
void set_magnet_path(const std::string& path);
void set_proxy_address(const std::string& addr);
void shutdown(bool force); void shutdown(bool force);
@@ -84,8 +79,6 @@ public:
void try_create_download_from_meta_download(torrent::Object* bencode, const std::string& metafile); void try_create_download_from_meta_download(torrent::Object* bencode, const std::string& metafile);
private: private:
typedef RangeMap<uint32_t, torrent::ThrottlePair> AddressThrottleMap;
void create_http(const std::string& uri); void create_http(const std::string& uri);
void create_final(std::istream* s); void create_final(std::istream* s);
@@ -95,22 +88,24 @@ private:
void receive_hashing_changed(); void receive_hashing_changed();
std::unique_ptr<DownloadList> m_download_list; std::unique_ptr<DownloadList> m_download_list;
std::unique_ptr<DownloadStore> m_download_store;
std::unique_ptr<FileStatusCache> m_file_status_cache; std::unique_ptr<FileStatusCache> m_file_status_cache;
std::unique_ptr<HttpQueue> m_http_queue; std::unique_ptr<HttpQueue> m_http_queue;
View* m_hashingView{}; View* m_hashingView{};
ThrottleMap m_throttles; ThrottleMap m_throttles;
AddressThrottleMap m_addressThrottles;
torrent::log_buffer_ptr m_log_important; torrent::log_buffer_ptr m_log_important;
torrent::log_buffer_ptr m_log_complete; torrent::log_buffer_ptr m_log_complete;
std::string m_magnet_path;
}; };
// Meh, cleanup. // Meh, cleanup.
extern void receive_tracker_dump(const std::string& url, const char* data, size_t size); extern void receive_tracker_dump(const std::string& url, const char* data, size_t size);
inline const std::string& Manager::magnet_path() { return m_magnet_path; }
} }
#endif #endif
+3 -3
View File
@@ -18,7 +18,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <torrent/object.h> #include <torrent/object.h>
#include <torrent/utils/scheduler.h> #include <torrent/system/scheduler.h>
#include "globals.h" #include "globals.h"
@@ -153,8 +153,8 @@ private:
std::chrono::microseconds m_last_changed{}; std::chrono::microseconds m_last_changed{};
signal_void m_signal_changed; signal_void m_signal_changed;
torrent::utils::SchedulerEntry m_delay_changed; torrent::system::SchedulerEntry m_delay_changed;
}; };
} // namespace core } // namespace core
+2 -1
View File
@@ -35,7 +35,8 @@ ViewManager::insert(const std::string& name) {
View* view = new View(); View* view = new View();
view->initialize(name); view->initialize(name);
return base_type::insert(end(), view); base_type::push_back(view);
return --end();
} }
ViewManager::iterator ViewManager::iterator
+4 -4
View File
@@ -2,16 +2,16 @@
#define RTORRENT_CORE_VIEW_MANAGER_H #define RTORRENT_CORE_VIEW_MANAGER_H
#include <string> #include <string>
#include <rak/unordered_vector.h> #include <torrent/utils/unordered_vector.h>
#include "view.h" #include "view.h"
namespace core { namespace core {
class ViewManager : public rak::unordered_vector<View*> { class ViewManager : public torrent::utils::unordered_vector<View*> {
public: public:
typedef rak::unordered_vector<View*> base_type; typedef torrent::utils::unordered_vector<View*> base_type;
typedef std::list<std::string> filter_args; typedef std::list<std::string> filter_args;
using base_type::iterator; using base_type::iterator;
using base_type::const_iterator; using base_type::const_iterator;
+3 -1
View File
@@ -4,7 +4,9 @@
#include <string> #include <string>
#include <vector> #include <vector>
#if defined(HAVE_NCURSESW_CURSES_H) #if defined(HAVE_NO_NCURSES)
#include "curses_stub.h"
#elif defined(HAVE_NCURSESW_CURSES_H)
#include <ncursesw/curses.h> #include <ncursesw/curses.h>
#elif defined(HAVE_NCURSESW_H) #elif defined(HAVE_NCURSESW_H)
#include <ncursesw.h> #include <ncursesw.h>
+38 -30
View File
@@ -11,23 +11,31 @@
namespace display { namespace display {
bool Canvas::m_isInitialized = false; bool Canvas::m_initialized{};
bool Canvas::m_isDaemon = false; bool Canvas::m_daemon{};
// Maps ncurses color IDs to a ncurses attribute int // Maps ncurses color IDs to a ncurses attribute int
std::unordered_map<int, int> Canvas::m_attr_map = {}; std::unordered_map<int, int> Canvas::m_attr_map = {};
Canvas::Canvas(int x, int y, int width, int height) { Canvas::Canvas(int x, int y, int width, int height) {
if (!m_isDaemon) { if (!m_daemon) {
m_window = newwin(height, width, y, x); m_window = newwin(height, width, y, x);
if (m_window == NULL) if (m_window == nullptr)
throw torrent::internal_error("Could not allocate ncurses canvas."); throw torrent::internal_error("Could not allocate ncurses canvas.");
} }
} }
Canvas::~Canvas() {
if (!m_daemon && m_window != nullptr) {
delwin(m_window);
m_window = nullptr;
}
}
void void
Canvas::resize(int x, int y, int w, int h) { Canvas::resize(int x, int y, int w, int h) {
if (!m_isDaemon) { if (!m_daemon) {
wresize(m_window, h, w); wresize(m_window, h, w);
mvwin(m_window, y, x); mvwin(m_window, y, x);
} }
@@ -35,7 +43,7 @@ Canvas::resize(int x, int y, int w, int h) {
void void
Canvas::print_attributes(unsigned int x, unsigned int y, const char* first, const char* last, const attributes_list* attributes) { Canvas::print_attributes(unsigned int x, unsigned int y, const char* first, const char* last, const attributes_list* attributes) {
if (!m_isDaemon) { if (!m_daemon) {
move(x, y); move(x, y);
attr_t org_attr; attr_t org_attr;
@@ -68,14 +76,13 @@ Canvas::print_attributes(unsigned int x, unsigned int y, const char* first, cons
void void
Canvas::initialize() { Canvas::initialize() {
if (m_isInitialized) if (m_initialized)
return; throw torrent::internal_error("Canvas::initialize() called more than once.");
m_isDaemon = rpc::call_command_value("system.daemon"); m_daemon = rpc::call_command_value("system.daemon");
m_initialized = true;
m_isInitialized = true; if (!m_daemon) {
if (!m_isDaemon) {
initscr(); initscr();
start_color(); start_color();
use_default_colors(); use_default_colors();
@@ -88,6 +95,19 @@ Canvas::initialize() {
} }
} }
void
Canvas::cleanup() {
if (!m_initialized)
return;
m_initialized = false;
if (!m_daemon) {
noraw();
endwin();
}
}
// Function wrapper for what possibly is a macro // Function wrapper for what possibly is a macro
int int
get_colors() { get_colors() {
@@ -102,7 +122,7 @@ Canvas::build_colors() {
// This may get called early in the start process by the config // This may get called early in the start process by the config
// file, so we need to delay building until initscr() has a chance // file, so we need to delay building until initscr() has a chance
// to run // to run
if (!m_isInitialized || m_isDaemon) if (!m_initialized || m_daemon)
return; return;
// basic color names, index maps to ncurses COLOR_* // basic color names, index maps to ncurses COLOR_*
@@ -115,7 +135,9 @@ Canvas::build_colors() {
for (int k = 1; k < RCOLOR_MAX; k++) { for (int k = 1; k < RCOLOR_MAX; k++) {
init_pair(k, -1, -1); init_pair(k, -1, -1);
std::string color_def = rpc::call_command_string(color_vars[k]); std::string color_def = rpc::call_command_string(color_vars[k]);
if (color_def.empty()) if (color_def.empty())
continue; // Use terminal default if definition is empty continue; // Use terminal default if definition is empty
@@ -126,6 +148,7 @@ Canvas::build_colors() {
// Process string as space-separated words // Process string as space-separated words
size_t start = 0, end = 0; size_t start = 0, end = 0;
while (true) { while (true) {
end = color_def.find(' ', start); end = color_def.find(' ', start);
std::string word = color_def.substr(start, end - start); std::string word = color_def.substr(start, end - start);
@@ -169,10 +192,8 @@ Canvas::build_colors() {
// Check that fg & bg color index is valid // Check that fg & bg color index is valid
if ((color[0] != -1 && color[0] >= get_colors()) || (color[1] != -1 && color[1] >= get_colors())) { if ((color[0] != -1 && color[0] >= get_colors()) || (color[1] != -1 && color[1] >= get_colors())) {
char buf[33];
snprintf(buf, 33, "%d", get_colors());
Canvas::cleanup(); Canvas::cleanup();
throw torrent::input_error(color_def + ": your terminal only supports " + buf + " colors."); throw torrent::input_error(color_def + ": your terminal only supports " + std::to_string(get_colors()) + " colors.");
} }
m_attr_map[k] = attr; // overwrite or insert the value m_attr_map[k] = attr; // overwrite or insert the value
@@ -196,24 +217,11 @@ Canvas::build_colors() {
} }
} }
void
Canvas::cleanup() {
if (!m_isInitialized)
return;
m_isInitialized = false;
if (!m_isDaemon) {
noraw();
endwin();
}
}
std::pair<int, int> std::pair<int, int>
Canvas::term_size() { Canvas::term_size() {
struct winsize ws; struct winsize ws;
if (!m_isDaemon) { if (!m_daemon) {
if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0)
return std::pair<int, int>(ws.ws_col, ws.ws_row); return std::pair<int, int>(ws.ws_col, ws.ws_row);
} }
+35 -43
View File
@@ -54,9 +54,10 @@ public:
// Initialize stdscr. // Initialize stdscr.
static void initialize(); static void initialize();
static void build_colors();
static void cleanup(); static void cleanup();
static void build_colors();
static int get_screen_width(); static int get_screen_width();
static int get_screen_height(); static int get_screen_height();
@@ -64,15 +65,15 @@ public:
static void do_update(); static void do_update();
static bool daemon() { return m_isDaemon; } static bool daemon() { return m_daemon; }
static const attributes_map& attr_map() { return m_attr_map; } static const attributes_map& attr_map() { return m_attr_map; }
private: private:
Canvas(const Canvas&); Canvas(const Canvas&);
void operator=(const Canvas&); void operator=(const Canvas&);
static bool m_isInitialized; static bool m_initialized;
static bool m_isDaemon; static bool m_daemon;
// Maps ncurses color IDs to a ncurses attribute int // Maps ncurses color IDs to a ncurses attribute int
static std::unordered_map<int, int> m_attr_map; static std::unordered_map<int, int> m_attr_map;
@@ -80,66 +81,59 @@ private:
WINDOW* m_window; WINDOW* m_window;
}; };
inline Canvas::~Canvas() {
if (!m_isDaemon) {
delwin(m_window);
}
}
inline void inline void
Canvas::refresh() { Canvas::refresh() {
if (!m_isDaemon) { if (!m_daemon) {
wnoutrefresh(m_window); wnoutrefresh(m_window);
} }
} }
inline void inline void
Canvas::refresh_std() { Canvas::refresh_std() {
if (!m_isDaemon) { if (!m_daemon) {
wnoutrefresh(stdscr); wnoutrefresh(stdscr);
} }
} }
inline void inline void
Canvas::redraw() { Canvas::redraw() {
if (!m_isDaemon) { if (!m_daemon) {
redrawwin(m_window); redrawwin(m_window);
} }
} }
inline void inline void
Canvas::redraw_std() { Canvas::redraw_std() {
if (!m_isDaemon) { if (!m_daemon) {
redrawwin(stdscr); redrawwin(stdscr);
} }
} }
inline void inline void
Canvas::resize(int w, int h) { Canvas::resize(int w, int h) {
if (!m_isDaemon) { if (!m_daemon) {
wresize(m_window, h, w); wresize(m_window, h, w);
} }
} }
inline void inline void
Canvas::resize_term(int x, int y) { Canvas::resize_term(int x, int y) {
if (!m_isDaemon) { if (!m_daemon) {
resizeterm(y, x); resizeterm(y, x);
} }
} }
inline void inline void
Canvas::resize_term(std::pair<int, int> dim) { Canvas::resize_term(std::pair<int, int> dim) {
if (!m_isDaemon) { if (!m_daemon) {
resizeterm(dim.second, dim.first); resizeterm(dim.second, dim.first);
} }
} }
inline unsigned int inline unsigned int
Canvas::get_x() { Canvas::get_x() {
int x; [[maybe_unused]] int x, y;
[[maybe_unused]] int y; if (!m_daemon) {
if (!m_isDaemon) {
getyx(m_window, y, x); getyx(m_window, y, x);
} else { } else {
x = 1; x = 1;
@@ -149,8 +143,8 @@ Canvas::get_x() {
inline unsigned int inline unsigned int
Canvas::get_y() { Canvas::get_y() {
int x, y; [[maybe_unused]] int x, y;
if (!m_isDaemon) { if (!m_daemon) {
getyx(m_window, y, x); getyx(m_window, y, x);
} else { } else {
y = 1; y = 1;
@@ -160,9 +154,8 @@ Canvas::get_y() {
inline unsigned int inline unsigned int
Canvas::width() { Canvas::width() {
int x; [[maybe_unused]] int x, y;
[[maybe_unused]] int y; if (!m_daemon) {
if (!m_isDaemon) {
getmaxyx(m_window, y, x); getmaxyx(m_window, y, x);
} else { } else {
x = 80; x = 80;
@@ -172,8 +165,8 @@ Canvas::width() {
inline unsigned int inline unsigned int
Canvas::height() { Canvas::height() {
int x, y; [[maybe_unused]] int x, y;
if (!m_isDaemon) { if (!m_daemon) {
getmaxyx(m_window, y, x); getmaxyx(m_window, y, x);
} else { } else {
y = 24; y = 24;
@@ -183,21 +176,21 @@ Canvas::height() {
inline void inline void
Canvas::move(unsigned int x, unsigned int y) { Canvas::move(unsigned int x, unsigned int y) {
if (!m_isDaemon) { if (!m_daemon) {
wmove(m_window, y, x); wmove(m_window, y, x);
} }
} }
inline void inline void
Canvas::erase() { Canvas::erase() {
if (!m_isDaemon) { if (!m_daemon) {
werase(m_window); werase(m_window);
} }
} }
inline void inline void
Canvas::erase_std() { Canvas::erase_std() {
if (!m_isDaemon) { if (!m_daemon) {
werase(stdscr); werase(stdscr);
} }
} }
@@ -206,7 +199,7 @@ inline void
Canvas::print(const char* str, ...) { Canvas::print(const char* str, ...) {
va_list arglist; va_list arglist;
if (!m_isDaemon) { if (!m_daemon) {
va_start(arglist, str); va_start(arglist, str);
vw_printw(m_window, const_cast<char*>(str), arglist); vw_printw(m_window, const_cast<char*>(str), arglist);
va_end(arglist); va_end(arglist);
@@ -217,7 +210,7 @@ inline void
Canvas::print(unsigned int x, unsigned int y, const char* str, ...) { Canvas::print(unsigned int x, unsigned int y, const char* str, ...) {
va_list arglist; va_list arglist;
if (!m_isDaemon) { if (!m_daemon) {
va_start(arglist, str); va_start(arglist, str);
wmove(m_window, y, x); wmove(m_window, y, x);
vw_printw(m_window, const_cast<char*>(str), arglist); vw_printw(m_window, const_cast<char*>(str), arglist);
@@ -227,44 +220,43 @@ Canvas::print(unsigned int x, unsigned int y, const char* str, ...) {
inline void inline void
Canvas::print_char(const chtype ch) { Canvas::print_char(const chtype ch) {
if (!m_isDaemon) { if (!m_daemon) {
waddch(m_window, ch); waddch(m_window, ch);
} }
} }
inline void inline void
Canvas::print_char(unsigned int x, unsigned int y, const chtype ch) { Canvas::print_char(unsigned int x, unsigned int y, const chtype ch) {
if (!m_isDaemon) { if (!m_daemon) {
mvwaddch(m_window, y, x, ch); mvwaddch(m_window, y, x, ch);
} }
} }
inline void inline void
Canvas::set_attr(unsigned int x, unsigned int y, unsigned int n, int attr, int color) { Canvas::set_attr(unsigned int x, unsigned int y, unsigned int n, int attr, int color) {
if (!m_isDaemon) { if (!m_daemon) {
mvwchgat(m_window, y, x, n, attr, color, NULL); mvwchgat(m_window, y, x, n, attr, color, NULL);
} }
} }
inline void inline void
Canvas::set_attr(unsigned int x, unsigned int y, unsigned int n, ColorKind k) { Canvas::set_attr(unsigned int x, unsigned int y, unsigned int n, ColorKind k) {
if (!m_isDaemon) { if (!m_daemon) {
mvwchgat(m_window, y, x, n, m_attr_map[k], k, NULL); mvwchgat(m_window, y, x, n, m_attr_map[k], k, NULL);
} }
} }
inline void inline void
Canvas::set_default_attributes(int attr) { Canvas::set_default_attributes(int attr) {
if (!m_isDaemon) { if (!m_daemon) {
(void)wattrset(m_window, attr); (void)wattrset(m_window, attr);
} }
} }
inline int inline int
Canvas::get_screen_width() { Canvas::get_screen_width() {
int x; [[maybe_unused]] int x, y;
[[maybe_unused]] int y; if (!m_daemon) {
if (!m_isDaemon) {
getmaxyx(stdscr, y, x); getmaxyx(stdscr, y, x);
} else { } else {
x = 80; x = 80;
@@ -274,8 +266,8 @@ Canvas::get_screen_width() {
inline int inline int
Canvas::get_screen_height() { Canvas::get_screen_height() {
int x, y; [[maybe_unused]] int x, y;
if (!m_isDaemon) { if (!m_daemon) {
getmaxyx(stdscr, y, x); getmaxyx(stdscr, y, x);
} else { } else {
y = 24; y = 24;
@@ -285,7 +277,7 @@ Canvas::get_screen_height() {
inline void inline void
Canvas::do_update() { Canvas::do_update() {
if (!m_isDaemon) { if (!m_daemon) {
doupdate(); doupdate();
} }
} }
+4
View File
@@ -4,7 +4,11 @@
#include <array> #include <array>
#include <map> #include <map>
#if defined(HAVE_NO_NCURSES)
#include "curses_stub.h"
#else
#include <curses.h> #include <curses.h>
#endif
namespace display { namespace display {
+6
View File
@@ -0,0 +1,6 @@
// Dummy ncurses stub: provides the stdscr symbol expected by the codebase.
#include "curses_stub.h"
// Define stdscr as a null pointer; all ncurses calls are no-ops in this stub.
WINDOW* stdscr = nullptr;
+108
View File
@@ -0,0 +1,108 @@
// Dummy ncurses stub header for building rtorrent without ncurses.
// Provides types, macros, and no-op function stubs so the codebase
// compiles without a real curses library. All display functions become
// no-ops; use with system.daemon.set = true.
#ifndef RTORRENT_DISPLAY_CURSES_STUB_H
#define RTORRENT_DISPLAY_CURSES_STUB_H
#include <cstdarg>
#include <cstdio>
#include <cstddef>
// Opaque window type
typedef void* WINDOW;
// Character type used by curses
typedef unsigned int chtype;
typedef unsigned int attr_t;
// The standard screen
extern WINDOW* stdscr;
// Boolean constants and return codes
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef ERR
#define ERR (-1)
#endif
// Attribute constants
#define A_NORMAL 0
#define A_STANDOUT (1U << 8)
#define A_UNDERLINE (1U << 9)
#define A_REVERSE (1U << 10)
#define A_BLINK (1U << 11)
#define A_DIM (1U << 12)
#define A_BOLD (1U << 13)
// Color support
#define COLORS 0
#define COLOR_BLACK 0
#define COLOR_RED 1
#define COLOR_GREEN 2
#define COLOR_YELLOW 3
#define COLOR_BLUE 4
#define COLOR_MAGENTA 5
#define COLOR_CYAN 6
#define COLOR_WHITE 7
#define COLOR_PAIR(n) 0
// Key constants
#define KEY_DOWN 0x102
#define KEY_UP 0x103
#define KEY_LEFT 0x104
#define KEY_RIGHT 0x105
#define KEY_HOME 0x106
#define KEY_BACKSPACE 0x107
#define KEY_DC 0x14a
#define KEY_ENTER 0x157
#define KEY_NPAGE 0x152
#define KEY_PPAGE 0x153
#define KEY_END 0x168
#define KEY_MOUSE 0x199
#define KEY_RESIZE 0x19a
// Stubbed functions
inline WINDOW* initscr() { return nullptr; }
inline int endwin() { return 0; }
inline WINDOW* newwin(int, int, int, int) { return nullptr; }
inline int delwin(WINDOW*) { return 0; }
inline int wresize(WINDOW*, int, int) { return 0; }
inline int mvwin(WINDOW*, int, int) { return 0; }
inline int wnoutrefresh(WINDOW*) { return 0; }
inline int redrawwin(WINDOW*) { return 0; }
inline int werase(WINDOW*) { return 0; }
inline int wmove(WINDOW*, int, int) { return 0; }
inline int vw_printw(WINDOW*, char*, va_list) { return 0; }
inline int waddch(WINDOW*, const chtype) { return 0; }
inline int mvwaddch(WINDOW*, int, int, const chtype) { return 0; }
inline int mvwchgat(WINDOW*, int, int, int, attr_t, short, const void*) { return 0; }
inline int wattrset(WINDOW*, int) { return 0; }
inline int wattr_get(WINDOW*, attr_t*, short*, void*) { return 0; }
inline int wattr_set(WINDOW*, attr_t, short, void*) { return 0; }
inline int doupdate() { return 0; }
inline int start_color() { return 0; }
inline int use_default_colors() { return 0; }
inline int raw() { return 0; }
inline int noraw() { return 0; }
inline int noecho() { return 0; }
inline int nodelay(WINDOW*, bool) { return 0; }
inline int keypad(WINDOW*, bool) { return 0; }
inline int curs_set(int) { return 0; }
inline int init_pair(short, short, short) { return 0; }
inline int pair_content(short, short*, short*) { return 0; }
inline int resizeterm(int, int) { return 0; }
inline int getch() { return ERR; }
inline int set_escdelay(int) { return 0; }
// getyx / getmaxyx are macros in real ncurses; stub them as no-ops
#define getyx(w, y, x) do { (y) = 0; (x) = 0; } while (0)
#define getmaxyx(w, y, x) do { (y) = 24; (x) = 80; } while (0)
#endif // RTORRENT_DISPLAY_CURSES_STUB_H
+5 -40
View File
@@ -1,47 +1,12 @@
// rTorrent - BitTorrent client
// Copyright (C) 2005-2011, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#include "config.h" #include "config.h"
#include "display/frame.h"
#include <algorithm> #include <algorithm>
#include <functional> #include <functional>
#include <rak/algorithm.h>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include "frame.h" #include "display/window.h"
#include "window.h"
namespace display { namespace display {
@@ -396,7 +361,7 @@ Frame::balance_row(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
(*itr)->balance(x, y, m_width, std::min((*itr)->m_height, height)); (*itr)->balance(x, y, m_width, std::min((*itr)->m_height, height));
y += (*itr)->m_height; y += (*itr)->m_height;
height -= (*itr)->m_height; height -= std::min(height, (*itr)->m_height);
} }
} }
@@ -471,7 +436,7 @@ Frame::balance_column(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
(*itr)->balance(x, y, std::min((*itr)->m_width, width), m_height); (*itr)->balance(x, y, std::min((*itr)->m_width, width), m_height);
x += (*itr)->m_width; x += (*itr)->m_width;
width -= (*itr)->m_width; width -= std::min(width, (*itr)->m_width);
} }
} }
+1 -34
View File
@@ -1,41 +1,8 @@
// rTorrent - BitTorrent client
// Copyright (C) 2005-2011, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#ifndef RTORRENT_DISPLAY_FRAME_H #ifndef RTORRENT_DISPLAY_FRAME_H
#define RTORRENT_DISPLAY_FRAME_H #define RTORRENT_DISPLAY_FRAME_H
#include <cinttypes> #include <cinttypes>
#include <utility>
namespace display { namespace display {
+7 -2
View File
@@ -55,6 +55,7 @@ Manager::receive_update() {
m_force_redraw = false; m_force_redraw = false;
display::Canvas::resize_term(display::Canvas::term_size()); display::Canvas::resize_term(display::Canvas::term_size());
keypad(stdscr, TRUE);
Canvas::redraw_std(); Canvas::redraw_std();
adjust_layout(); adjust_layout();
@@ -79,10 +80,14 @@ Manager::schedule_update(std::chrono::microseconds min_interval) {
return; return;
} }
if (m_task_update.is_scheduled() && m_task_update.time() <= m_scheduler.front()->time()) auto cached = torrent::this_thread::cached_time();
m_scheduler.external_set_cached_time(cached);
auto next = cached + m_scheduler.next_timeout(cached);
if (m_task_update.is_scheduled() && m_task_update.time_or_zero() <= next)
return; return;
torrent::this_thread::scheduler()->update_wait_until(&m_task_update, std::max(m_scheduler.front()->time(), m_time_last_update + min_interval)); torrent::this_thread::scheduler()->update_wait_until(&m_task_update, std::max(next, m_time_last_update + min_interval));
} }
} }
+5 -5
View File
@@ -1,9 +1,9 @@
#ifndef RTORRENT_DISPLAY_MANAGER_H #ifndef RTORRENT_DISPLAY_MANAGER_H
#define RTORRENT_DISPLAY_MANAGER_H #define RTORRENT_DISPLAY_MANAGER_H
#include <torrent/utils/scheduler.h> #include <torrent/system/scheduler.h>
#include "frame.h" #include "display/frame.h"
namespace display { namespace display {
@@ -31,9 +31,9 @@ private:
bool m_force_redraw{false}; bool m_force_redraw{false};
Frame m_root_frame; Frame m_root_frame;
std::chrono::microseconds m_time_last_update{}; std::chrono::microseconds m_time_last_update{};
torrent::utils::ExternalScheduler m_scheduler; torrent::system::ExternalScheduler m_scheduler;
torrent::utils::SchedulerEntry m_task_update; torrent::system::SchedulerEntry m_task_update;
}; };
} }
+9 -42
View File
@@ -1,43 +1,10 @@
// rTorrent - BitTorrent client
// Copyright (C) 2005-2011, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#include "config.h" #include "config.h"
#include <rak/string_manip.h> #include "display/text_element_string.h"
#include <torrent/utils/string_manip.h>
#include "rpc/parse_commands.h" #include "rpc/parse_commands.h"
#include "text_element_string.h"
namespace display { namespace display {
@@ -53,13 +20,13 @@ TextElementStringBase::print(char* first, char* last, Canvas::attributes_list* a
auto buffer = std::make_unique<char[]>(last - first); auto buffer = std::make_unique<char[]>(last - first);
char* bufferLast = copy_string(buffer.get(), buffer.get() + (last - first), target); char* bufferLast = copy_string(buffer.get(), buffer.get() + (last - first), target);
first = rak::transform_hex(buffer.get(), bufferLast, first, last); first = torrent::utils::transform_to_hex(buffer.get(), bufferLast, first, last);
} else if (m_flags & flag_escape_html) { } else if (m_flags & flag_escape_html) {
auto buffer = std::make_unique<char[]>(last - first); auto buffer = std::make_unique<char[]>(last - first);
char* bufferLast = copy_string(buffer.get(), buffer.get() + (last - first), target); char* bufferLast = copy_string(buffer.get(), buffer.get() + (last - first), target);
first = rak::copy_escape_html(buffer.get(), bufferLast, first, last); first = torrent::utils::copy_escape_html(buffer.get(), bufferLast, first, last);
} else { } else {
first = copy_string(first, last, target); first = copy_string(first, last, target);
@@ -71,7 +38,7 @@ TextElementStringBase::print(char* first, char* last, Canvas::attributes_list* a
} }
char* char*
TextElementString::copy_string(char* first, char* last, rpc::target_type target) { TextElementString::copy_string(char* first, char* last, [[maybe_unused]] rpc::target_type target) {
extent_type length = std::min<extent_type>(last - first, m_string.size()); extent_type length = std::min<extent_type>(last - first, m_string.size());
std::memcpy(first, m_string.c_str(), length); std::memcpy(first, m_string.c_str(), length);
@@ -80,7 +47,7 @@ TextElementString::copy_string(char* first, char* last, rpc::target_type target)
} }
char* char*
TextElementCString::copy_string(char* first, char* last, rpc::target_type target) { TextElementCString::copy_string(char* first, char* last, [[maybe_unused]] rpc::target_type target) {
extent_type length = std::min<extent_type>(last - first, m_length); extent_type length = std::min<extent_type>(last - first, m_length);
std::memcpy(first, m_string, length); std::memcpy(first, m_string, length);
@@ -104,10 +71,10 @@ TextElementCommand::print(char* first, char* last, Canvas::attributes_list* attr
const std::string& str = result.as_string(); const std::string& str = result.as_string();
if (m_flags & flag_escape_hex) { if (m_flags & flag_escape_hex) {
first = rak::transform_hex(str.c_str(), str.c_str() + str.size(), first, last); first = torrent::utils::transform_to_hex(str.c_str(), str.c_str() + str.size(), first, last);
} else if (m_flags & flag_escape_html) { } else if (m_flags & flag_escape_html) {
first = rak::copy_escape_html(str.c_str(), str.c_str() + str.size(), first, last); first = torrent::utils::copy_escape_html(str.c_str(), str.c_str() + str.size(), first, last);
} else { } else {
size_t length = std::min<size_t>(str.size(), std::distance(first, last)); size_t length = std::min<size_t>(str.size(), std::distance(first, last));
-34
View File
@@ -1,37 +1,3 @@
// rTorrent - BitTorrent client
// Copyright (C) 2005-2011, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#ifndef RTORRENT_DISPLAY_TEXT_ELEMENT_STRING_H #ifndef RTORRENT_DISPLAY_TEXT_ELEMENT_STRING_H
#define RTORRENT_DISPLAY_TEXT_ELEMENT_STRING_H #define RTORRENT_DISPLAY_TEXT_ELEMENT_STRING_H
+16 -17
View File
@@ -7,7 +7,6 @@
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include <torrent/connection_manager.h>
#include <torrent/rate.h> #include <torrent/rate.h>
#include <torrent/throttle.h> #include <torrent/throttle.h>
#include <torrent/torrent.h> #include <torrent/torrent.h>
@@ -16,10 +15,11 @@
#include <torrent/data/file_manager.h> #include <torrent/data/file_manager.h>
#include <torrent/download/resource_manager.h> #include <torrent/download/resource_manager.h>
#include <torrent/net/http_stack.h> #include <torrent/net/http_stack.h>
#include <torrent/net/network_config.h>
#include <torrent/net/network_manager.h>
#include <torrent/net/socket_address.h> #include <torrent/net/socket_address.h>
#include <torrent/peer/client_info.h> #include <torrent/peer/client_info.h>
#include <torrent/runtime/network_config.h>
#include <torrent/runtime/runtime.h>
#include <torrent/runtime/socket_manager.h>
#include "control.h" #include "control.h"
#include "globals.h" #include "globals.h"
@@ -151,11 +151,11 @@ print_download_status(char* first, char* last, core::Download* d) {
if (d->is_hash_checking()) { if (d->is_hash_checking()) {
first = print_buffer(first, last, "Checking hash [%2i%%]", first = print_buffer(first, last, "Checking hash [%2i%%]",
(d->download()->chunks_hashed() * 100) / d->download()->file_list()->size_chunks()); d->download()->file_list()->size_chunks() != 0 ? (d->download()->chunks_hashed() * 100) / d->download()->file_list()->size_chunks() : 0);
} else if (d->tracker_controller().has_active_trackers_not_scrape()) { } else if (d->tracker_controller().has_active_trackers_not_scrape()) {
auto tracker = d->tracker_controller().find_if([](const auto& t) { auto tracker = d->tracker_controller().find_if([](const auto& t) {
return t.is_busy_not_scrape(); return t.is_requesting_not_scrape();
}); });
if (tracker.is_valid()) { if (tracker.is_valid()) {
@@ -209,7 +209,7 @@ print_download_info_compact(char* first, char* last, core::Download* d) {
if (d->is_done()) if (d->is_done())
first = print_buffer(first, last, " 100%% "); first = print_buffer(first, last, " 100%% ");
else if (d->is_open()) else if (d->is_open())
first = print_buffer(first, last, " %2u%% ",(d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks()); first = print_buffer(first, last, " %2u%% ", d->download()->file_list()->size_chunks() != 0 ? (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks() : 0);
else else
first = print_buffer(first, last, " "); first = print_buffer(first, last, " ");
@@ -261,7 +261,7 @@ print_download_percentage_done(char* first, char* last, core::Download* d) {
//return print_buffer(first, last, "[--%%]"); //return print_buffer(first, last, "[--%%]");
return print_buffer(first, last, " "); return print_buffer(first, last, " ");
else else
return print_buffer(first, last, "[%2u%%]", (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks()); return print_buffer(first, last, "[%2u%%]", d->download()->file_list()->size_chunks() != 0 ? (d->download()->file_list()->completed_chunks() * 100) / d->download()->file_list()->size_chunks() : 0);
} }
char* char*
@@ -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", torrent::up_throttle_global()->max_rate() / 1024); first = print_buffer(first, last, "[Throttle %3i", (int)(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", torrent::down_throttle_global()->max_rate() / 1024); first = print_buffer(first, last, " / %3i", (int)(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);
@@ -375,10 +375,9 @@ print_status_info(char* first, char* last) {
first = print_status_throttle_rate(first, last, false, throttle_down_names, global_downrate); first = print_status_throttle_rate(first, last, false, throttle_down_names, global_downrate);
first = print_buffer(first, last, " KB]"); first = print_buffer(first, last, " KB]");
first = print_buffer(first, last, " [Port: %i]", (unsigned int)torrent::runtime::listen_port());
first = print_buffer(first, last, " [Port: %i]", (unsigned int)torrent::runtime::network_manager()->listen_port()); auto local_address = torrent::runtime::network_config()->local_address_best_match();
auto local_address = torrent::config::network_config()->local_address_best_match();
if (!torrent::sa_is_any(local_address.get())) { if (!torrent::sa_is_any(local_address.get())) {
first = print_buffer(first, last, " [Local "); first = print_buffer(first, last, " [Local ");
@@ -389,8 +388,8 @@ print_status_info(char* first, char* last) {
if (first > last) if (first > last)
throw torrent::internal_error("print_status_info(...) wrote past end of the buffer."); throw torrent::internal_error("print_status_info(...) wrote past end of the buffer.");
auto bind_inet_address = torrent::config::network_config()->bind_inet_address(); auto bind_inet_address = torrent::runtime::network_config()->bind_inet_address();
auto bind_inet6_address = torrent::config::network_config()->bind_inet6_address(); auto bind_inet6_address = torrent::runtime::network_config()->bind_inet6_address();
bool show_bind_inet = !torrent::sa_is_any(bind_inet_address.get()); bool show_bind_inet = !torrent::sa_is_any(bind_inet_address.get());
bool show_bind_inet6 = !torrent::sa_is_any(bind_inet6_address.get()); bool show_bind_inet6 = !torrent::sa_is_any(bind_inet6_address.get());
@@ -428,9 +427,9 @@ print_status_extra(char* first, char* last) {
torrent::net_thread::http_stack()->max_total_connections()); torrent::net_thread::http_stack()->max_total_connections());
first = print_buffer(first, last, " [S %i/%i/%i]", first = print_buffer(first, last, " [S %i/%i/%i]",
torrent::total_handshakes(), torrent::runtime::total_handshakes(),
torrent::connection_manager()->size(), torrent::runtime::socket_manager()->size(),
torrent::connection_manager()->max_size()); torrent::runtime::socket_manager()->max_size());
first = print_buffer(first, last, " [F %i/%i]", first = print_buffer(first, last, " [F %i/%i]",
torrent::file_manager()->open_files(), torrent::file_manager()->open_files(),
-2
View File
@@ -31,8 +31,6 @@ Window::Window(Canvas* canvas, int flags, extent_type min_width, extent_type min
Window::~Window() { Window::~Window() {
if (is_active()) if (is_active())
m_slot_unschedule(this); m_slot_unschedule(this);
delete m_canvas;
} }
void void
+61 -4
View File
@@ -2,8 +2,8 @@
#define RTORRENT_WINDOW_BASE_H #define RTORRENT_WINDOW_BASE_H
#include <functional> #include <functional>
#include <torrent/utils/scheduler.h> #include <torrent/system/scheduler.h>
#include <torrent/utils/thread.h> #include <torrent/system/thread.h>
#include "canvas.h" #include "canvas.h"
#include "globals.h" #include "globals.h"
@@ -89,7 +89,7 @@ protected:
static SlotWindow m_slot_unschedule; static SlotWindow m_slot_unschedule;
static Slot m_slot_adjust; static Slot m_slot_adjust;
Canvas* m_canvas; std::unique_ptr<Canvas> m_canvas;
int m_flags; int m_flags;
@@ -99,9 +99,66 @@ protected:
extent_type m_max_width; extent_type m_max_width;
extent_type m_max_height; extent_type m_max_height;
torrent::utils::SchedulerEntry m_task_update; torrent::system::SchedulerEntry m_task_update;
}; };
// Return a range with a distance of no more than __distance and
// between __first and __last, centered on __middle1.
template <typename _InputIter, typename _Distance>
std::pair<_InputIter, _InputIter>
advance_bidirectional(_InputIter __first, _InputIter __middle1, _InputIter __last, _Distance __distance) {
_InputIter __middle2 = __middle1;
do {
if (!__distance)
break;
if (__middle2 != __last) {
++__middle2;
--__distance;
} else if (__middle1 == __first) {
break;
}
if (!__distance)
break;
if (__middle1 != __first) {
--__middle1;
--__distance;
} else if (__middle2 == __last) {
break;
}
} while (true);
return std::make_pair(__middle1, __middle2);
}
template <typename _InputIter, typename _Distance>
_InputIter
advance_forward(_InputIter __first, _InputIter __last, _Distance __distance) {
while (__first != __last && __distance != 0) {
__first++;
__distance--;
}
return __first;
}
template <typename _InputIter, typename _Distance>
_InputIter
advance_backward(_InputIter __first, _InputIter __last, _Distance __distance) {
while (__first != __last && __distance != 0) {
__first--;
__distance--;
}
return __first;
}
} }
#endif #endif
+4 -4
View File
@@ -1,17 +1,17 @@
#include "config.h" #include "config.h"
#include "display/window_download_chunks_seen.h"
#include <cmath> #include <cmath>
#include <stdexcept> #include <stdexcept>
#include <rak/string_manip.h>
#include <torrent/bitfield.h> #include <torrent/bitfield.h>
#include <torrent/data/block.h> #include <torrent/data/block.h>
#include <torrent/data/block_list.h> #include <torrent/data/block_list.h>
#include <torrent/data/transfer_list.h> #include <torrent/data/transfer_list.h>
#include <torrent/utils/string_manip.h>
#include "core/download.h" #include "core/download.h"
#include "window_download_chunks_seen.h"
namespace display { namespace display {
WindowDownloadChunksSeen::WindowDownloadChunksSeen(core::Download* d, unsigned int *focus) : WindowDownloadChunksSeen::WindowDownloadChunksSeen(core::Download* d, unsigned int *focus) :
@@ -82,7 +82,7 @@ WindowDownloadChunksSeen::redraw() {
attr = A_BOLD; attr = A_BOLD;
} }
m_canvas->print_char(attr | rak::value_to_hexchar<0>(std::min<uint8_t>(*chunk, 0xF))); m_canvas->print_char(attr | torrent::utils::value_to_hex0(std::min<uint8_t>(*chunk, 0xF)));
chunk++; chunk++;
if ((chunk - seen) % 10 == 0) { if ((chunk - seen) % 10 == 0) {
+8 -10
View File
@@ -1,17 +1,15 @@
#include "config.h" #include "config.h"
#include "display/color_map.h" #include "display/color_map.h"
#include <rak/algorithm.h>
#include "globals.h"
#include "core/download.h" #include "core/download.h"
#include "core/view.h" #include "core/view.h"
#include "display/canvas.h"
#include "display/utils.h"
#include "display/window_download_list.h"
#include "rpc/parse_commands.h" #include "rpc/parse_commands.h"
#include "canvas.h"
#include "globals.h"
#include "utils.h"
#include "window_download_list.h"
namespace display { namespace display {
WindowDownloadList::WindowDownloadList() : WindowDownloadList::WindowDownloadList() :
@@ -104,10 +102,10 @@ WindowDownloadList::redraw() {
typedef std::pair<core::View::iterator, core::View::iterator> Range; typedef std::pair<core::View::iterator, core::View::iterator> Range;
Range range = rak::advance_bidirectional(m_view->begin_visible(), Range range = advance_bidirectional(m_view->begin_visible(),
m_view->focus() != m_view->end_visible() ? m_view->focus() : m_view->begin_visible(), m_view->focus() != m_view->end_visible() ? m_view->focus() : m_view->begin_visible(),
m_view->end_visible(), m_view->end_visible(),
page_size(layout_name)); page_size(layout_name));
// Make sure we properly fill out the last lines so it looks like // Make sure we properly fill out the last lines so it looks like
// there are more torrents, yet don't hide it if we got the last one // there are more torrents, yet don't hide it if we got the last one
@@ -2,7 +2,6 @@
#include <cmath> #include <cmath>
#include <stdexcept> #include <stdexcept>
#include <rak/string_manip.h>
#include <torrent/data/block_list.h> #include <torrent/data/block_list.h>
#include <torrent/data/transfer_list.h> #include <torrent/data/transfer_list.h>
+11 -4
View File
@@ -135,8 +135,11 @@ WindowFileList::redraw() {
m_canvas->print(0, pos, "%*c%-*s", 16, ' ', filenameWidth, "EMPTY"); m_canvas->print(0, pos, "%*c%-*s", 16, ' ', filenameWidth, "EMPTY");
} else if (itr.is_entering()) { } else if (itr.is_entering()) {
m_canvas->print(0, pos, "%*c %ls", 16 + itr.depth(), '\\', if (itr.depth() < itr.file()->path()->size())
itr.depth() < itr.file()->path()->size() ? wstring_width(itr.file()->path()->at(itr.depth()), filenameWidth - itr.depth() - 1).c_str() : L"UNKNOWN"); m_canvas->print(0, pos, "%*c %ls", 16 + itr.depth(), '\\',
wstring_width(itr.file()->path()->at(itr.depth()).str(), filenameWidth - itr.depth() - 1).c_str());
else
m_canvas->print(0, pos, "%*c %ls", 16 + itr.depth(), '\\', L"UNKNOWN");
} else if (itr.is_leaving()) { } else if (itr.is_leaving()) {
m_canvas->print(0, pos, "%*c %-*s", 16 + (itr.depth() - 1), '/', filenameWidth - (itr.depth() - 1), ""); m_canvas->print(0, pos, "%*c %-*s", 16 + (itr.depth() - 1), '/', filenameWidth - (itr.depth() - 1), "");
@@ -166,8 +169,12 @@ WindowFileList::redraw() {
else else
m_canvas->print(8, pos, "%5.1f T", (double)val / (int64_t(1) << 40)); m_canvas->print(8, pos, "%5.1f T", (double)val / (int64_t(1) << 40));
m_canvas->print(15, pos, "%*c %ls", 1 + itr.depth(), '|', if (itr.depth() < itr.file()->path()->size())
itr.depth() < itr.file()->path()->size() ? wstring_width(itr.file()->path()->at(itr.depth()), filenameWidth - itr.depth() - 1).c_str() : L"UNKNOWN"); m_canvas->print(15, pos, "%*c %ls", 1 + itr.depth(), '|',
wstring_width(itr.file()->path()->at(itr.depth()).str(), filenameWidth - itr.depth() - 1).c_str());
else
m_canvas->print(15, pos, "%*c %ls", 1 + itr.depth(), '|', L"UNKNOWN");
} else { } else {
m_canvas->print(0, pos, "BORK BORK"); m_canvas->print(0, pos, "BORK BORK");
+1 -1
View File
@@ -19,7 +19,7 @@ WindowHttpQueue::WindowHttpQueue(core::HttpQueue* q) :
set_active(false); set_active(false);
m_conn_insert = m_queue->signal_insert().insert(m_queue->signal_insert().end(), [this](auto h) { receive_insert(h); }); m_conn_insert = m_queue->signal_insert().insert(m_queue->signal_insert().end(), [this](auto h) { receive_insert(h); });
m_conn_erase = m_queue->signal_erase().insert(m_queue->signal_insert().end(), [this](auto h) { receive_erase(h); }); m_conn_erase = m_queue->signal_erase().insert(m_queue->signal_erase().end(), [this](auto h) { receive_erase(h); });
m_task_deactivate.slot() = [this] { m_task_deactivate.slot() = [this] {
if (!m_container.empty()) if (!m_container.empty())
+1 -1
View File
@@ -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::utils::SchedulerEntry m_task_deactivate; torrent::system::SchedulerEntry m_task_deactivate;
}; };
} }
+1 -35
View File
@@ -1,37 +1,3 @@
// rTorrent - BitTorrent client
// Copyright (C) 2005-2011, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#include "config.h" #include "config.h"
#include "canvas.h" #include "canvas.h"
@@ -46,7 +12,7 @@ WindowInput::redraw() {
m_canvas->erase(); m_canvas->erase();
m_canvas->print(0, 0, "%s> %s", m_title.c_str(), m_input != NULL ? m_input->c_str() : "<NULL>"); m_canvas->print(0, 0, "%s> %s", m_title.c_str(), m_input != NULL ? m_input->c_str() : "<NULL>");
if (m_focus) if (m_focus && m_input != NULL)
m_canvas->set_attr(m_input->get_pos() + 2 + m_title.size(), 0, 1, A_REVERSE, COLOR_PAIR(0)); m_canvas->set_attr(m_input->get_pos() + 2 + m_title.size(), 0, 1, A_REVERSE, COLOR_PAIR(0));
} }
-34
View File
@@ -1,37 +1,3 @@
// rTorrent - BitTorrent client
// Copyright (C) 2005-2011, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#ifndef RTORRENT_DISPLAY_WINDOW_INPUT_H #ifndef RTORRENT_DISPLAY_WINDOW_INPUT_H
#define RTORRENT_DISPLAY_WINDOW_INPUT_H #define RTORRENT_DISPLAY_WINDOW_INPUT_H
+13 -8
View File
@@ -2,7 +2,8 @@
#include <ctime> #include <ctime>
#include <torrent/torrent.h> #include <torrent/torrent.h>
#include <torrent/utils/thread.h> #include <torrent/system/callbacks.h>
#include <torrent/system/thread.h>
#include "canvas.h" #include "canvas.h"
#include "utils.h" #include "utils.h"
@@ -14,16 +15,18 @@ WindowLog::WindowLog(torrent::log_buffer* l) :
Window(new Canvas, 0, 0, 0, extent_full, extent_static), Window(new Canvas, 0, 0, 0, extent_full, extent_static),
m_log(l) { m_log(l) {
m_task_update.slot() = std::bind(&WindowLog::receive_update, this); m_task_update.slot() = [this]() { receive_update(); };
unsigned int signal_index = torrent::main_thread::thread()->signal_bitfield()->add_signal(std::bind(&WindowLog::receive_update, this)); m_log->lock_and_set_update_slot([this]() {
if (m_log_updating.exchange(true) == true)
return;
m_log->lock_and_set_update_slot([signal_index]() { torrent::main_thread::callback([this]() { receive_update(); });
torrent::main_thread::thread()->send_event_signal(signal_index, false); });
});
} }
WindowLog::~WindowLog() { WindowLog::~WindowLog() {
m_log->lock_and_set_update_slot(nullptr);
torrent::this_thread::scheduler()->erase(&m_task_update); torrent::this_thread::scheduler()->erase(&m_task_update);
} }
@@ -52,11 +55,13 @@ WindowLog::redraw() {
// gets updated. // gets updated.
void void
WindowLog::receive_update() { WindowLog::receive_update() {
m_log_updating = false;
if (!is_active()) if (!is_active())
return; return;
iterator itr = find_older(); auto itr = find_older();
extent_type height = std::min(std::distance(itr, (iterator)m_log->end()), (std::iterator_traits<iterator>::difference_type)10); auto height = std::min(std::distance(itr, (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;
+7 -3
View File
@@ -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"
@@ -22,8 +22,12 @@ public:
private: private:
inline iterator find_older(); inline iterator find_older();
torrent::log_buffer* m_log; torrent::log_buffer* m_log;
torrent::utils::SchedulerEntry m_task_update; torrent::system::SchedulerEntry m_task_update;
align_cacheline
std::atomic<bool> m_log_updating{};
}; };
} }
+8 -9
View File
@@ -1,5 +1,7 @@
#include "config.h" #include "config.h"
#include "display/window_peer_list.h"
#include <stdexcept> #include <stdexcept>
#include <torrent/rate.h> #include <torrent/rate.h>
#include <torrent/data/block_transfer.h> #include <torrent/data/block_transfer.h>
@@ -9,11 +11,8 @@
#include <torrent/peer/peer_info.h> #include <torrent/peer/peer_info.h>
#include "core/download.h" #include "core/download.h"
#include "rak/algorithm.h" #include "display/canvas.h"
#include "display/utils.h"
#include "canvas.h"
#include "utils.h"
#include "window_peer_list.h"
namespace display { namespace display {
@@ -50,10 +49,10 @@ WindowPeerList::redraw() {
typedef std::pair<PList::iterator, PList::iterator> Range; typedef std::pair<PList::iterator, PList::iterator> Range;
Range range = rak::advance_bidirectional(m_list->begin(), Range range = advance_bidirectional(m_list->begin(),
*m_focus != m_list->end() ? *m_focus : m_list->begin(), *m_focus != m_list->end() ? *m_focus : m_list->begin(),
m_list->end(), m_list->end(),
m_canvas->height() - y); m_canvas->height() - y);
if (m_download->download()->file_list()->size_chunks() <= 0) if (m_download->download()->file_list()->size_chunks() <= 0)
throw std::logic_error("WindowPeerList::redraw() m_slotChunksTotal() returned invalid value"); throw std::logic_error("WindowPeerList::redraw() m_slotChunksTotal() returned invalid value");
+6 -7
View File
@@ -2,10 +2,9 @@
#include "window_tracker_list.h" #include "window_tracker_list.h"
#include <rak/algorithm.h>
#include <rak/string_manip.h>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include <torrent/tracker/tracker.h> #include <torrent/tracker/tracker.h>
#include <torrent/utils/string_manip.h>
#include "core/download.h" #include "core/download.h"
@@ -37,7 +36,7 @@ WindowTrackerList::redraw() {
if (tc_size == 0 || *m_focus >= tc_size) if (tc_size == 0 || *m_focus >= tc_size)
return; return;
auto range = rak::advance_bidirectional<unsigned int>(0, *m_focus, tc_size, (m_canvas->height() - 1) / 2); auto range = advance_bidirectional<unsigned int>(0, *m_focus, tc_size, (m_canvas->height() - 1) / 2);
auto group = tc.at(range.first).group(); auto group = tc.at(range.first).group();
while (range.first != range.second) { while (range.first != range.second) {
@@ -52,9 +51,9 @@ WindowTrackerList::redraw() {
if (pos < m_canvas->height()) { if (pos < m_canvas->height()) {
const char* state; const char* state;
if (tracker.is_busy_not_scrape()) if (tracker.is_requesting_not_scrape())
state = "req "; state = "req ";
else if (tracker.is_busy()) else if (tracker.is_requesting())
state = "scr "; state = "scr ";
else else
state = " "; state = " ";
@@ -63,7 +62,7 @@ WindowTrackerList::redraw() {
m_canvas->print(0, pos++, "%s Id: %s Counters: %uf / %us (%u) %s S/L/D: %u/%u/%u (%u/%u)", m_canvas->print(0, pos++, "%s Id: %s Counters: %uf / %us (%u) %s S/L/D: %u/%u/%u (%u/%u)",
state, state,
rak::copy_escape_html(tracker.tracker_id()).c_str(), torrent::utils::copy_escape_html_str(tracker.tracker_id()).c_str(),
tracker_state.failed_counter(), tracker_state.failed_counter(),
tracker_state.success_counter(), tracker_state.success_counter(),
tracker_state.scrape_counter(), tracker_state.scrape_counter(),
@@ -80,7 +79,7 @@ WindowTrackerList::redraw() {
m_canvas->set_attr(4, pos - 1, m_canvas->width(), is_focused() ? A_REVERSE : A_BOLD, COLOR_PAIR(0)); m_canvas->set_attr(4, pos - 1, m_canvas->width(), is_focused() ? A_REVERSE : A_BOLD, COLOR_PAIR(0));
} }
if (tracker.is_busy()) { if (tracker.is_requesting()) {
m_canvas->set_attr(0, pos - 2, 4, A_REVERSE, COLOR_PAIR(0)); m_canvas->set_attr(0, pos - 2, 4, A_REVERSE, COLOR_PAIR(0));
m_canvas->set_attr(0, pos - 1, 4, A_REVERSE, COLOR_PAIR(0)); m_canvas->set_attr(0, pos - 1, 4, A_REVERSE, COLOR_PAIR(0));
} }
+53 -1
View File
@@ -2,7 +2,59 @@
#include "globals.h" #include "globals.h"
#include <cstdlib>
#include <stdlib.h>
#include <torrent/exceptions.h>
rpc::ip_table_list ip_tables; rpc::ip_table_list ip_tables;
Control* control{}; Control* control{};
ThreadWorker* worker_thread{};
std::string
expand_path(const std::string& path) {
if (path.empty())
return std::string();
if (path[0] == '~') {
if (path.size() >= 2 && path[1] != '/')
throw torrent::input_error("Could not expand ~ in path, only '~' or '~/...' is supported.");
const char* home = std::getenv("HOME");
if (home == nullptr || *home == '\0')
throw torrent::input_error("Could not expand ~ in path, HOME environment variable not set.");
return home + path.substr(1);
}
return path;
}
// 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;
}
+43 -4
View File
@@ -1,14 +1,53 @@
#ifndef TORRENT_GLOBALS_H #ifndef TORRENT_GLOBALS_H
#define TORRENT_GLOBALS_H #define TORRENT_GLOBALS_H
#include "thread_worker.h" #include <torrent/common.h>
#include "rpc/ip_table_list.h" #include "rpc/ip_table_list.h"
class Control; class Control;
extern rpc::ip_table_list ip_tables; extern rpc::ip_table_list ip_tables;
extern Control* control;
extern Control* control; std::string expand_path(const std::string& path);
extern ThreadWorker* worker_thread; std::string resolve_path(const std::string& path);
std::string resolve_path_or_throw(const std::string& path);
namespace rpc {
class SCgi;
}
namespace session {
class SessionManager;
}
namespace scgi_thread {
torrent::system::Thread* thread();
std::thread::id thread_id();
void callback_interrupt(torrent::system::callback_id& id, std::function<void ()>&& fn);
rpc::SCgi* scgi();
void set_scgi(rpc::SCgi* scgi);
void set_rpc_log(const std::string& filename);
} // namespace torrent::scgi_thread
namespace session_thread {
torrent::system::Thread* thread();
std::thread::id thread_id();
void callback(std::function<void ()>&& fn);
void callback(torrent::system::callback_id& id, std::function<void ()>&& fn);
void cancel_callback(torrent::system::callback_id& id);
session::SessionManager* manager();
std::string session_path();
} // namespace torrent::session_thread
#endif #endif
+14 -6
View File
@@ -2,20 +2,26 @@
#include "input_event.h" #include "input_event.h"
#include <torrent/common.h>
#include <torrent/system/poll.h>
#include "display/attributes.h" #include "display/attributes.h"
namespace input { namespace input {
void void
InputEvent::insert(torrent::Poll* p) { InputEvent::insert() {
p->open(this); torrent::this_thread::poll()->open(this);
p->insert_read(this); torrent::this_thread::poll()->insert_read(this);
} }
void void
InputEvent::remove(torrent::Poll* p) { InputEvent::remove() {
p->remove_read(this); if (!is_open())
p->close(this); return;
torrent::this_thread::poll()->remove_and_close(this);
set_file_descriptor(-1);
} }
void void
@@ -32,6 +38,8 @@ InputEvent::event_write() {
void void
InputEvent::event_error() { InputEvent::event_error() {
torrent::this_thread::poll()->remove_and_close(this);
set_file_descriptor(-1);
} }
} }
+10 -10
View File
@@ -2,24 +2,24 @@
#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>
#include <torrent/poll.h>
namespace input { namespace input {
class InputEvent : public torrent::Event { class InputEvent : public torrent::system::Event {
public: public:
typedef std::function<void (int)> slot_int; typedef std::function<void (int)> slot_int;
InputEvent(int fd) { m_fileDesc = fd; } InputEvent(int fd) { set_file_descriptor(fd); }
void insert(torrent::Poll* p); const char* type_name() const override { return "input"; }
void remove(torrent::Poll* p);
void event_read(); void insert();
void event_write(); void remove();
void event_error();
void event_read() override;
void event_write() override;
void event_error() override;
void slot_pressed(slot_int s) { m_slotPressed = s; } void slot_pressed(slot_int s) { m_slotPressed = s; }
+48 -37
View File
@@ -1,42 +1,7 @@
// rTorrent - BitTorrent client
// Copyright (C) 2005-2011, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#include "config.h" #include "config.h"
#include <algorithm>
#include <functional> #include <functional>
#include <rak/algorithm.h>
#include <rak/path.h>
#include <dirent.h> #include <dirent.h>
#include <sys/stat.h> #include <sys/stat.h>
@@ -45,6 +10,52 @@
#include "path_input.h" #include "path_input.h"
namespace {
template <typename _Value>
struct compare_base {
bool operator () (const _Value& complete, const _Value& base) const {
return !complete.compare(0, base.size(), base);
}
};
// Count the number of elements from the start of the containers to
// the first inequal element.
template <typename _InputIter1, typename _InputIter2>
typename std::iterator_traits<_InputIter1>::difference_type
count_base(_InputIter1 __first1, _InputIter1 __last1,
_InputIter2 __first2, _InputIter2 __last2) {
typename std::iterator_traits<_InputIter1>::difference_type __n = 0;
for ( ;__first1 != __last1 && __first2 != __last2; ++__first1, ++__first2, ++__n)
if (*__first1 != *__first2)
return __n;
return __n;
}
template <typename _Return, typename _InputIter, typename _Ftor>
_Return
make_base(_InputIter __first, _InputIter __last, _Ftor __ftor) {
if (__first == __last)
return "";
_Return __base = __ftor(*__first++);
for ( ;__first != __last; ++__first) {
auto __pos = count_base(__base.begin(), __base.end(),
__ftor(*__first).begin(), __ftor(*__first).end());
if (__pos < (typename std::iterator_traits<_InputIter>::difference_type)__base.size())
__base.resize(__pos);
}
return __base;
}
}
namespace input { namespace input {
bool bool
@@ -94,7 +105,7 @@ PathInput::receive_do_complete() {
if (r.first == r.second) if (r.first == r.second)
return; // Show some nice colors here. return; // Show some nice colors here.
std::string base = rak::make_base<std::string>(r.first, r.second, std::mem_fn(&utils::directory_entry::s_name)); std::string base = make_base<std::string>(r.first, r.second, [](auto& de) { return de.s_name; });
// Clear the path after the cursor to make this code cleaner. It's // Clear the path after the cursor to make this code cleaner. It's
// not really nessesary to add the complexity just because someone // not really nessesary to add the complexity just because someone
+203 -204
View File
@@ -10,9 +10,12 @@
#include <torrent/torrent.h> #include <torrent/torrent.h>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include <torrent/data/chunk_utils.h> #include <torrent/data/chunk_utils.h>
#include <torrent/net/fd.h>
#include <torrent/net/http_stack.h>
#include <torrent/runtime/memory_manager.h>
#include <torrent/runtime/runtime.h>
#include <torrent/utils/chrono.h> #include <torrent/utils/chrono.h>
#include <torrent/utils/log.h> #include <torrent/utils/log.h>
#include <rak/error_number.h>
#ifdef HAVE_BACKTRACE #ifdef HAVE_BACKTRACE
#include <execinfo.h> #include <execinfo.h>
@@ -20,28 +23,27 @@
#include "core/dht_manager.h" #include "core/dht_manager.h"
#include "core/download.h" #include "core/download.h"
#include "core/download_factory.h"
#include "core/download_store.h"
#include "core/manager.h" #include "core/manager.h"
#include "display/canvas.h" #include "display/canvas.h"
#include "display/window.h" #include "display/window.h"
#include "display/manager.h" #include "display/manager.h"
#include "input/bindings.h" #include "input/bindings.h"
#include "ui/root.h"
#include "rpc/command_scheduler.h" #include "rpc/command_scheduler.h"
#include "rpc/command_scheduler_item.h" #include "rpc/command_scheduler_item.h"
#include "rpc/parse_commands.h" #include "rpc/parse_commands.h"
#include "scgi/thread_scgi.h"
#include "session/session_manager.h"
#include "session/thread_session.h"
#include "ui/root.h"
#include "utils/directory.h" #include "utils/directory.h"
#include "control.h" #include "control.h"
#include "command_helpers.h" #include "command_helpers.h"
#include "globals.h" #include "globals.h"
#include "setup.h"
#include "signal_handler.h" #include "signal_handler.h"
#include "option_parser.h" #include "option_parser.h"
#include "thread_worker.h"
#define LT_LOG(log_fmt, ...) \ #define LT_LOG(log_fmt, ...) \
lt_log_print(torrent::LOG_SYSTEM, "system: " log_fmt, __VA_ARGS__); lt_log_print(torrent::LOG_SYSTEM, "system: " log_fmt, __VA_ARGS__);
@@ -50,37 +52,6 @@ void do_panic(int signum);
void print_help(); void print_help();
void initialize_commands(); void initialize_commands();
void do_nothing() {}
void do_nothing_str(const std::string&) {}
int
parse_options(int argc, char** argv) {
try {
OptionParser optionParser;
// Converted.
optionParser.insert_flag('h', std::bind(&print_help));
optionParser.insert_flag('n', std::bind(&do_nothing_str, std::placeholders::_1));
optionParser.insert_flag('D', std::bind(&do_nothing_str, std::placeholders::_1));
optionParser.insert_flag('I', std::bind(&do_nothing_str, std::placeholders::_1));
optionParser.insert_flag('K', std::bind(&do_nothing_str, std::placeholders::_1));
optionParser.insert_option('b', std::bind(&rpc::call_command_set_string, "network.bind_address.set", std::placeholders::_1));
optionParser.insert_option('d', std::bind(&rpc::call_command_set_string, "directory.default.set", std::placeholders::_1));
optionParser.insert_option('i', std::bind(&rpc::call_command_set_string, "ip", std::placeholders::_1));
optionParser.insert_option('p', std::bind(&rpc::call_command_set_string, "network.port_range.set", std::placeholders::_1));
optionParser.insert_option('s', std::bind(&rpc::call_command_set_string, "session", std::placeholders::_1));
optionParser.insert_option('O', std::bind(&rpc::parse_command_single_std, std::placeholders::_1));
optionParser.insert_option_list('o', std::bind(&rpc::call_command_set_std_string, std::placeholders::_1, std::placeholders::_2));
return optionParser.process(argc, argv);
} catch (torrent::input_error& e) {
throw torrent::input_error("Failed to parse command line option: " + std::string(e.what()));
}
}
void void
initialize_rpc_slots() { initialize_rpc_slots() {
rpc::rpc.slot_find_download() = [](const char* hash) { rpc::rpc.slot_find_download() = [](const char* hash) {
@@ -110,43 +81,6 @@ initialize_rpc_slots() {
}; };
} }
void
load_session_torrents() {
utils::Directory entries = control->core()->download_store()->get_formated_entries();
for (const auto& entry : entries) {
// We don't really support session torrents that are links. These
// would be overwritten anyway on exit, and thus not really be
// useful.
if (!entry.is_file())
continue;
core::DownloadFactory* f = new core::DownloadFactory(control->core());
// Replace with session torrent flag.
f->set_session(true);
f->set_init_load(true);
f->slot_finished([f](){ delete f; });
f->load(entries.path() + entry.s_name);
f->commit();
}
}
void
load_arg_torrents(char** first, char** last) {
//std::for_each(begin, end, std::bind1st(std::mem_fun(&core::Manager::insert), &control->get_core()));
for (; first != last; ++first) {
core::DownloadFactory* f = new core::DownloadFactory(control->core());
// Replace with session torrent flag.
f->set_start(true);
f->set_init_load(true);
f->slot_finished([f](){ delete f; });
f->load(*first);
f->commit();
}
}
static void static void
client_perform() { client_perform() {
// Use throw exclusively. // Use throw exclusively.
@@ -176,34 +110,50 @@ main(int argc, char** argv) {
srandom(random_seed); srandom(random_seed);
srand48(random_seed); srand48(random_seed);
torrent::initialize_main_thread();
torrent::log_initialize(); torrent::log_initialize();
control = new Control; // TODO: Create a fake thread object for initializing other processes and enabling logging.
torrent::initialize_main_thread();
SignalHandler::set_ignore(SIGPIPE); // Block SIGCHLD until all threads are created, then unblock on main-thread, to avoid SIGCHLD
SignalHandler::set_handler(SIGINT, std::bind(&Control::receive_normal_shutdown, control)); // interrupting other threads.
SignalHandler::set_handler(SIGHUP, std::bind(&Control::receive_normal_shutdown, control)); //
SignalHandler::set_handler(SIGTERM, std::bind(&Control::receive_quick_shutdown, control)); // This means only main-thread can fork and wait for child processes.
SignalHandler::set_handler(SIGWINCH, std::bind(&display::Manager::force_redraw, control->display()));
SignalHandler::set_block(SIGALRM);
SignalHandler::set_block(SIGPIPE);
SignalHandler::set_block(SIGCHLD);
// All signal handlers must restore errno if they return.
SignalHandler::set_handler(SIGSEGV, std::bind(&do_panic, SIGSEGV)); SignalHandler::set_handler(SIGSEGV, std::bind(&do_panic, SIGSEGV));
SignalHandler::set_handler(SIGILL, std::bind(&do_panic, SIGILL)); SignalHandler::set_handler(SIGILL, std::bind(&do_panic, SIGILL));
SignalHandler::set_handler(SIGFPE, std::bind(&do_panic, SIGFPE)); SignalHandler::set_handler(SIGFPE, std::bind(&do_panic, SIGFPE));
// Limited list of commands with the following format:
//
// cat ~/.rtorrent.rc:
//
// # do:log.open_file=system,/usr/rakshasa/system.log
// # do:log.add_output=system,system
//
parse_config_file(argc, argv, [](auto& path) {
if (path.empty())
return;
parse_config_file_comments(path);
});
control = new Control;
SignalHandler::set_handler(SIGINT, std::bind(&Control::receive_normal_shutdown, control));
SignalHandler::set_handler(SIGHUP, std::bind(&Control::receive_normal_shutdown, control));
SignalHandler::set_handler(SIGTERM, std::bind(&Control::receive_quick_shutdown, control));
SignalHandler::set_handler(SIGWINCH, std::bind(&display::Manager::force_redraw, control->display()));
SignalHandler::set_sigaction_handler(SIGBUS, &handle_sigbus); SignalHandler::set_sigaction_handler(SIGBUS, &handle_sigbus);
// SIGUSR1 is used for interrupting polling, forcing the target torrent::log_add_group_output(torrent::LOG_NOTICE, "important");
// thread to process new non-socket events. torrent::log_add_group_output(torrent::LOG_DHT_ERROR, "important");
//
// LibTorrent uses sockets for this purpose on Solaris and other
// platforms that do not properly pass signals to the target
// threads. Use '--enable-interrupt-socket' when configuring
// LibTorrent to enable this workaround.
if (torrent::utils::Thread::should_handle_sigusr1())
SignalHandler::set_handler(SIGUSR1, std::bind(&do_nothing));
torrent::log_add_group_output(torrent::LOG_NOTICE, "important");
torrent::log_add_group_output(torrent::LOG_DHT_ERROR, "important");
torrent::log_add_group_output(torrent::LOG_INFO, "complete"); torrent::log_add_group_output(torrent::LOG_INFO, "complete");
torrent::log_add_group_output(torrent::LOG_DHT_ERROR, "complete"); torrent::log_add_group_output(torrent::LOG_DHT_ERROR, "complete");
@@ -212,10 +162,12 @@ main(int argc, char** argv) {
initialize_rpc_slots(); initialize_rpc_slots();
torrent::initialize(); torrent::initialize();
torrent::set_main_thread_slots(std::bind(&client_perform)); torrent::main_thread::set_client_callback(&client_perform);
worker_thread = new ThreadWorker(); scgi::ThreadScgi::create_thread();
worker_thread->init_thread(); session::ThreadSession::create_thread();
SignalHandler::set_unblock(SIGCHLD);
// Initialize option handlers after libtorrent to ensure // Initialize option handlers after libtorrent to ensure
// torrent::ConnectionManager* are valid etc. // torrent::ConnectionManager* are valid etc.
@@ -261,7 +213,14 @@ main(int argc, char** argv) {
"method.insert = event.download.hash_queued,multi|rlookup|static\n" "method.insert = event.download.hash_queued,multi|rlookup|static\n"
"method.set_key = event.download.inserted, 1_send_scrape, ((d.tracker.send_scrape,30))\n" "method.set_key = event.download.inserted, 1_send_scrape, ((d.tracker.send_scrape,30))\n"
"method.set_key = event.download.inserted_new, 1_prepare, {(branch,((d.state)),((view.set_visible,started)),((view.set_visible,stopped)) ),(d.save_full_session)}\n" "method.set_key = event.download.inserted_new, 1_prepare, {(branch,((d.state)),((view.set_visible,started)),((view.set_visible,stopped)) )}\n"
// d.save_full_session runs LAST (~ prefix is ASCII 0x7E, sorts after all alphanumerics,
// matching the existing ~_delete_tied precedent on event.download.erased) so the on-disk
// snapshot includes custom fields written by other inserted_new handlers (addtime,
// seedingtime, etc.). Was previously inside 1_prepare alongside view setup, which
// snapshotted bencode before later handlers ran, so d.set_custom=addtime did not reach
// .rtorrent until the next periodic resume save — a window where rtorrent restart lost them.
"method.set_key = event.download.inserted_new, ~_save_full, ((d.save_full_session))\n"
"method.set_key = event.download.inserted_session, 1_prepare, {(branch,((d.state)),((view.set_visible,started)),((view.set_visible,stopped)) )}\n" "method.set_key = event.download.inserted_session, 1_prepare, {(branch,((d.state)),((view.set_visible,started)),((view.set_visible,stopped)) )}\n"
"method.set_key = event.download.inserted, 1_prioritize_toc, \"branch=file.prioritize_toc=,{\\\"f.multicall=(file.prioritize_toc.first),f.prioritize_first.enable=\\\",\\\"f.multicall=(file.prioritize_toc.last),f.prioritize_last.enable=\\\",d.update_priorities=}\"\n" "method.set_key = event.download.inserted, 1_prioritize_toc, \"branch=file.prioritize_toc=,{\\\"f.multicall=(file.prioritize_toc.first),f.prioritize_first.enable=\\\",\\\"f.multicall=(file.prioritize_toc.last),f.prioritize_last.enable=\\\",d.update_priorities=}\"\n"
@@ -335,14 +294,12 @@ main(int argc, char** argv) {
"view.filter = leeching,((and,((d.state)),((not,((d.complete))))))\n" "view.filter = leeching,((and,((d.state)),((not,((d.complete))))))\n"
"view.filter_on = leeching,event.download.resumed,event.download.paused,event.download.finished\n" "view.filter_on = leeching,event.download.resumed,event.download.paused,event.download.finished\n"
"schedule2 = view.main,10,10,((view.sort,main,20))\n" "schedule = view.main,10,10,((view.sort,main,20))\n"
"schedule2 = view.name,10,10,((view.sort,name,20))\n" "schedule = view.name,10,10,((view.sort,name,20))\n"
"schedule2 = session_save,1200,1200,((session.save))\n" "schedule = session_save,1200,1200,((session.save))\n"
"schedule2 = low_diskspace,5,60,((close_low_diskspace,500M))\n" "schedule = low_diskspace,5,60,((close_low_diskspace,500M))\n"
"schedule2 = 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.set=allow_incoming,prefer_plaintext,enable_retry\n"
"ui.color.focus.set=reverse\n" "ui.color.focus.set=reverse\n"
); );
@@ -350,143 +307,180 @@ main(int argc, char** argv) {
// Functions that might not get depracted as they are nice for // Functions that might not get depracted as they are nice for
// configuration files, and thus might do with just some // configuration files, and thus might do with just some
// cleanup. // cleanup.
CMD2_REDIRECT("upload_rate", "throttle.global_up.max_rate.set_kb"); CMD_REDIRECT("upload_rate", "throttle.global_up.max_rate.set_kb");
CMD2_REDIRECT("download_rate", "throttle.global_down.max_rate.set_kb"); CMD_REDIRECT("download_rate", "throttle.global_down.max_rate.set_kb");
CMD2_REDIRECT("ratio.enable", "group.seeding.ratio.enable"); CMD_REDIRECT("ratio.enable", "group.seeding.ratio.enable");
CMD2_REDIRECT("ratio.disable", "group.seeding.ratio.disable"); CMD_REDIRECT("ratio.disable", "group.seeding.ratio.disable");
CMD2_REDIRECT("ratio.min", "group.seeding.ratio.min"); CMD_REDIRECT("ratio.min", "group.seeding.ratio.min");
CMD2_REDIRECT("ratio.max", "group.seeding.ratio.max"); CMD_REDIRECT("ratio.max", "group.seeding.ratio.max");
CMD2_REDIRECT("ratio.upload", "group.seeding.ratio.upload"); CMD_REDIRECT("ratio.upload", "group.seeding.ratio.upload");
CMD2_REDIRECT("ratio.min.set", "group.seeding.ratio.min.set"); CMD_REDIRECT("ratio.min.set", "group.seeding.ratio.min.set");
CMD2_REDIRECT("ratio.max.set", "group.seeding.ratio.max.set"); CMD_REDIRECT("ratio.max.set", "group.seeding.ratio.max.set");
CMD2_REDIRECT("ratio.upload.set", "group.seeding.ratio.upload.set"); CMD_REDIRECT("ratio.upload.set", "group.seeding.ratio.upload.set");
CMD2_REDIRECT("encryption", "protocol.encryption.set"); CMD_REDIRECT("encryption", "protocol.encryption.set");
CMD2_REDIRECT("encoding_list", "encoding.add");
CMD2_REDIRECT("connection_leech", "protocol.connection.leech.set"); CMD_REDIRECT("check_hash", "pieces.hash.on_completion.set");
CMD2_REDIRECT("connection_seed", "protocol.connection.seed.set");
CMD2_REDIRECT("min_peers", "throttle.min_peers.normal.set"); CMD_REDIRECT("connection_leech", "protocol.connection.leech.set");
CMD2_REDIRECT("max_peers", "throttle.max_peers.normal.set"); CMD_REDIRECT("connection_seed", "protocol.connection.seed.set");
CMD2_REDIRECT("min_peers_seed", "throttle.min_peers.seed.set");
CMD2_REDIRECT("max_peers_seed", "throttle.max_peers.seed.set");
CMD2_REDIRECT("min_uploads", "throttle.min_uploads.set"); CMD_REDIRECT("min_peers", "throttle.min_peers.normal.set");
CMD2_REDIRECT("max_uploads", "throttle.max_uploads.set"); CMD_REDIRECT("max_peers", "throttle.max_peers.normal.set");
CMD2_REDIRECT("min_downloads", "throttle.min_downloads.set"); CMD_REDIRECT("min_peers_seed", "throttle.min_peers.seed.set");
CMD2_REDIRECT("max_downloads", "throttle.max_downloads.set"); CMD_REDIRECT("max_peers_seed", "throttle.max_peers.seed.set");
CMD2_REDIRECT("max_uploads_div", "throttle.max_uploads.div.set"); CMD_REDIRECT("min_uploads", "throttle.min_uploads.set");
CMD2_REDIRECT("max_uploads_global", "throttle.max_uploads.global.set"); CMD_REDIRECT("max_uploads", "throttle.max_uploads.set");
CMD2_REDIRECT("max_downloads_div", "throttle.max_downloads.div.set"); CMD_REDIRECT("min_downloads", "throttle.min_downloads.set");
CMD2_REDIRECT("max_downloads_global", "throttle.max_downloads.global.set"); CMD_REDIRECT("max_downloads", "throttle.max_downloads.set");
CMD2_REDIRECT("max_memory_usage", "pieces.memory.max.set"); CMD_REDIRECT("max_uploads_div", "throttle.max_uploads.div.set");
CMD_REDIRECT("max_uploads_global", "throttle.max_uploads.global.set");
CMD_REDIRECT("max_downloads_div", "throttle.max_downloads.div.set");
CMD_REDIRECT("max_downloads_global", "throttle.max_downloads.global.set");
CMD2_REDIRECT("bind", "network.bind_address.set"); CMD_REDIRECT("directory", "directory.default.set");
CMD2_REDIRECT("ip", "network.local_address.set"); CMD_REDIRECT("session", "session.path.set");
CMD2_REDIRECT("port_range", "network.port_range.set");
CMD2_REDIRECT("dht", "dht.mode.set"); CMD_REDIRECT_NO_EXPORT("port_range", "network.listen.port.range.set");
CMD_REDIRECT_NO_EXPORT("scgi_port", "network.scgi.open_port");
CMD_REDIRECT_NO_EXPORT("scgi_local", "network.scgi.open_local");
CMD2_REDIRECT("port_random", "network.port_random.set"); CMD_REDIRECT("to_gm_time", "convert.gm_time");
CMD2_REDIRECT("proxy_address", "network.proxy_address.set"); CMD_REDIRECT("to_gm_date", "convert.gm_date");
CMD_REDIRECT("to_time", "convert.time");
CMD_REDIRECT("to_date", "convert.date");
CMD_REDIRECT("to_elapsed_time", "convert.elapsed_time");
CMD_REDIRECT("to_kb", "convert.kb");
CMD_REDIRECT("to_mb", "convert.mb");
CMD_REDIRECT("to_xb", "convert.xb");
CMD_REDIRECT("to_throttle", "convert.throttle");
CMD2_REDIRECT("scgi_port", "network.scgi.open_port"); // TODO: Deprecate these at some point a while after 1.1 release.
CMD2_REDIRECT("scgi_local", "network.scgi.open_local");
CMD2_REDIRECT("directory", "directory.default.set"); CMD_REDIRECT("d.multicall2", "d.multicall");
CMD2_REDIRECT("session", "session.path.set"); CMD_REDIRECT("network.open_sockets", "system.sockets.size");
CMD_REDIRECT("network.max_open_sockets", "system.sockets.max_size");
CMD_REDIRECT("network.max_open_sockets.set", "system.sockets.max_size.set");
CMD_REDIRECT("network.http.proxy_address", "network.proxy.http");
CMD_REDIRECT("network.http.proxy_address.set", "network.proxy.http.set");
CMD2_REDIRECT("check_hash", "pieces.hash.on_completion.set"); rpc::rpc.mark_safe("d.multicall2");
rpc::rpc.mark_safe("network.max_open_sockets");
rpc::rpc.mark_safe("network.http.proxy_address");
CMD2_REDIRECT("key_layout", "keys.layout.set"); CMD2_ANY_VALUE_V("network.http.max_total_connections.set", [](auto, auto) {
lt_log_print(torrent::LOG_WARN, "network.http.max_total_connections.set is deprecated, use system.sockets.http.min_alloc.set instead.");
});
CMD2_REDIRECT("to_gm_time", "convert.gm_time"); CMD2_ANY_VALUE_V("network.max_open_files.set", [](auto, auto) {
CMD2_REDIRECT("to_gm_date", "convert.gm_date"); lt_log_print(torrent::LOG_WARN, "network.max_open_files.set is deprecated, use system.sockets.files.min_alloc.set instead.");
CMD2_REDIRECT("to_time", "convert.time"); });
CMD2_REDIRECT("to_date", "convert.date");
CMD2_REDIRECT("to_elapsed_time", "convert.elapsed_time");
CMD2_REDIRECT("to_kb", "convert.kb");
CMD2_REDIRECT("to_mb", "convert.mb");
CMD2_REDIRECT("to_xb", "convert.xb");
CMD2_REDIRECT("to_throttle", "convert.throttle");
CMD2_REDIRECT("torrent_list_layout", "ui.torrent_list.layout.set");
// Deprecate:
CMD2_REDIRECT("network.http.max_open", "network.http.max_total_connections");
CMD2_REDIRECT("network.http.max_open.set", "network.http.max_total_connections.set");
// Deprecated commands. Don't use these anymore.
//
// It has been so long that we now re-create these commands with the new (old by now) command
// call style, where the first argument is the target.
// if (rpc::call_command_value("method.use_intermediate") == 1) { // if (rpc::call_command_value("method.use_intermediate") == 1) {
// CMD2_REDIRECT("execute", "execute2");
// CMD2_REDIRECT("schedule", "schedule2");
// CMD2_REDIRECT("schedule_remove", "schedule_remove2");
// } else if (rpc::call_command_value("method.use_intermediate") == 2) { // } else if (rpc::call_command_value("method.use_intermediate") == 2) {
// Allow for use in config files, etc, just don't export it. // Allow for use in config files, etc, just don't export it.
// CMD2_REDIRECT_NO_EXPORT("execute", "execute2");
// CMD2_REDIRECT_NO_EXPORT("schedule", "schedule2");
// CMD2_REDIRECT_NO_EXPORT("schedule_remove", "schedule_remove2");
// } // }
// if (rpc::call_command_value("method.use_deprecated") == 1) { if (rpc::call_command_value("method.use_deprecated") == 1) {
// } CMD_REDIRECT("execute2", "execute");
CMD_REDIRECT("schedule2", "schedule");
CMD_REDIRECT("schedule_remove2", "schedule.remove");
int firstArg = parse_options(argc, argv); CMD_REDIRECT("bind", "network.bind_address.set");
CMD_REDIRECT("ip", "network.local_address.set");
if (OptionParser::has_flag('n', argc, argv)) { // TODO: Check if dht is on by default.
lt_log_print(torrent::LOG_WARN, "Ignoring rtorrent.rc."); CMD_REDIRECT("dht", "dht.mode.set");
} else {
char* config_dir = std::getenv("XDG_CONFIG_HOME");
char* home_dir = std::getenv("HOME");
if (config_dir != NULL && config_dir[0] == '/' && CMD_REDIRECT("port_random", "network.listen.port.random.set");
access((std::string(config_dir) + "/rtorrent/rtorrent.rc").c_str(), F_OK) != -1) { CMD_REDIRECT("proxy_address", "network.proxy.http.set");
rpc::parse_command_single(rpc::make_target(), "try_import = " + std::string(config_dir) + "/rtorrent/rtorrent.rc");
} else if (home_dir != NULL && access((std::string(home_dir) + "/.config/rtorrent/rtorrent.rc").c_str(), F_OK) != -1) { CMD_REDIRECT("key_layout", "keys.layout.set");
rpc::parse_command_single(rpc::make_target(), "try_import = ~/.config/rtorrent/rtorrent.rc");
CMD_REDIRECT("torrent_list_layout", "ui.torrent_list.layout.set");
CMD2_VAR_STRING("dht.throttle.name", "deprecated");
rpc::rpc.mark_safe("dht.throttle.name");
CMD_REDIRECT("network.http.max_open", "network.http.max_total_connections");
CMD_REDIRECT("network.http.max_open.set", "network.http.max_total_connections.set");
// Users should check their setups to see if they need to modify their use of these options.
CMD_REDIRECT("max_memory_usage", "pieces.memory.max.set");
CMD_ANY_LIST("throttle.ip", []( auto, auto) {
lt_log_print(torrent::LOG_WARN, "The 'throttle.ip' command is deprecated and does nothing.");
return torrent::Object();
});
CMD_ANY("network.port_open", [](auto, auto) {
lt_log_print(torrent::LOG_WARN, "The 'network.port_open' command is deprecated and does nothing.");
return torrent::Object();
});
CMD_ANY("network.port_open.set", [](auto, auto) {
lt_log_print(torrent::LOG_WARN, "The 'network.port_open.set' command is deprecated and does nothing.");
return torrent::Object();
});
CMD_REDIRECT("network.port_random", "network.listen.port.random");
CMD_REDIRECT("network.port_random.set", "network.listen.port.random.set");
CMD_REDIRECT("network.port_range", "network.listen.port.range");
CMD_REDIRECT("network.port_range.set", "network.listen.port.range.set");
}
{
auto fd = torrent::fd_open_family(torrent::fd_flag_stream, AF_INET6);
if (fd == -1) {
if (errno == EAFNOSUPPORT) {
lt_log_print(torrent::LOG_WARN, "disabling ipv6 support, not available on system");
rpc::call_command_set_value("network.block.ipv6.set", true);
}
} else { } else {
rpc::parse_command_single(rpc::make_target(), "try_import = ~/.rtorrent.rc"); torrent::fd_close(fd);
} }
} }
int firstArg = parse_main_options(argc, argv);
parse_config_file(argc, argv, [](auto& path) {
if (path.empty()) {
lt_log_print(torrent::LOG_WARN, "Ignoring rtorrent.rc.");
return;
}
rpc::parse_command_single(rpc::make_target(), "try_import=" + path);
});
LT_LOG("seeded srandom and srand48 (seed:%u)", random_seed); LT_LOG("seeded srandom and srand48 (seed:%u)", random_seed);
LT_LOG("max memory usage: %" PRIu64, torrent::runtime::memory_manager()->max_memory_usage());
control->initialize(); control->initialize();
control->ui()->load_input_history(); control->ui()->load_input_history();
// Load session torrents and perform scheduled tasks to ensure torrent::net_thread::http_stack()->set_user_agent(USER_AGENT);
// session torrents are loaded before arg torrents. torrent::runtime::initialize_network();
// Load session torrents and perform scheduled tasks to ensure session torrents are loaded
// before arg torrents.
control->dht_manager()->set_auto_if_untouched_and_has_session();
control->dht_manager()->load_dht_cache(); control->dht_manager()->load_dht_cache();
load_session_torrents();
// TODO: Check if this is required.
// rak::priority_queue_perform(&taskScheduler, cachedTime);
load_session_torrents(session_thread::manager()->path());
load_arg_torrents(argv + firstArg, argv + argc); load_arg_torrents(argv + firstArg, argv + argc);
// Make sure we update the display before any scheduled tasks can // Make sure we update the display before any scheduled tasks can run, so that loading of
// run, so that loading of torrents doesn't look like it hangs on // torrents doesn't look like it hangs on startup.
// startup.
control->display()->adjust_layout(); control->display()->adjust_layout();
control->display()->receive_update(); control->display()->receive_update();
worker_thread->start_thread();
rpc::commands.call_catch("event.system.startup_done", rpc::make_target(), "startup_done", "System startup_done event action failed: "); rpc::commands.call_catch("event.system.startup_done", rpc::make_target(), "startup_done", "System startup_done event action failed: ");
torrent::utils::Thread::self()->event_loop(); torrent::system::Thread::self()->event_loop();
control->core()->download_list()->session_save(); control->core()->download_list()->session_save();
control->cleanup(); control->cleanup();
@@ -515,10 +509,13 @@ main(int argc, char** argv) {
return -1; return -1;
} }
torrent::log_cleanup();
delete control; delete control;
delete worker_thread; control = nullptr;
scgi::ThreadScgi::destroy_thread();
session::ThreadSession::destroy_thread();
torrent::log_cleanup();
return 0; return 0;
} }
@@ -547,7 +544,7 @@ 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: " << rak::error_number(sa->si_errno).c_str() << std::endl; output << std::endl << "Error: " << std::strerror(sa->si_errno) << std::endl;
const char* signal_reason; const char* signal_reason;
@@ -599,7 +596,9 @@ do_panic(int signum) {
// Use the default signal handler in the future to avoid infinit // Use the default signal handler in the future to avoid infinit
// loops. // loops.
SignalHandler::set_default(signum); SignalHandler::set_default(signum);
display::Canvas::cleanup();
if (control != nullptr)
display::Canvas::cleanup();
std::stringstream output; std::stringstream output;
+6 -37
View File
@@ -1,39 +1,7 @@
// rTorrent - BitTorrent client
// Copyright (C) 2005-2011, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#include "config.h" #include "config.h"
#include "option_parser.h"
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
#include <cstdio> #include <cstdio>
@@ -42,8 +10,6 @@
#include <stdexcept> #include <stdexcept>
#include <unistd.h> #include <unistd.h>
#include "option_parser.h"
void void
OptionParser::insert_flag(char c, slot_string s) { OptionParser::insert_flag(char c, slot_string s) {
m_container[c].m_slot = s; m_container[c].m_slot = s;
@@ -137,8 +103,11 @@ void
OptionParser::call_int_pair(slot_int_pair slot, const std::string& arg) { OptionParser::call_int_pair(slot_int_pair slot, const std::string& arg) {
int a, b; int a, b;
if (std::sscanf(arg.c_str(), "%u-%u", &a, &b) != 2) if (std::sscanf(arg.c_str(), "%d-%d", &a, &b) != 2)
throw std::runtime_error("Invalid argument, \"" + arg + "\" should be \"a-b\""); throw std::runtime_error("Invalid argument, \"" + arg + "\" should be \"a-b\"");
if (a < 0 || b < 0)
throw std::runtime_error("Invalid argument, \"" + arg + "\" should be positive numbers");
slot(a, b); slot(a, b);
} }
-34
View File
@@ -1,37 +1,3 @@
// rTorrent - BitTorrent client
// Copyright (C) 2005-2011, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL. If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so. If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact: Jari Sundell <sundell.software@gmail.com>
#ifndef RTORRENT_OPTION_PARSER_H #ifndef RTORRENT_OPTION_PARSER_H
#define RTORRENT_OPTION_PARSER_H #define RTORRENT_OPTION_PARSER_H
+30 -30
View File
@@ -6,31 +6,31 @@
#include "command.h" #include "command.h"
#define COMMAND_BASE_TEMPLATE_DEFINE(func_name) \ #define COMMAND_BASE_TEMPLATE_DEFINE(func_name) \
template const torrent::Object func_name<target_type>(command_base* rawCommand, target_type target, const torrent::Object& args); \ template const torrent::Object func_name<target_type>(command_base* command_raw, target_type target, const torrent::Object& args); \
template const torrent::Object func_name<core::Download*>(command_base* rawCommand, target_type target, const torrent::Object& args); \ template const torrent::Object func_name<core::Download*>(command_base* command_raw, target_type target, const torrent::Object& args); \
template const torrent::Object func_name<torrent::Peer*>(command_base* rawCommand, target_type target, const torrent::Object& args); \ template const torrent::Object func_name<torrent::Peer*>(command_base* command_raw, target_type target, const torrent::Object& args); \
template const torrent::Object func_name<torrent::tracker::Tracker*>(command_base* rawCommand, target_type target, const torrent::Object& args); \ template const torrent::Object func_name<torrent::tracker::Tracker*>(command_base* command_raw, target_type target, const torrent::Object& args); \
template const torrent::Object func_name<torrent::File*>(command_base* rawCommand, target_type target, const torrent::Object& args); \ template const torrent::Object func_name<torrent::File*>(command_base* command_raw, target_type target, const torrent::Object& args); \
template const torrent::Object func_name<torrent::FileListIterator*>(command_base* rawCommand, target_type target, const torrent::Object& args); template const torrent::Object func_name<torrent::FileListIterator*>(command_base* command_raw, target_type target, const torrent::Object& args);
namespace rpc { namespace rpc {
template <typename T> const torrent::Object template <typename T> const torrent::Object
command_base_call(command_base* rawCommand, target_type target, const torrent::Object& args) { command_base_call(command_base* command_raw, target_type target, const torrent::Object& args) {
if (!is_target_compatible<T>(target)) if (!is_target_compatible<T>(target))
throw torrent::input_error("Target of wrong type to command."); throw torrent::input_error("Target of wrong type to generic command.");
return command_base::_call<typename command_function<T>::type, T>(rawCommand, target, args); return command_base::_call<typename command_function<T>::type, T>(command_raw, target, args);
} }
COMMAND_BASE_TEMPLATE_DEFINE(command_base_call); COMMAND_BASE_TEMPLATE_DEFINE(command_base_call);
template <typename T> const torrent::Object template <typename T> const torrent::Object
command_base_call_value_base(command_base* rawCommand, target_type target, const torrent::Object& rawArgs, int base, int unit) { command_base_call_value_base(command_base* command_raw, target_type target, const torrent::Object& args_raw, int base, int unit) {
if (!is_target_compatible<T>(target)) if (!is_target_compatible<T>(target))
throw torrent::input_error("Target of wrong type to command."); throw torrent::input_error("Target of wrong type to value command.");
const torrent::Object& arg = convert_to_single_argument(rawArgs); auto& arg = convert_to_single_argument(args_raw);
if (arg.type() == torrent::Object::TYPE_STRING) { if (arg.type() == torrent::Object::TYPE_STRING) {
torrent::Object::value_type val; torrent::Object::value_type val;
@@ -38,55 +38,55 @@ command_base_call_value_base(command_base* rawCommand, target_type target, const
if (!parse_whole_value_nothrow(arg.as_string().c_str(), &val, base, unit)) if (!parse_whole_value_nothrow(arg.as_string().c_str(), &val, base, unit))
throw torrent::input_error("Not a value."); throw torrent::input_error("Not a value.");
return command_base::_call<typename command_value_function<T>::type, T>(rawCommand, target, val); 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>(rawCommand, target, unit * arg.as_value()); return command_base::_call<typename command_value_function<T>::type, T>(command_raw, target, unit * arg.as_value());
} }
template <typename T> const torrent::Object template <typename T> const torrent::Object
command_base_call_value(command_base* rawCommand, target_type target, const torrent::Object& rawArgs) { command_base_call_value(command_base* command_raw, target_type target, const torrent::Object& args_raw) {
return command_base_call_value_base<T>(rawCommand, target, rawArgs, 0, 1); return command_base_call_value_base<T>(command_raw, target, args_raw, 0, 1);
} }
template <typename T> const torrent::Object template <typename T> const torrent::Object
command_base_call_value_kb(command_base* rawCommand, target_type target, const torrent::Object& rawArgs) { command_base_call_value_kb(command_base* command_raw, target_type target, const torrent::Object& args_raw) {
return command_base_call_value_base<T>(rawCommand, target, rawArgs, 0, 1024); return command_base_call_value_base<T>(command_raw, target, args_raw, 0, 1024);
} }
COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_value); COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_value);
COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_value_kb); COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_value_kb);
template <typename T> const torrent::Object template <typename T> const torrent::Object
command_base_call_string(command_base* rawCommand, target_type target, const torrent::Object& rawArgs) { command_base_call_string(command_base* command_raw, target_type target, const torrent::Object& args_raw) {
if (!is_target_compatible<T>(target)) if (!is_target_compatible<T>(target))
throw torrent::input_error("Target of wrong type to command."); throw torrent::input_error("Target of wrong type to string command.");
const torrent::Object& arg = convert_to_single_argument(rawArgs); auto& arg = convert_to_single_argument(args_raw);
if (arg.type() == torrent::Object::TYPE_RAW_STRING) if (arg.type() == torrent::Object::TYPE_RAW_STRING)
return command_base::_call<typename command_string_function<T>::type, T>(rawCommand, target, arg.as_raw_string().as_string()); return command_base::_call<typename command_string_function<T>::type, T>(command_raw, target, arg.as_raw_string().as_string());
return command_base::_call<typename command_string_function<T>::type, T>(rawCommand, target, arg.as_string()); return command_base::_call<typename command_string_function<T>::type, T>(command_raw, target, arg.as_string());
} }
COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_string); COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_string);
template <typename T> const torrent::Object template <typename T> const torrent::Object
command_base_call_list(command_base* rawCommand, target_type target, const torrent::Object& rawArgs) { command_base_call_list(command_base* command_raw, target_type target, const torrent::Object& args_raw) {
if (!is_target_compatible<T>(target)) if (!is_target_compatible<T>(target))
throw torrent::input_error("Target of wrong type to command."); throw torrent::input_error("Target of wrong type to list command.");
if (rawArgs.type() != torrent::Object::TYPE_LIST) { if (args_raw.type() != torrent::Object::TYPE_LIST) {
torrent::Object::list_type arg; torrent::Object::list_type arg;
if (!rawArgs.is_empty()) if (!args_raw.is_empty())
arg.push_back(rawArgs); arg.push_back(args_raw);
return command_base::_call<typename command_list_function<T>::type, T>(rawCommand, target, arg); return command_base::_call<typename command_list_function<T>::type, T>(command_raw, target, arg);
} }
return command_base::_call<typename command_list_function<T>::type, T>(rawCommand, target, rawArgs.as_list()); return command_base::_call<typename command_list_function<T>::type, T>(command_raw, target, args_raw.as_list());
} }
COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_list); COMMAND_BASE_TEMPLATE_DEFINE(command_base_call_list);
+80 -51
View File
@@ -3,6 +3,7 @@
#include <functional> #include <functional>
#include <limits> #include <limits>
#include <new>
#include <torrent/common.h> #include <torrent/common.h>
#include <torrent/object.h> #include <torrent/object.h>
#include <torrent/data/file_list_iterator.h> #include <torrent/data/file_list_iterator.h>
@@ -28,7 +29,6 @@ struct target_wrapper<void> {
typedef no_type* cleaned_type; typedef no_type* cleaned_type;
}; };
// Since c++0x isn't out yet...
template <typename T1, typename T2, typename T3> template <typename T1, typename T2, typename T3>
struct rt_triple : private std::pair<T1, T2> { struct rt_triple : private std::pair<T1, T2> {
typedef std::pair<T1, T2> base_type; typedef std::pair<T1, T2> base_type;
@@ -55,15 +55,11 @@ struct rt_triple : private std::pair<T1, T2> {
base_type(src.first, src.second), third(src.third) {} base_type(src.first, src.second), third(src.third) {}
}; };
// Since it gets used so many places we might as well put it in the
// rpc namespace.
//typedef std::pair<int, void*> target_type;
typedef rt_triple<int, void*, void*> target_type;
class command_base; class command_base;
typedef const torrent::Object (*command_base_call_type)(command_base*, target_type, const torrent::Object&); using target_type = rt_triple<int, void*, void*>;
typedef std::function<torrent::Object (target_type, const torrent::Object&)> base_function; using base_function = std::function<torrent::Object (target_type, const torrent::Object&)>;
using command_base_call_type = const torrent::Object (command_base*, target_type, const torrent::Object&);
template <typename tmpl> struct command_base_is_valid {}; template <typename tmpl> struct command_base_is_valid {};
template <command_base_call_type tmpl_func> struct command_base_is_type {}; template <command_base_call_type tmpl_func> struct command_base_is_type {};
@@ -87,17 +83,18 @@ public:
typedef const torrent::Object (*download_pair_slot) (command_base*, core::Download*, core::Download*, const torrent::Object&); typedef const torrent::Object (*download_pair_slot) (command_base*, core::Download*, core::Download*, const torrent::Object&);
static const int target_generic = 0; static constexpr int target_generic = 0;
static const int target_any = 1; static constexpr int target_any = 1;
static const int target_download = 2; static constexpr int target_download = 2;
static const int target_peer = 3; static constexpr int target_peer = 3;
static const int target_tracker = 4; static constexpr int target_tracker = 4;
static const int target_file = 5; static constexpr int target_file = 5;
static const int target_file_itr = 6; static constexpr int target_file_itr = 6;
static constexpr int target_download_pair = 7;
static const int target_download_pair = 7; static constexpr unsigned int max_arguments = 10;
static const unsigned int max_arguments = 10; static constexpr std::size_t optimal_alignment = std::max(alignof(std::max_align_t), alignof(base_function));
struct stack_type { struct stack_type {
torrent::Object* begin() { return reinterpret_cast<torrent::Object*>(buffer); } torrent::Object* begin() { return reinterpret_cast<torrent::Object*>(buffer); }
@@ -114,10 +111,48 @@ public:
char buffer[sizeof(torrent::Object) * max_arguments]; char buffer[sizeof(torrent::Object) * max_arguments];
}; };
command_base() { new (&_pod<base_function>()) base_function(); } command_base() : m_copy_helper(nullptr), m_dest_helper(nullptr) {}
command_base(const command_base& src) { new (&_pod<base_function>()) base_function(src._pod<base_function>()); }
~command_base() { _pod<base_function>().~base_function(); } command_base(const command_base& src) {
m_copy_helper = src.m_copy_helper;
m_dest_helper = src.m_dest_helper;
if (src.m_copy_helper)
src.m_copy_helper(t_pod, src.t_pod);
}
command_base& operator=(const command_base& src) {
if (this != &src) {
if (m_dest_helper) m_dest_helper(t_pod);
m_copy_helper = src.m_copy_helper;
m_dest_helper = src.m_dest_helper;
if (src.m_copy_helper)
src.m_copy_helper(t_pod, src.t_pod);
}
return *this;
}
command_base(command_base&& src) noexcept {
m_copy_helper = src.m_copy_helper;
m_dest_helper = src.m_dest_helper;
if (src.m_copy_helper)
src.m_copy_helper(t_pod, src.t_pod);
}
command_base& operator=(command_base&& src) noexcept {
if (this != &src) {
if (m_dest_helper) m_dest_helper(t_pod);
m_copy_helper = src.m_copy_helper;
m_dest_helper = src.m_dest_helper;
if (src.m_copy_helper)
src.m_copy_helper(t_pod, src.t_pod);
}
return *this;
}
~command_base() {
if (m_dest_helper)
m_dest_helper(t_pod);
}
static torrent::Object* argument(unsigned int index) { return current_stack.begin() + index; } static torrent::Object* argument(unsigned int index) { return current_stack.begin() + index; }
static torrent::Object& argument_ref(unsigned int index) { return *(current_stack.begin() + index); } static torrent::Object& argument_ref(unsigned int index) { return *(current_stack.begin() + index); }
@@ -132,55 +167,51 @@ public:
static void pop_stack(stack_type* stack, torrent::Object* last_stack); static void pop_stack(stack_type* stack, torrent::Object* last_stack);
template <typename T> template <typename T>
void set_function(T s, [[maybe_unused]] int value = command_base_is_valid<T>::value) { _pod<T>() = s; } void set_function(T s, [[maybe_unused]] int value = command_base_is_valid<T>::value) {
static_assert(sizeof(T) <= sizeof(t_pod), "t_pod storage overflow");
static_assert(optimal_alignment >= alignof(T), "t_pod alignment insufficient for type");
if (m_dest_helper)
m_dest_helper(t_pod);
::new (t_pod) T(std::move(s));
m_copy_helper = [](void* dest, const void* src) {
::new (dest) T(*static_cast<const T*>(src));
};
m_dest_helper = [](void* ptr) {
static_cast<T*>(ptr)->~T();
};
}
template <command_base_call_type T> template <command_base_call_type T>
void set_function_2(typename command_base_is_type<T>::type s, [[maybe_unused]] int value = command_base_is_valid<typename command_base_is_type<T>::type>::value) { void set_function_2(typename command_base_is_type<T>::type s, [[maybe_unused]] int value = command_base_is_valid<typename command_base_is_type<T>::type>::value) {
_pod<typename command_base_is_type<T>::type>() = s; set_function<typename command_base_is_type<T>::type>(std::move(s));
} }
// The std::function object in GCC is castable between types with a
// pointer to a struct of ctor/dtor/calls for non-POD slots. As such
// it should be safe to cast between different std::function
// template types, yet what the C++0x standard will say about this I
// have no idea atm.
template <typename tmpl> tmpl& _pod() { return reinterpret_cast<tmpl&>(t_pod); } template <typename tmpl> tmpl& _pod() { return reinterpret_cast<tmpl&>(t_pod); }
template <typename tmpl> const tmpl& _pod() const { return reinterpret_cast<const tmpl&>(t_pod); } template <typename tmpl> const tmpl& _pod() const { return reinterpret_cast<const tmpl&>(t_pod); }
template <typename Func, typename T, typename Args> template <typename Func, typename T, typename Args>
static const torrent::Object _call(command_base* cmd, target_type target, Args args); static const torrent::Object _call(command_base* cmd, target_type target, Args args);
command_base& operator = (const command_base& src) {
_pod<base_function>() = src._pod<base_function>();
return *this;
}
protected: protected:
// For use by functions that need to use placeholders to arguments using copy_fn_t = void (*)(void* dest, const void* src);
// within commands. E.d. callable command strings where one of the using dest_fn_t = void (*)(void* ptr);
// arguments within the command needs to be supplied by the caller.
#ifdef HAVE_CXX11 alignas(optimal_alignment) char t_pod[sizeof(base_function)];
union {
base_function t_pod; copy_fn_t m_copy_helper;
// char t_pod[sizeof(base_function)]; dest_fn_t m_dest_helper;
};
#else
union {
char t_pod[sizeof(base_function)];
};
#endif
}; };
template <typename T1 = void, typename T2 = void> template <typename T1 = void, typename T2 = void>
struct target_type_id { struct target_type_id {
// Nothing here, so we cause an error.
}; };
template <typename T> inline bool template <typename T> inline bool
is_target_compatible(const target_type& target) { return target.first == target_type_id<T>::value; } is_target_compatible(const target_type& target) { return target.first == target_type_id<T>::value; }
// Splitting pairs into separate targets.
inline bool is_target_pair(const target_type& target) { return target.first >= command_base::target_download_pair; } inline bool is_target_pair(const target_type& target) { return target.first >= command_base::target_download_pair; }
template <typename T> inline T template <typename T> inline T
@@ -203,7 +234,7 @@ command_base::_call(command_base* cmd, target_type target, Args args) {
#define COMMAND_BASE_TEMPLATE_TYPE(func_type, func_parm) \ #define COMMAND_BASE_TEMPLATE_TYPE(func_type, func_parm) \
template <typename T, int proper = target_type_id<T>::proper_type> struct func_type { typedef std::function<func_parm> type; }; \ template <typename T, int proper = target_type_id<T>::proper_type> struct func_type { typedef std::function<func_parm> type; }; \
\ \
template <> struct command_base_is_valid<func_type<target_type>::type> { static const int value = 1; }; \ template <> struct command_base_is_valid<func_type<target_type>::type> { static const int value = 1; }; \
template <> struct command_base_is_valid<func_type<core::Download*>::type> { static const int value = 1; }; \ template <> struct command_base_is_valid<func_type<core::Download*>::type> { static const int value = 1; }; \
template <> struct command_base_is_valid<func_type<torrent::Peer*>::type> { static const int value = 1; }; \ template <> struct command_base_is_valid<func_type<torrent::Peer*>::type> { static const int value = 1; }; \
@@ -211,8 +242,6 @@ command_base::_call(command_base* cmd, target_type target, Args args) {
template <> struct command_base_is_valid<func_type<torrent::File*>::type> { static const int value = 1; }; \ template <> struct command_base_is_valid<func_type<torrent::File*>::type> { static const int value = 1; }; \
template <> struct command_base_is_valid<func_type<torrent::FileListIterator*>::type> { static const int value = 1; }; template <> struct command_base_is_valid<func_type<torrent::FileListIterator*>::type> { static const int value = 1; };
// template <typename Q> struct command_base_is_valid<typename func_type<Q>::type > { static const int value = 1; };
COMMAND_BASE_TEMPLATE_TYPE(command_function, torrent::Object (T, const torrent::Object&)); COMMAND_BASE_TEMPLATE_TYPE(command_function, torrent::Object (T, const torrent::Object&));
COMMAND_BASE_TEMPLATE_TYPE(command_value_function, torrent::Object (T, const torrent::Object::value_type&)); COMMAND_BASE_TEMPLATE_TYPE(command_value_function, torrent::Object (T, const torrent::Object::value_type&));
COMMAND_BASE_TEMPLATE_TYPE(command_string_function, torrent::Object (T, const std::string&)); COMMAND_BASE_TEMPLATE_TYPE(command_string_function, torrent::Object (T, const std::string&));
@@ -220,7 +249,7 @@ COMMAND_BASE_TEMPLATE_TYPE(command_list_function, torrent::Object (T, const to
#define COMMAND_BASE_TEMPLATE_CALL(func_name, func_type) \ #define COMMAND_BASE_TEMPLATE_CALL(func_name, func_type) \
template <typename T> const torrent::Object func_name(command_base* rawCommand, target_type target, const torrent::Object& args); \ template <typename T> const torrent::Object func_name(command_base* rawCommand, target_type target, const torrent::Object& args); \
\ \
template <> struct command_base_is_type<func_name<target_type> > { static const int value = 1; typedef func_type<target_type>::type type; }; \ template <> struct command_base_is_type<func_name<target_type> > { static const int value = 1; typedef func_type<target_type>::type type; }; \
template <> struct command_base_is_type<func_name<core::Download*> > { static const int value = 1; typedef func_type<core::Download*>::type type; }; \ template <> struct command_base_is_type<func_name<core::Download*> > { static const int value = 1; typedef func_type<core::Download*>::type type; }; \
template <> struct command_base_is_type<func_name<torrent::Peer*> > { static const int value = 1; typedef func_type<torrent::Peer*>::type type; }; \ template <> struct command_base_is_type<func_name<torrent::Peer*> > { static const int value = 1; typedef func_type<torrent::Peer*>::type type; }; \
+6
View File
@@ -98,11 +98,17 @@ CommandMap::call_command(const key_type& key, const mapped_type& arg, const targ
if (itr == base_type::end()) if (itr == base_type::end())
throw torrent::input_error("Command \"" + std::string(key) + "\" does not exist."); throw torrent::input_error("Command \"" + std::string(key) + "\" does not exist.");
if (!rpc.is_trusted() && !(itr->second.m_flags & flag_untrusted_safe))
throw untrusted_error("Command \"" + std::string(key) + "\" is not allowed for untrusted connections.");
return itr->second.m_anySlot(&itr->second.m_variable, target, arg); return itr->second.m_anySlot(&itr->second.m_variable, target, arg);
} }
const CommandMap::mapped_type const CommandMap::mapped_type
CommandMap::call_command(iterator itr, const mapped_type& arg, const target_type& target) { CommandMap::call_command(iterator itr, const mapped_type& arg, const target_type& target) {
if (!rpc.is_trusted() && !(itr->second.m_flags & flag_untrusted_safe))
throw untrusted_error("Command \"" + itr->first + "\" is not allowed for untrusted connections.");
return itr->second.m_anySlot(&itr->second.m_variable, target, arg); return itr->second.m_anySlot(&itr->second.m_variable, target, arg);
} }
+2
View File
@@ -56,6 +56,8 @@ public:
static const int flag_file_target = 0x100; static const int flag_file_target = 0x100;
static const int flag_tracker_target = 0x200; static const int flag_tracker_target = 0x200;
static const int flag_untrusted_safe = 0x400;
CommandMap() = default; CommandMap() = default;
bool has(const std::string& key) const { return base_type::find(key) != base_type::end(); } bool has(const std::string& key) const { return base_type::find(key) != base_type::end(); }

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